2012-08-14 23:40:26 +04:00
|
|
|
/******************************************* Actions (EXPERIMENTAL) **/
|
|
|
|
|
// XXX this set of features is experimental...
|
|
|
|
|
//
|
2012-08-15 02:15:37 +04:00
|
|
|
// this gives us:
|
|
|
|
|
// - namespace cleanup
|
|
|
|
|
// - auto-generated help
|
|
|
|
|
//
|
2012-08-14 23:40:26 +04:00
|
|
|
// the main questions are:
|
|
|
|
|
// - is this overcomplicating things?
|
2012-08-15 02:15:37 +04:00
|
|
|
// - are the benefits worth the code bloat?
|
|
|
|
|
//
|
2012-08-14 23:40:26 +04:00
|
|
|
|
2012-09-10 03:02:34 +04:00
|
|
|
|
2012-08-14 23:40:26 +04:00
|
|
|
var ImageGrid = {
|
2012-08-15 02:15:37 +04:00
|
|
|
// this can be serialized...
|
|
|
|
|
// NOTE: to load a serialized set of options use ImageGrid.set(options)...
|
2012-08-19 19:30:59 +04:00
|
|
|
actions: {},
|
2012-08-15 02:15:37 +04:00
|
|
|
option: {},
|
|
|
|
|
option_props: {},
|
2012-08-19 19:30:59 +04:00
|
|
|
option_groups: [],
|
2012-08-15 02:15:37 +04:00
|
|
|
|
2012-09-03 22:03:43 +04:00
|
|
|
image_data: null,
|
|
|
|
|
|
2012-08-15 02:15:37 +04:00
|
|
|
// define an action...
|
|
|
|
|
// the two values that are obligatory are:
|
2012-08-15 03:03:00 +04:00
|
|
|
// title - name of the action
|
2012-08-15 02:15:37 +04:00
|
|
|
// call - callable
|
|
|
|
|
// XXX revise...
|
2012-08-19 19:30:59 +04:00
|
|
|
ACTION: function(obj, func){
|
2012-08-30 03:43:24 +04:00
|
|
|
var base = this
|
|
|
|
|
var id = func.name != '' ? func.name : obj.id
|
|
|
|
|
|
2012-08-19 19:30:59 +04:00
|
|
|
if(func != null){
|
|
|
|
|
obj = $.extend(obj, {
|
2012-08-30 03:43:24 +04:00
|
|
|
id: id,
|
|
|
|
|
//call: func
|
|
|
|
|
call: function(){
|
|
|
|
|
res = func.apply(base, arguments)
|
|
|
|
|
$(document).trigger(id)
|
|
|
|
|
return res
|
|
|
|
|
}
|
2012-08-19 19:30:59 +04:00
|
|
|
})
|
|
|
|
|
}
|
2012-08-15 02:15:37 +04:00
|
|
|
// add all the attrs to the function...
|
2012-08-15 03:30:35 +04:00
|
|
|
if(this._type_handler[obj.type] != null){
|
|
|
|
|
this._type_handler[obj.type](obj)
|
|
|
|
|
}
|
|
|
|
|
var call = obj.call
|
2012-08-15 02:15:37 +04:00
|
|
|
for(i in obj){
|
|
|
|
|
if(i == 'doc' && call.doc != null){
|
|
|
|
|
call.func_doc = call.doc
|
|
|
|
|
}
|
|
|
|
|
call[i] = obj[i]
|
|
|
|
|
}
|
2012-08-17 06:33:01 +04:00
|
|
|
this[obj.id] = call
|
2012-08-19 19:30:59 +04:00
|
|
|
this.actions[obj.id] = call
|
2012-08-15 02:15:37 +04:00
|
|
|
return call
|
|
|
|
|
},
|
2012-08-19 19:30:59 +04:00
|
|
|
// shorthand: each argument is an action, the group of each will be set the same...
|
|
|
|
|
GROUP: function(group){
|
|
|
|
|
for(var i=1; i<arguments.length; i++){
|
|
|
|
|
var obj = arguments[i]
|
|
|
|
|
obj.group = group
|
|
|
|
|
// if we have an option for this prop then fix it's group too...
|
|
|
|
|
if(this.option_props[obj.id] != null){
|
|
|
|
|
this.option_props[obj.id].group = group
|
|
|
|
|
}
|
|
|
|
|
if(this.option_groups.indexOf(obj.group) < 0 && obj.group != null){
|
|
|
|
|
this.option_groups.push(obj.group)
|
|
|
|
|
this.option_groups.sort()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
2012-08-15 02:15:37 +04:00
|
|
|
// define an option...
|
|
|
|
|
OPTION: function(obj){
|
|
|
|
|
this.option[obj.name] = obj.value
|
|
|
|
|
this.option_props[obj.name] = obj
|
2012-08-17 06:33:01 +04:00
|
|
|
if(this.option_groups.indexOf(obj.group) < 0 && obj.group != null){
|
|
|
|
|
this.option_groups.push(obj.group)
|
|
|
|
|
this.option_groups.sort()
|
|
|
|
|
}
|
2012-08-19 19:30:59 +04:00
|
|
|
return obj
|
2012-08-15 02:15:37 +04:00
|
|
|
},
|
2012-08-15 03:30:35 +04:00
|
|
|
TYPE: function(name, handler){
|
|
|
|
|
this._type_handler[name] = handler
|
|
|
|
|
},
|
2012-08-15 03:02:27 +04:00
|
|
|
_type_handler: {
|
|
|
|
|
},
|
2012-08-15 02:15:37 +04:00
|
|
|
}
|
|
|
|
|
|
2012-08-19 19:30:59 +04:00
|
|
|
// system actions and handlers...
|
|
|
|
|
ImageGrid.GROUP('API',
|
|
|
|
|
ImageGrid.ACTION({
|
|
|
|
|
doc: 'Set option(s) value(s), calling apropriate callbacks.',
|
2012-08-19 23:18:11 +04:00
|
|
|
group: 'API',
|
|
|
|
|
display: false,
|
2012-08-19 19:30:59 +04:00
|
|
|
},
|
|
|
|
|
function set(obj){
|
|
|
|
|
for(var n in obj){
|
|
|
|
|
this.option[n] = obj[n]
|
2012-08-15 02:15:37 +04:00
|
|
|
}
|
2012-08-19 19:30:59 +04:00
|
|
|
// NOTE: this is separate so as to exclude the posibility of race
|
|
|
|
|
// conditions...
|
|
|
|
|
// ...thogh there is still a posibility of conflicting
|
|
|
|
|
// modes, especially if one mode sets more modes...
|
|
|
|
|
for(var n in obj){
|
|
|
|
|
// call the callback if it exists...
|
2012-08-22 23:37:09 +04:00
|
|
|
if(this.option_props[n].set != null){
|
|
|
|
|
this.option_props[n].set()
|
2012-08-19 19:30:59 +04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}),
|
2012-08-30 03:43:24 +04:00
|
|
|
ImageGrid.ACTION({
|
|
|
|
|
doc: 'Get documentation for name.',
|
|
|
|
|
group: 'API',
|
|
|
|
|
display: false,
|
|
|
|
|
},
|
|
|
|
|
function doc(name){
|
|
|
|
|
return {
|
|
|
|
|
action: this[name] != null ? this[name].doc : null,
|
|
|
|
|
action_func: this[name] != null ? this[name].func_doc : null,
|
|
|
|
|
option: this.option_props[name] != null ? this.option_props[name].doc : null,
|
|
|
|
|
}
|
|
|
|
|
}))
|
2012-08-29 18:28:01 +04:00
|
|
|
|
2012-08-30 03:43:24 +04:00
|
|
|
ImageGrid.GROUP('State',
|
2012-08-29 18:39:13 +04:00
|
|
|
ImageGrid.ACTION({
|
|
|
|
|
doc: 'Save state to local storage',
|
|
|
|
|
display: false,
|
|
|
|
|
},
|
|
|
|
|
function save_config(name){
|
|
|
|
|
if(name == null){
|
|
|
|
|
name = ''
|
|
|
|
|
}
|
|
|
|
|
$.jStorage.set(this.option.KEY_NAME_CONFIG+name, this.sync())
|
|
|
|
|
}),
|
2012-10-04 16:05:59 +04:00
|
|
|
// XXX merge this with save_localstorage...
|
|
|
|
|
ImageGrid.ACTION({
|
|
|
|
|
doc: 'Save state to file',
|
|
|
|
|
display: false,
|
|
|
|
|
},
|
|
|
|
|
function save_file(name){
|
|
|
|
|
if(dumpJSONfile == null){
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
if(name == null){
|
|
|
|
|
name = ''
|
|
|
|
|
// push the last config to a new version...
|
|
|
|
|
var history = loadJSONfile(this.option.FILE_NAME_HISTORY, [])
|
|
|
|
|
// XXX should we add a date?
|
|
|
|
|
history.push(loadJSONfile(this.option.FILE_NAME_STATE))
|
|
|
|
|
// remove versions beyond VERSIONS_TO_KEEP...
|
|
|
|
|
var c = history.length - this.option.VERSIONS_TO_KEEP
|
|
|
|
|
if(c > 0){
|
|
|
|
|
history.splice(0, c)
|
|
|
|
|
}
|
|
|
|
|
dumpJSONfile(this.option.FILE_NAME_HISTORY, history)
|
|
|
|
|
} else {
|
|
|
|
|
name = '-' + name
|
|
|
|
|
}
|
|
|
|
|
this.save_config()
|
|
|
|
|
dumpJSONfile(this.option.FILE_NAME_STATE+name, buildJSON())
|
|
|
|
|
}),
|
2012-08-29 17:02:40 +04:00
|
|
|
ImageGrid.ACTION({
|
2012-08-29 18:28:01 +04:00
|
|
|
doc: 'Save state to local storage',
|
2012-08-29 17:02:40 +04:00
|
|
|
display: false,
|
|
|
|
|
},
|
2012-10-04 16:05:59 +04:00
|
|
|
function save_localstorage(name){
|
2012-08-29 18:28:01 +04:00
|
|
|
if(name == null){
|
|
|
|
|
name = ''
|
|
|
|
|
// push the last config to a new version...
|
2012-08-29 18:39:13 +04:00
|
|
|
var history = $.jStorage.get(this.option.KEY_NAME_HISTORY, [])
|
2012-08-29 18:28:01 +04:00
|
|
|
// XXX should we add a date?
|
2012-08-29 18:39:13 +04:00
|
|
|
history.push($.jStorage.get(this.option.KEY_NAME_STATE))
|
|
|
|
|
// remove versions beyond VERSIONS_TO_KEEP...
|
|
|
|
|
var c = history.length - this.option.VERSIONS_TO_KEEP
|
2012-08-29 18:28:01 +04:00
|
|
|
if(c > 0){
|
|
|
|
|
history.splice(0, c)
|
|
|
|
|
}
|
2012-08-29 18:39:13 +04:00
|
|
|
$.jStorage.set(this.option.KEY_NAME_HISTORY, history)
|
2012-08-29 18:28:01 +04:00
|
|
|
} else {
|
|
|
|
|
name = '-' + name
|
|
|
|
|
}
|
2012-08-29 18:39:13 +04:00
|
|
|
this.save_config()
|
|
|
|
|
$.jStorage.set(this.option.KEY_NAME_STATE+name, buildJSON())
|
2012-08-29 17:02:40 +04:00
|
|
|
}),
|
|
|
|
|
ImageGrid.ACTION({
|
2012-08-29 18:28:01 +04:00
|
|
|
doc: 'Load state from local storage',
|
2012-08-29 17:02:40 +04:00
|
|
|
display: false,
|
|
|
|
|
},
|
2012-08-30 04:26:59 +04:00
|
|
|
function load(name, dfl_state, dfl_config){
|
2012-08-29 18:28:01 +04:00
|
|
|
if(name == null){
|
|
|
|
|
name = ''
|
|
|
|
|
} else {
|
|
|
|
|
name = '-' + name
|
|
|
|
|
}
|
2012-08-30 04:26:59 +04:00
|
|
|
if(dfl_state == null){
|
|
|
|
|
dfl_state = {}
|
|
|
|
|
}
|
|
|
|
|
if(dfl_config == null){
|
|
|
|
|
dfl_config = {}
|
|
|
|
|
}
|
|
|
|
|
loadJSON($.jStorage.get(this.option.KEY_NAME_STATE+name, dfl_state))
|
2012-08-29 17:07:26 +04:00
|
|
|
// NOTE: we need to load the config ACTER the state as to be
|
|
|
|
|
// able to set correct state-related data like current
|
|
|
|
|
// image ID...
|
2012-08-30 04:26:59 +04:00
|
|
|
this.set($.jStorage.get(this.option.KEY_NAME_CONFIG+name, dfl_config))
|
2012-08-29 18:28:01 +04:00
|
|
|
}),
|
|
|
|
|
ImageGrid.ACTION({
|
|
|
|
|
doc: 'Revert to last verison. if n is given then revert n versions back.\n\n'+
|
|
|
|
|
'NOTE: this will push the current state to history, thus '+
|
|
|
|
|
'enabling trivial redo.\n'+
|
|
|
|
|
'NOTE: if n is greater than 1 then all the skipped steps will '+
|
|
|
|
|
'get dropped.',
|
|
|
|
|
display: false,
|
|
|
|
|
},
|
|
|
|
|
function undo(n){
|
|
|
|
|
if(n < 1){
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if(n == null){
|
|
|
|
|
n = 1
|
|
|
|
|
}
|
|
|
|
|
var cur = buildJSON()
|
2012-08-29 18:39:13 +04:00
|
|
|
var history = $.jStorage.get(this.option.KEY_NAME_HISTORY, [])
|
2012-08-29 18:28:01 +04:00
|
|
|
if(history.length <= n){
|
|
|
|
|
n = history.length-1
|
|
|
|
|
}
|
|
|
|
|
// do the loading...
|
|
|
|
|
var i = history.length - n
|
|
|
|
|
loadJSON(history[i])
|
|
|
|
|
// remove the history top...
|
|
|
|
|
history.splice(i, history.length)
|
|
|
|
|
// push the prev state to enable redo...
|
|
|
|
|
history.push(cur)
|
2012-08-29 18:39:13 +04:00
|
|
|
$.jStorage.set(this.option.KEY_NAME_HISTORY, history)
|
2012-08-29 17:02:40 +04:00
|
|
|
}),
|
2012-08-22 23:37:09 +04:00
|
|
|
ImageGrid.ACTION({
|
|
|
|
|
doc: 'Sync and update option values.\n\n'+
|
|
|
|
|
'NOTE: this is here because JS has no direct way to '+
|
|
|
|
|
'on-demand, transparently update the value of an attr. '+
|
|
|
|
|
'.valueOf() is not transparent enough.',
|
|
|
|
|
display: false,
|
|
|
|
|
},
|
|
|
|
|
function sync(){
|
2012-08-29 18:39:13 +04:00
|
|
|
for(var n in this.option_props){
|
2012-08-22 23:37:09 +04:00
|
|
|
if(this.option_props[n].get != null){
|
|
|
|
|
var value = this.option_props[n].get()
|
|
|
|
|
this.option_props[n].value = value
|
|
|
|
|
this.option[n] = value
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-08-29 18:39:13 +04:00
|
|
|
return this.option
|
2012-08-19 19:30:59 +04:00
|
|
|
}))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-08-15 03:30:35 +04:00
|
|
|
ImageGrid.TYPE('toggle', function(obj){
|
|
|
|
|
var call = obj.call
|
|
|
|
|
// wrap the call to set the option...
|
2012-08-17 06:33:01 +04:00
|
|
|
// XXX this is context mirroring...
|
2012-08-15 03:30:35 +04:00
|
|
|
obj.call = function(action){
|
2012-08-30 03:43:24 +04:00
|
|
|
//var res = call(action)
|
|
|
|
|
var res = call.apply(ImageGrid, [action])
|
|
|
|
|
//ImageGrid.option[obj.id] = call('?')
|
|
|
|
|
ImageGrid.option[obj.id] = call.apply(ImageGrid, ['?'])
|
2012-08-15 03:30:35 +04:00
|
|
|
return res
|
|
|
|
|
}
|
|
|
|
|
// add an option to store the state...
|
|
|
|
|
ImageGrid.OPTION({
|
2012-08-17 06:33:01 +04:00
|
|
|
name: obj.id,
|
|
|
|
|
title: obj.title,
|
|
|
|
|
group: obj.group,
|
|
|
|
|
display: obj.display,
|
|
|
|
|
doc: obj.doc == null ? 'Stores the state of '+obj.id+' action.' : obj.doc,
|
2012-08-15 03:30:35 +04:00
|
|
|
value: obj.call('?'),
|
2012-08-22 23:37:09 +04:00
|
|
|
set: function(){
|
|
|
|
|
obj.call(ImageGrid.option[obj.id])
|
|
|
|
|
},
|
|
|
|
|
get: function(){
|
|
|
|
|
return obj.call('?')
|
2012-08-17 06:33:01 +04:00
|
|
|
},
|
|
|
|
|
click_handler: function(){
|
|
|
|
|
obj.call()
|
2012-08-15 03:30:35 +04:00
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
})
|
2012-08-14 23:40:26 +04:00
|
|
|
|
|
|
|
|
|
2012-08-17 22:23:48 +04:00
|
|
|
|
2012-08-15 14:26:23 +04:00
|
|
|
|
2012-08-04 20:26:38 +04:00
|
|
|
/******************************************* Setup Data and Globals **/
|
2012-06-17 16:46:46 +04:00
|
|
|
|
2012-08-10 19:40:26 +04:00
|
|
|
var DEBUG = true
|
2012-08-17 06:33:01 +04:00
|
|
|
//var DEBUG = false
|
2012-08-10 19:40:26 +04:00
|
|
|
|
2012-08-17 07:30:03 +04:00
|
|
|
|
2012-08-19 19:30:59 +04:00
|
|
|
ImageGrid.GROUP('State',
|
2012-08-29 17:02:40 +04:00
|
|
|
ImageGrid.OPTION({
|
|
|
|
|
name: 'KEY_NAME_CONFIG',
|
|
|
|
|
title: 'Name of localStorage key to store config data.',
|
|
|
|
|
value: 'ImageGrid_config',
|
2012-08-29 18:28:01 +04:00
|
|
|
display: false,
|
2012-08-29 17:02:40 +04:00
|
|
|
}),
|
2012-10-04 16:05:59 +04:00
|
|
|
ImageGrid.OPTION({
|
|
|
|
|
name: 'FILE_NAME_STATE',
|
|
|
|
|
title: 'File name to store state.',
|
|
|
|
|
value: '.ImageGrid.state',
|
|
|
|
|
display: false,
|
|
|
|
|
}),
|
|
|
|
|
ImageGrid.OPTION({
|
|
|
|
|
name: 'FILE_NAME_HISTORY',
|
|
|
|
|
title: 'File name to store state history.',
|
|
|
|
|
value: '.ImageGrid.history',
|
|
|
|
|
display: false,
|
|
|
|
|
}),
|
2012-08-29 17:02:40 +04:00
|
|
|
ImageGrid.OPTION({
|
|
|
|
|
name: 'KEY_NAME_STATE',
|
|
|
|
|
title: 'Name of localStorage key to store state.',
|
|
|
|
|
value: 'ImageGrid_state',
|
2012-08-29 18:28:01 +04:00
|
|
|
display: false,
|
|
|
|
|
}),
|
|
|
|
|
ImageGrid.OPTION({
|
|
|
|
|
name: 'KEY_NAME_HISTORY',
|
|
|
|
|
title: 'Name of localStorage key to store state history.',
|
|
|
|
|
value: 'ImageGrid_history',
|
|
|
|
|
display: false,
|
|
|
|
|
}),
|
|
|
|
|
ImageGrid.OPTION({
|
|
|
|
|
name: 'VERSIONS_TO_KEEP',
|
|
|
|
|
title: 'History depth.',
|
|
|
|
|
value: 10,
|
2012-08-29 17:02:40 +04:00
|
|
|
}),
|
2012-09-07 13:48:47 +04:00
|
|
|
ImageGrid.OPTION({
|
|
|
|
|
name: 'LAST_MOVE_DIRECTION',
|
|
|
|
|
title: 'Direction the last move was made to',
|
|
|
|
|
doc: 'Used to naturally position the current image after '+
|
|
|
|
|
'shift up/down operations.',
|
|
|
|
|
value: 'next',
|
|
|
|
|
display: false,
|
|
|
|
|
}),
|
2012-08-29 18:28:01 +04:00
|
|
|
/*
|
2012-08-30 04:26:59 +04:00
|
|
|
// XXX is this the correct way to go...
|
2012-08-22 23:37:09 +04:00
|
|
|
ImageGrid.OPTION({
|
|
|
|
|
name: 'CURRENT_IMAGE_ID',
|
|
|
|
|
doc: '',
|
|
|
|
|
display: false,
|
|
|
|
|
set: function(){
|
|
|
|
|
$('#' + ImageGrid.option.CURRENT_IMAGE_ID).click()
|
|
|
|
|
},
|
|
|
|
|
get: function(){
|
|
|
|
|
return parseInt($('.current.image').attr('id'))
|
|
|
|
|
}
|
|
|
|
|
}),
|
2012-08-29 18:28:01 +04:00
|
|
|
*/
|
2012-08-19 19:30:59 +04:00
|
|
|
ImageGrid.OPTION({
|
|
|
|
|
name: 'BACKGROUND_MODES',
|
2012-08-19 20:12:40 +04:00
|
|
|
doc: 'list of available background styles.\n\n'+
|
2012-08-19 19:30:59 +04:00
|
|
|
'NOTE: there is also a null mode that is what is set in the '+
|
|
|
|
|
'main CSS.',
|
|
|
|
|
display: false,
|
|
|
|
|
value: [
|
|
|
|
|
'dark',
|
|
|
|
|
'black',
|
|
|
|
|
// this can be removed but when given it must be last.
|
|
|
|
|
null
|
|
|
|
|
]
|
|
|
|
|
}),
|
2012-09-24 19:40:06 +04:00
|
|
|
ImageGrid.OPTION({
|
|
|
|
|
name: 'NORMAL_MODE_INFO',
|
|
|
|
|
display: false,
|
|
|
|
|
value: null,
|
|
|
|
|
doc: 'Info display in normal mode.',
|
|
|
|
|
set: function(){
|
|
|
|
|
if(ImageGrid.toggleSingleImageMode('?') == 'off'){
|
|
|
|
|
ImageGrid.toggleInfo(ImageGrid.option.NORMAL_MODE_INFO)
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
get: function(){
|
|
|
|
|
if(ImageGrid.toggleSingleImageMode('?') == 'on'){
|
|
|
|
|
return ImageGrid.option.NORMAL_MODE_INFO
|
|
|
|
|
}
|
|
|
|
|
return ImageGrid.toggleInfo('?')
|
|
|
|
|
}
|
|
|
|
|
}),
|
2012-08-19 19:30:59 +04:00
|
|
|
ImageGrid.OPTION({
|
|
|
|
|
name: 'NORMAL_MODE_BG',
|
|
|
|
|
display: false,
|
|
|
|
|
value: null,
|
2012-08-19 20:12:40 +04:00
|
|
|
doc: 'Background style in normal (ribbon) mode.\n\n'+
|
2012-08-19 19:30:59 +04:00
|
|
|
'NOTE: This will get updated on background change in tuntime.\n'+
|
|
|
|
|
'NOTE: null represents the default style.',
|
2012-08-22 23:37:09 +04:00
|
|
|
set: function(){
|
2012-08-19 19:30:59 +04:00
|
|
|
if(ImageGrid.toggleSingleImageMode('?') == 'off'){
|
2012-08-19 20:32:37 +04:00
|
|
|
ImageGrid.setBackgroundMode(ImageGrid.option.NORMAL_MODE_BG)
|
2012-08-19 19:30:59 +04:00
|
|
|
}
|
2012-08-22 23:37:09 +04:00
|
|
|
},
|
|
|
|
|
get: function(){
|
|
|
|
|
if(ImageGrid.toggleSingleImageMode('?') == 'on'){
|
|
|
|
|
return ImageGrid.option.NORMAL_MODE_BG
|
|
|
|
|
}
|
|
|
|
|
return ImageGrid.toggleBackgroundModes('?')
|
2012-08-19 19:30:59 +04:00
|
|
|
}
|
|
|
|
|
}),
|
|
|
|
|
ImageGrid.OPTION({
|
|
|
|
|
name: 'SINGLE_IMAGE_MODE_BG',
|
|
|
|
|
display: false,
|
|
|
|
|
value: 'black',
|
2012-08-19 20:12:40 +04:00
|
|
|
doc: 'Background style in single image mode.\n\n'+
|
2012-08-19 19:30:59 +04:00
|
|
|
'NOTE: This will get updated on background change in tuntime.\n'+
|
|
|
|
|
'NOTE: null represents the default style.',
|
2012-08-22 23:37:09 +04:00
|
|
|
set: function(){
|
2012-08-19 19:30:59 +04:00
|
|
|
if(ImageGrid.toggleSingleImageMode('?') == 'on'){
|
2012-08-19 20:32:37 +04:00
|
|
|
ImageGrid.setBackgroundMode(ImageGrid.option.SINGLE_IMAGE_MODE_BG)
|
2012-08-19 19:30:59 +04:00
|
|
|
}
|
2012-08-22 23:37:09 +04:00
|
|
|
},
|
|
|
|
|
get: function(){
|
|
|
|
|
if(ImageGrid.toggleSingleImageMode('?') == 'off'){
|
|
|
|
|
return ImageGrid.option.SINGLE_IMAGE_MODE_BG
|
|
|
|
|
}
|
|
|
|
|
return ImageGrid.toggleBackgroundModes('?')
|
2012-08-19 19:30:59 +04:00
|
|
|
}
|
|
|
|
|
}),
|
|
|
|
|
ImageGrid.OPTION({
|
|
|
|
|
name: 'ORIGINAL_FIELD_SCALE',
|
|
|
|
|
display: false,
|
|
|
|
|
value: 1.0,
|
2012-08-19 20:12:40 +04:00
|
|
|
doc: 'Scale of view in image mode.\n\n'+
|
2012-08-19 19:30:59 +04:00
|
|
|
'NOTE: this will change if changed at runtime.',
|
2012-08-22 23:37:09 +04:00
|
|
|
set: function(){
|
2012-08-19 19:30:59 +04:00
|
|
|
if(ImageGrid.toggleSingleImageMode('?') == 'off'){
|
2012-08-19 20:12:40 +04:00
|
|
|
ImageGrid.setContainerScale(ImageGrid.option.ORIGINAL_FIELD_SCALE)
|
2012-08-19 19:30:59 +04:00
|
|
|
}
|
2012-08-22 23:37:09 +04:00
|
|
|
},
|
|
|
|
|
get: function(){
|
|
|
|
|
if(ImageGrid.toggleSingleImageMode('?') == 'on'){
|
|
|
|
|
return ImageGrid.option.ORIGINAL_FIELD_SCALE
|
|
|
|
|
}
|
|
|
|
|
return getElementScale($('.field'))
|
2012-08-19 19:30:59 +04:00
|
|
|
}
|
|
|
|
|
}))
|
2012-08-05 21:03:37 +04:00
|
|
|
|
2012-08-04 20:26:38 +04:00
|
|
|
|
2012-08-19 19:30:59 +04:00
|
|
|
ImageGrid.GROUP('Mode: All',
|
|
|
|
|
ImageGrid.OPTION({
|
|
|
|
|
name: 'ZOOM_FACTOR',
|
|
|
|
|
title: 'Zooming factor',
|
|
|
|
|
value: 2,
|
|
|
|
|
doc: 'Sets the zoom factor used for a manual zooming step.'
|
|
|
|
|
}),
|
|
|
|
|
ImageGrid.OPTION({
|
|
|
|
|
name: 'MOVE_DELTA',
|
|
|
|
|
title: 'Move step',
|
|
|
|
|
value: 50,
|
|
|
|
|
doc: 'Sets the move delta in pixels for keyboard view moving.'
|
|
|
|
|
}))
|
2012-08-04 20:26:38 +04:00
|
|
|
|
|
|
|
|
|
2012-08-20 01:06:08 +04:00
|
|
|
if(DEBUG){
|
|
|
|
|
ImageGrid.OPTION({
|
|
|
|
|
name: 'TEST',
|
|
|
|
|
title: 'Test the Other group mechanics',
|
2012-08-22 23:37:09 +04:00
|
|
|
doc: 'this will not be created wone the DEBUG flag is false',
|
|
|
|
|
display: false,
|
2012-08-20 01:06:08 +04:00
|
|
|
value: 0,
|
|
|
|
|
})
|
|
|
|
|
}
|
2012-08-04 20:26:38 +04:00
|
|
|
|
|
|
|
|
|
2012-08-08 01:45:22 +04:00
|
|
|
|
2012-08-14 23:40:26 +04:00
|
|
|
/************************************************ jQuery extensions **/
|
2012-08-08 01:45:22 +04:00
|
|
|
|
|
|
|
|
|
2012-08-14 17:26:46 +04:00
|
|
|
jQuery.fn.reverseChildren = function(){
|
|
|
|
|
return $(this).each(function(_, e){
|
|
|
|
|
return $(e).append($(e).children().detach().get().reverse())
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2012-08-14 23:40:26 +04:00
|
|
|
|
2012-08-14 17:26:46 +04:00
|
|
|
jQuery.fn.sortChildren = function(func){
|
|
|
|
|
return $(this).each(function(_, e){
|
|
|
|
|
return $(e).append($(e).children().detach().get().sort(func))
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-08-14 23:40:26 +04:00
|
|
|
|
|
|
|
|
/********************************************************** Helpers **/
|
|
|
|
|
|
2012-09-24 00:44:52 +04:00
|
|
|
function getImagePath(img){
|
|
|
|
|
var data = getImageData($(img).attr('id'))
|
|
|
|
|
if(data != null){
|
|
|
|
|
return data.path
|
|
|
|
|
}
|
2012-08-10 20:25:49 +04:00
|
|
|
}
|
2012-08-14 18:08:45 +04:00
|
|
|
|
2012-09-24 00:44:52 +04:00
|
|
|
function getImageDate(img){
|
|
|
|
|
var data = getImageData($(img).attr('id'))
|
|
|
|
|
if(data != null){
|
|
|
|
|
return data.ctime
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-08-14 18:08:45 +04:00
|
|
|
|
2012-09-24 00:44:52 +04:00
|
|
|
|
|
|
|
|
function getImageId(img){
|
|
|
|
|
var id = $(img).attr('id')
|
2012-08-10 20:25:49 +04:00
|
|
|
}
|
|
|
|
|
|
2012-09-24 00:44:52 +04:00
|
|
|
// XXX make this an attr...
|
|
|
|
|
var getImageOrder = getImagePath
|
2012-08-10 20:25:49 +04:00
|
|
|
|
2012-09-24 00:44:52 +04:00
|
|
|
|
|
|
|
|
function setImageOrder(img, order){
|
|
|
|
|
return $(img).attr({'id': order})
|
2012-08-14 17:26:46 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2012-09-24 00:44:52 +04:00
|
|
|
function cmpImageOrder(a, b){
|
|
|
|
|
a = getImageOrder(a)
|
|
|
|
|
b = getImageOrder(b)
|
2012-09-05 19:35:47 +04:00
|
|
|
return a > b ? 1 : a < b ? -1 : 0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-08-20 02:12:33 +04:00
|
|
|
// NOTE: don't understand why am I the one who has to write this...
|
|
|
|
|
var SPECIAL_KEYS = {
|
|
|
|
|
9: 'Tab',
|
|
|
|
|
13: 'Enter',
|
|
|
|
|
16: 'Shift',
|
|
|
|
|
17: 'Ctrl',
|
|
|
|
|
18: 'Alt',
|
|
|
|
|
20: 'Caps Lock',
|
|
|
|
|
27: 'Esc',
|
|
|
|
|
32: 'Space',
|
|
|
|
|
33: 'PgUp',
|
|
|
|
|
34: 'PgDown',
|
|
|
|
|
35: 'End',
|
|
|
|
|
36: 'Home',
|
|
|
|
|
37: 'Right',
|
|
|
|
|
38: 'Up',
|
|
|
|
|
39: 'Left',
|
|
|
|
|
40: 'Down',
|
|
|
|
|
45: 'Ins',
|
|
|
|
|
46: 'Del',
|
|
|
|
|
80: 'Backspace',
|
|
|
|
|
91: 'Win',
|
|
|
|
|
93: 'Menu',
|
|
|
|
|
|
|
|
|
|
112: 'F1',
|
|
|
|
|
113: 'F2',
|
|
|
|
|
114: 'F3',
|
|
|
|
|
115: 'F4',
|
|
|
|
|
116: 'F5',
|
|
|
|
|
117: 'F6',
|
|
|
|
|
118: 'F7',
|
|
|
|
|
119: 'F8',
|
|
|
|
|
120: 'F9',
|
|
|
|
|
121: 'F10',
|
|
|
|
|
122: 'F11',
|
|
|
|
|
123: 'F12',
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// XXX some keys look really wrong...
|
|
|
|
|
function toKeyName(code){
|
|
|
|
|
// check for special keys...
|
|
|
|
|
var k = SPECIAL_KEYS[code]
|
|
|
|
|
if(k != null){
|
|
|
|
|
return k
|
|
|
|
|
}
|
|
|
|
|
// chars...
|
|
|
|
|
k = String.fromCharCode(code)
|
|
|
|
|
if(k != ''){
|
|
|
|
|
return k.toLowerCase()
|
|
|
|
|
}
|
|
|
|
|
return null
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2012-08-19 19:30:59 +04:00
|
|
|
// show a jQuary opject in viewer overlay...
|
2012-08-20 02:12:33 +04:00
|
|
|
// XXX need to set .scrollTop(0) when showing different UI...
|
|
|
|
|
// ...and not set it when the UI is the same
|
2012-09-03 00:15:48 +04:00
|
|
|
// XXX this must create it's own overlay...
|
2012-08-19 19:30:59 +04:00
|
|
|
function showInOverlay(obj){
|
2012-08-20 01:06:08 +04:00
|
|
|
obj.click(function(){ return false })
|
2012-09-10 03:02:34 +04:00
|
|
|
// XXX
|
|
|
|
|
$('.viewer').addClass('overlay-mode')
|
2012-08-19 19:30:59 +04:00
|
|
|
// clean things up...
|
|
|
|
|
$('.overlay .content').children().remove()
|
|
|
|
|
// put it in the overlay...
|
|
|
|
|
$('.overlay .content').append(obj)
|
|
|
|
|
// prepare the overlay...
|
|
|
|
|
$('.overlay')
|
|
|
|
|
.one('click', function(){
|
|
|
|
|
$('.overlay')
|
|
|
|
|
.fadeOut(function(){
|
|
|
|
|
$('.overlay .content')
|
|
|
|
|
.children()
|
|
|
|
|
.remove()
|
2012-09-10 03:02:34 +04:00
|
|
|
$('.overlay-mode').removeClass('overlay-mode')
|
2012-08-19 19:30:59 +04:00
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
.fadeIn()
|
|
|
|
|
return obj
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-08-25 03:16:44 +04:00
|
|
|
function overlayMessage(text){
|
|
|
|
|
return showInOverlay($('<div class="overlay-message">' +text+ '</div>'))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-08-20 02:12:33 +04:00
|
|
|
// XXX revise!!
|
|
|
|
|
function showOptionsUI(data, get_value, get_handler){
|
|
|
|
|
var tree = {}
|
|
|
|
|
var groups = []
|
|
|
|
|
var groups_ui = {}
|
|
|
|
|
// build the group/action structure...
|
|
|
|
|
for(var a in data){
|
|
|
|
|
var group = data[a].group!=null?data[a].group:'Other'
|
|
|
|
|
if(groups.indexOf(group) == -1){
|
|
|
|
|
groups.push(group)
|
|
|
|
|
}
|
|
|
|
|
if(tree[group] == null){
|
|
|
|
|
tree[group] = []
|
|
|
|
|
}
|
|
|
|
|
tree[group].push([
|
|
|
|
|
data[a].title!=null?data[a].title:a,
|
|
|
|
|
a
|
|
|
|
|
])
|
|
|
|
|
}
|
|
|
|
|
// sort things...
|
|
|
|
|
groups.sort()
|
|
|
|
|
for(var g in tree){
|
|
|
|
|
tree[g].sort(function(a, b){
|
|
|
|
|
a = a[0]
|
|
|
|
|
b = b[0]
|
|
|
|
|
return a > b ? 1 : a < b ? -1 : 0
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
// build the HTML...
|
|
|
|
|
var ui = $('<div class="options"/>')
|
|
|
|
|
for(var g in tree){
|
|
|
|
|
var group = null
|
|
|
|
|
for(var i=0; i<tree[g].length; i++){
|
|
|
|
|
// get the element...
|
|
|
|
|
var elem = data[tree[g][i][1]]
|
|
|
|
|
if(!DEBUG && elem.display == false){
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if(group == null){
|
|
|
|
|
group = $('<div class="group"/>')
|
|
|
|
|
.append($('<div class="title"/>').text(g))
|
|
|
|
|
}
|
|
|
|
|
var option
|
|
|
|
|
group.append(
|
|
|
|
|
option = $('<div class="option"/>').append($([
|
|
|
|
|
$('<div class="title"/>').text(tree[g][i][0])[0],
|
|
|
|
|
$('<div class="doc"/>').html(
|
|
|
|
|
elem.doc?elem.doc.replace(/\n/g, '<br>'):'')[0],
|
|
|
|
|
$('<div class="value"/>').text(get_value(elem))[0]
|
|
|
|
|
])))
|
|
|
|
|
if(elem.display == false){
|
|
|
|
|
option.addClass('disabled')
|
|
|
|
|
} else {
|
|
|
|
|
// handler...
|
|
|
|
|
var handler = get_handler(elem)
|
|
|
|
|
if(handler != null){
|
|
|
|
|
option.click(handler)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if(group != null){
|
|
|
|
|
groups_ui[g] = group
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// put the Other group in the back...
|
|
|
|
|
var i = groups.indexOf('Other')
|
|
|
|
|
if(i != -1){
|
|
|
|
|
groups.splice(i, 1)
|
|
|
|
|
groups.push('Other')
|
|
|
|
|
}
|
|
|
|
|
// buildup the sorted groups...
|
|
|
|
|
for(var i=0; i<groups.length; i++){
|
|
|
|
|
ui.append(groups_ui[groups[i]])
|
|
|
|
|
}
|
|
|
|
|
// refresh...
|
|
|
|
|
ui.click(function(){
|
|
|
|
|
// XXX is this a good way to do a refresh?
|
|
|
|
|
showOptionsUI(data, get_value, get_handler)
|
|
|
|
|
})
|
|
|
|
|
showInOverlay(ui)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-08-19 20:32:37 +04:00
|
|
|
// Return a scale value for the given element(s).
|
|
|
|
|
// NOTE: this will only return a single scale value...
|
2012-08-19 20:12:40 +04:00
|
|
|
function getElementScale(elem){
|
|
|
|
|
//var transform = elem.css('transform')
|
|
|
|
|
var vendors = ['o', 'moz', 'ms', 'webkit']
|
|
|
|
|
var transform = elem.css('transform')
|
|
|
|
|
var res
|
|
|
|
|
|
|
|
|
|
// go through vendor prefixes... (hate this!)
|
|
|
|
|
if(!transform || transform == 'none'){
|
|
|
|
|
for(var i in vendors){
|
|
|
|
|
transform = elem.css('-' + vendors[i] + '-transform')
|
|
|
|
|
if(transform && transform != 'none'){
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// no transform is set...
|
|
|
|
|
if(!transform || transform == 'none'){
|
|
|
|
|
return 1
|
|
|
|
|
}
|
|
|
|
|
// get the scale value -- first argument of scale/matrix...
|
|
|
|
|
return parseFloat((/(scale|matrix)\(([^,]*),.*\)/).exec(transform)[2])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function setElementScale(elem, scale){
|
|
|
|
|
return elem.css({
|
|
|
|
|
'transform': 'scale('+scale+', '+scale+')',
|
|
|
|
|
'-moz-transform': 'scale('+scale+', '+scale+')',
|
|
|
|
|
'-o-transform': 'scale('+scale+', '+scale+')',
|
|
|
|
|
'-ms-transform': 'scale('+scale+', '+scale+')',
|
|
|
|
|
'-webkit-transform': 'scale('+scale+', '+scale+')',
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2012-09-03 00:15:48 +04:00
|
|
|
|
|
|
|
|
// returns the width of the current image square...
|
|
|
|
|
function getCurrentImageSize(){
|
|
|
|
|
return ImageGrid.getContainerScale() * $('.image').width()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// returns the number of images fitting viewer size...
|
|
|
|
|
function getViewerWidthImages(){
|
|
|
|
|
return Math.floor($('.viewer').width()/getCurrentImageSize())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2012-08-08 01:45:22 +04:00
|
|
|
// this will create a function that will add/remove a css_class to elem
|
2012-08-10 19:40:26 +04:00
|
|
|
// calling the optional callbacks before and/or after.
|
|
|
|
|
//
|
|
|
|
|
// elem is a jquery compatible object; default use-case: a css selector.
|
|
|
|
|
//
|
2012-08-09 06:25:09 +04:00
|
|
|
// the resulting function understands the folowing arguments:
|
|
|
|
|
// - 'on' : switch mode on
|
|
|
|
|
// - 'off' : switch mode off
|
|
|
|
|
// - '?' : return current state ('on'|'off')
|
|
|
|
|
// - no arguments : toggle the state
|
2012-08-10 19:40:26 +04:00
|
|
|
//
|
|
|
|
|
// NOTE: of only one callback is given then it will be called after the
|
|
|
|
|
// class change...
|
|
|
|
|
// a way around this is to pass an empty function as callback_b
|
|
|
|
|
//
|
2012-08-08 01:45:22 +04:00
|
|
|
function createCSSClassToggler(elem, css_class, callback_a, callback_b){
|
|
|
|
|
// prepare the pre/post callbacks...
|
|
|
|
|
if(callback_b == null){
|
|
|
|
|
var callback_pre = null
|
|
|
|
|
var callback_post = callback_a
|
|
|
|
|
} else {
|
|
|
|
|
var callback_pre = callback_a
|
|
|
|
|
var callback_post = callback_b
|
|
|
|
|
}
|
2012-08-08 06:21:00 +04:00
|
|
|
// build the acual toggler function...
|
2012-08-15 02:15:37 +04:00
|
|
|
var func = function(action){
|
2012-08-09 06:25:09 +04:00
|
|
|
if(action == null || action == '?'){
|
|
|
|
|
var getter = action == '?' ? true : false
|
2012-08-08 01:45:22 +04:00
|
|
|
action = 'on'
|
|
|
|
|
// get current state...
|
|
|
|
|
if( $(elem).hasClass(css_class) ){
|
|
|
|
|
action = 'off'
|
|
|
|
|
}
|
2012-08-09 06:25:09 +04:00
|
|
|
if(getter){
|
|
|
|
|
// as the above actions indicate intent and not state,
|
|
|
|
|
// we'll need to swap the values...
|
|
|
|
|
return action == 'on' ? 'off' : 'on'
|
|
|
|
|
}
|
2012-08-08 01:45:22 +04:00
|
|
|
}
|
|
|
|
|
if(callback_pre != null){
|
|
|
|
|
callback_pre(action)
|
|
|
|
|
}
|
|
|
|
|
// play with the class...
|
2012-08-08 01:57:04 +04:00
|
|
|
if(action == 'on'){
|
2012-08-08 01:45:22 +04:00
|
|
|
$(elem).addClass(css_class)
|
|
|
|
|
} else {
|
|
|
|
|
$(elem).removeClass(css_class)
|
|
|
|
|
}
|
|
|
|
|
if(callback_post != null){
|
|
|
|
|
callback_post(action)
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-08-15 02:15:37 +04:00
|
|
|
func.doc = 'With no arguments this will toggle between "on" and '+
|
|
|
|
|
'"off".\n'+
|
|
|
|
|
'If either "on" or "off" are given then this will switch '+
|
|
|
|
|
'to that mode.\n'+
|
|
|
|
|
'If "?" is given, this will return either "on" or "off" '+
|
|
|
|
|
'depending on the current state.'
|
|
|
|
|
return func
|
2012-08-08 01:45:22 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// disable transitions on obj, call func then enable transitions back...
|
|
|
|
|
function doWithoutTransitions(obj, func){
|
|
|
|
|
obj
|
|
|
|
|
.addClass('unanimated')
|
|
|
|
|
.one("webkitTransitionEnd oTransitionEnd msTransitionEnd transitionend", function(){
|
|
|
|
|
func()
|
|
|
|
|
$('.viewer')
|
|
|
|
|
.one("webkitTransitionEnd oTransitionEnd msTransitionEnd transitionend", function(){
|
|
|
|
|
obj.removeClass('unanimated')
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2012-08-19 19:30:59 +04:00
|
|
|
|
|
|
|
|
function clickAfterTransitionsDone(img){
|
|
|
|
|
if(img == null){
|
|
|
|
|
img = $('.current.image')
|
|
|
|
|
}
|
|
|
|
|
$('.viewer')
|
|
|
|
|
.one("webkitTransitionEnd oTransitionEnd msTransitionEnd transitionend", function(){
|
|
|
|
|
img.click()
|
|
|
|
|
return true
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-08-08 01:45:22 +04:00
|
|
|
// find an image object after which to position image ID...
|
|
|
|
|
// used for two main tasks:
|
|
|
|
|
// - positioning promoted/demoted images
|
|
|
|
|
// - centering ribbons
|
|
|
|
|
// returns:
|
|
|
|
|
// - null - empty ribbon or no element greater id should be first
|
|
|
|
|
// - element
|
|
|
|
|
// XXX do we need to make ids numbers for this to work?
|
2012-08-10 20:25:49 +04:00
|
|
|
function getImageBefore_lin(id, ribbon, get_order){
|
|
|
|
|
if(get_order == null){
|
|
|
|
|
get_order = getImageOrder
|
|
|
|
|
}
|
2012-09-24 00:44:52 +04:00
|
|
|
var order = get_order($('#'+id))
|
2012-08-08 01:45:22 +04:00
|
|
|
// walk the ribbon till we find two images one with an ID less and
|
|
|
|
|
// another greater that id...
|
|
|
|
|
var images = ribbon.children('.image')
|
|
|
|
|
var prev = null
|
|
|
|
|
for(var i=0; i < images.length; i++){
|
2012-08-10 20:25:49 +04:00
|
|
|
// XXX replace the id attr with a universal getter
|
2012-09-24 00:44:52 +04:00
|
|
|
if(get_order(images[i]) > order){
|
2012-08-08 01:45:22 +04:00
|
|
|
return prev
|
|
|
|
|
}
|
|
|
|
|
prev = $(images[i])
|
|
|
|
|
}
|
|
|
|
|
return prev
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// generic binery search for element just before the id...
|
|
|
|
|
// NOTE: if id is in lst, this will return the element just before it.
|
|
|
|
|
// NOTE: lst must be sorted.
|
2012-09-24 00:44:52 +04:00
|
|
|
function binarySearch(order, lst, get_order){
|
2012-08-10 20:25:49 +04:00
|
|
|
if(get_order == null){
|
|
|
|
|
get_order = function(o){return o}
|
2012-08-08 01:45:22 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// empty list...
|
|
|
|
|
if(lst.length == 0){
|
|
|
|
|
return null
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// current section length
|
|
|
|
|
var l = Math.round((lst.length-1)/2)
|
|
|
|
|
// current position...
|
|
|
|
|
var i = l
|
|
|
|
|
|
|
|
|
|
while(true){
|
2012-09-24 00:44:52 +04:00
|
|
|
var i_order = get_order(lst[i])
|
2012-08-08 01:45:22 +04:00
|
|
|
// beginning of the array...
|
|
|
|
|
if(i == 0){
|
2012-09-24 00:44:52 +04:00
|
|
|
if(order > i_order){
|
2012-08-08 01:45:22 +04:00
|
|
|
return i
|
|
|
|
|
}
|
|
|
|
|
return null
|
|
|
|
|
}
|
|
|
|
|
// we got a hit...
|
2012-09-24 00:44:52 +04:00
|
|
|
if(i_order == order){
|
2012-08-08 01:45:22 +04:00
|
|
|
return i-1
|
|
|
|
|
}
|
|
|
|
|
// we are at the end...
|
2012-09-24 00:44:52 +04:00
|
|
|
if(i == lst.length-1 && order > i_order){
|
2012-08-08 01:45:22 +04:00
|
|
|
return i
|
|
|
|
|
}
|
2012-09-24 00:44:52 +04:00
|
|
|
var ii_order = get_order(lst[i+1])
|
|
|
|
|
// test if order is between i and i+1...
|
|
|
|
|
if( i_order < order && order < ii_order ){
|
2012-08-08 01:45:22 +04:00
|
|
|
return i
|
|
|
|
|
}
|
|
|
|
|
// prepare for next iteration...
|
|
|
|
|
// NOTE: we saturate the values so we will never get out of bounds.
|
|
|
|
|
l = Math.round(l/2)
|
2012-09-24 00:44:52 +04:00
|
|
|
if(order < i_order){
|
2012-08-08 01:45:22 +04:00
|
|
|
// lower half...
|
|
|
|
|
i = Math.max(0, i-l)
|
|
|
|
|
} else {
|
|
|
|
|
// upper half...
|
|
|
|
|
i = Math.min(i+l, lst.length-1)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// wrapper around binarySearch.
|
|
|
|
|
// this is here to make binarySearch simpler to test and debug...
|
2012-08-10 20:25:49 +04:00
|
|
|
function getImageBefore_bin(id, ribbon, get_order){
|
|
|
|
|
if(get_order == null){
|
|
|
|
|
get_order = getImageOrder
|
|
|
|
|
}
|
2012-09-24 00:44:52 +04:00
|
|
|
var order = get_order($('#'+id))
|
2012-08-08 01:45:22 +04:00
|
|
|
var images = ribbon.children('.image')
|
2012-09-24 00:44:52 +04:00
|
|
|
var i = binarySearch(order, images, get_order)
|
2012-08-08 01:45:22 +04:00
|
|
|
if(i == null){
|
|
|
|
|
return null
|
|
|
|
|
}
|
|
|
|
|
return $(images[i])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// set the default search...
|
|
|
|
|
var getImageBefore = getImageBefore_bin
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-08-19 19:30:59 +04:00
|
|
|
/*
|
|
|
|
|
* The folowing two functions will get the vertical and horizontal
|
|
|
|
|
* distance components between the points a and A, centers of the small
|
|
|
|
|
* and large squares respectively.
|
|
|
|
|
* One of the squares is .field and the other is .container,
|
|
|
|
|
* which is small or big is not important.
|
|
|
|
|
*
|
|
|
|
|
* +---------------+-------+
|
|
|
|
|
* | | |
|
|
|
|
|
* | | |
|
|
|
|
|
* | + a . . | . . . | . +
|
|
|
|
|
* | . | | +- getCurrentVerticalOffset(...)
|
|
|
|
|
* | . + A | . . . | . +
|
|
|
|
|
* +---------------+ |
|
|
|
|
|
* | . . |
|
|
|
|
|
* | . . |
|
|
|
|
|
* | . . |
|
|
|
|
|
* +-----------------------+
|
|
|
|
|
* . .
|
|
|
|
|
* +-+-+
|
|
|
|
|
* +------------------- getCurrentHorizontalOffset(...)
|
|
|
|
|
*
|
|
|
|
|
*
|
|
|
|
|
* Adding this distance to margins of one of the sqares will effectively
|
|
|
|
|
* allign the two points.
|
|
|
|
|
*
|
|
|
|
|
* NOTE: neither function accunts for field margins.
|
|
|
|
|
*
|
|
|
|
|
*/
|
2012-08-08 01:45:22 +04:00
|
|
|
|
2012-08-19 19:30:59 +04:00
|
|
|
// get the vertical offset of the center of square from center of container
|
|
|
|
|
// NOTE: this does not account for field margins
|
|
|
|
|
function getCurrentVerticalOffset(image){
|
|
|
|
|
if(image == null){
|
|
|
|
|
image = $('.image.current')
|
2012-06-08 18:30:54 +04:00
|
|
|
}
|
2012-08-06 16:24:20 +04:00
|
|
|
|
2012-08-19 19:30:59 +04:00
|
|
|
var scale = getElementScale($('.field'))
|
2012-08-06 16:24:20 +04:00
|
|
|
|
2012-08-19 19:30:59 +04:00
|
|
|
var ribbons = $('.ribbon')
|
|
|
|
|
var ribbon = image.parents('.ribbon')
|
|
|
|
|
var images = ribbon.children('.image')
|
|
|
|
|
|
|
|
|
|
// vertical...
|
|
|
|
|
var H = $('.container').height()
|
|
|
|
|
var h = ribbons.outerHeight(true)
|
|
|
|
|
// margin...
|
|
|
|
|
var mh = h - ribbons.outerHeight()
|
|
|
|
|
// current ribbon position (1-based)
|
|
|
|
|
var rn = ribbons.index(ribbon) + 1
|
|
|
|
|
// relative position to field...
|
|
|
|
|
// XXX is there a better way to get this?
|
|
|
|
|
var t = rn * (h - mh/2)
|
|
|
|
|
|
|
|
|
|
return -t + H/2 + h/2
|
2012-06-17 16:46:46 +04:00
|
|
|
}
|
|
|
|
|
|
2012-08-19 19:30:59 +04:00
|
|
|
// get the horizontal offset of the center of square from center of container
|
|
|
|
|
// NOTE: this does not account for field margins
|
|
|
|
|
function getCurrentHorizontalOffset(image){
|
|
|
|
|
if(image == null){
|
|
|
|
|
image = $('.image.current')
|
|
|
|
|
}
|
2012-06-17 16:46:46 +04:00
|
|
|
|
2012-08-19 19:30:59 +04:00
|
|
|
var ribbon = image.parents('.ribbon')
|
|
|
|
|
var images = ribbon.children('.image')
|
2012-06-17 16:46:46 +04:00
|
|
|
|
2012-08-19 19:30:59 +04:00
|
|
|
var W = $('.container').width()
|
|
|
|
|
var w = images.outerWidth(true)
|
|
|
|
|
// margin...
|
|
|
|
|
var mw = w - images.outerWidth()
|
|
|
|
|
// current square position (1-based)
|
|
|
|
|
var sn = images.index(image) + 1
|
|
|
|
|
var l = sn * (w - mw/2)
|
|
|
|
|
|
|
|
|
|
return -l + W/2 + w/2
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-08-22 19:39:20 +04:00
|
|
|
// XXX some minor inacuracies...
|
|
|
|
|
function centerIndicator(){
|
|
|
|
|
// XXX something odd going on with the border here...
|
|
|
|
|
var i_border = Math.abs($('.current-indicator').outerHeight() - $('.current-indicator').height())/2
|
|
|
|
|
$('.current-indicator').css({
|
|
|
|
|
'top': ($('.ribbon').index($('.current.ribbon'))) * $('.ribbon').outerHeight() - i_border,
|
|
|
|
|
'left': ($('.viewer').outerWidth() - $('.current-indicator').outerWidth())/2,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-08-19 19:30:59 +04:00
|
|
|
function centerSquare(){
|
|
|
|
|
$('.field').css({
|
|
|
|
|
'margin-top': getCurrentVerticalOffset()
|
|
|
|
|
})
|
|
|
|
|
// horizontal...
|
|
|
|
|
alignRibbon()
|
|
|
|
|
ImageGrid.centerCurrentImage()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function alignRibbon(image, position){
|
|
|
|
|
// default values...
|
|
|
|
|
if(image == null){
|
|
|
|
|
image = $('.image.current')
|
|
|
|
|
}
|
|
|
|
|
if(position == null){
|
|
|
|
|
position = 'center'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var ribbon = image.parents('.ribbon')
|
|
|
|
|
|
|
|
|
|
// account for margined field...
|
|
|
|
|
// NOTE: this enables us to cheat and shift all the ribbons just
|
|
|
|
|
// by changing field margin-left...
|
|
|
|
|
var cml = parseFloat($('.field').css('margin-left'))
|
|
|
|
|
if(!cml){
|
|
|
|
|
cml = 0
|
|
|
|
|
}
|
|
|
|
|
var h_offset = getCurrentHorizontalOffset(image) - cml
|
|
|
|
|
var w = $('.image').outerWidth(true)
|
|
|
|
|
|
|
|
|
|
switch(position){
|
|
|
|
|
case 'before':
|
|
|
|
|
ribbon.css({'margin-left': h_offset - w/2})
|
|
|
|
|
return true
|
|
|
|
|
case 'center':
|
|
|
|
|
ribbon.css({'margin-left': h_offset})
|
|
|
|
|
return true
|
|
|
|
|
case 'after':
|
|
|
|
|
ribbon.css({'margin-left': h_offset + w/2})
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// center other ribbons relative to current image...
|
|
|
|
|
// NOTE: only two ribbons are positioned at this point...
|
2012-09-24 00:44:52 +04:00
|
|
|
function alignRibbons(){
|
2012-08-19 19:30:59 +04:00
|
|
|
// XXX might be good to move this to a more generic location...
|
2012-09-24 00:44:52 +04:00
|
|
|
var id = $('.current.image').attr('id')
|
2012-08-19 19:30:59 +04:00
|
|
|
var directions = ['prev', 'next']
|
|
|
|
|
for(var i in directions){
|
|
|
|
|
var ribbon = $('.current.ribbon')[directions[i]]('.ribbon')
|
|
|
|
|
if(ribbon.length == 1){
|
|
|
|
|
var img = getImageBefore(id, ribbon)
|
|
|
|
|
if(img != null){
|
|
|
|
|
alignRibbon(img, 'before')
|
|
|
|
|
} else {
|
|
|
|
|
// there are no images before...
|
|
|
|
|
alignRibbon(ribbon.children('.image').first(), 'after')
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-09-24 19:40:06 +04:00
|
|
|
/************************************************** Info Generators **/
|
|
|
|
|
|
|
|
|
|
function currentImageNumberInRibbon(){
|
|
|
|
|
// XXX use image_data intead of DOM as the later can be loaded partially...
|
|
|
|
|
return (
|
|
|
|
|
($('.current.ribbon').children('.image').index($('.current.image'))+1)
|
|
|
|
|
+ '/'
|
|
|
|
|
+ $('.current.ribbon').children('.image').length)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function currentImagePath(){
|
|
|
|
|
if($('.current.image').length == 0){
|
|
|
|
|
return ''
|
|
|
|
|
}
|
|
|
|
|
return unescape(getImageData($('.current.image').attr('id')).path)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function updateInfo(){
|
|
|
|
|
$('.info .bottom-right')
|
|
|
|
|
.text(currentImageNumberInRibbon())
|
|
|
|
|
|
|
|
|
|
//$('.info .bottom-left')
|
|
|
|
|
// .text(currentImagePath())
|
|
|
|
|
}
|
|
|
|
|
|
2012-08-19 19:30:59 +04:00
|
|
|
|
2012-10-04 15:13:50 +04:00
|
|
|
|
|
|
|
|
|
2012-08-19 19:30:59 +04:00
|
|
|
/************************************************** Setup Functions **/
|
|
|
|
|
// XXX is this a correct place for these?
|
|
|
|
|
|
|
|
|
|
function setDefaultInitialState(){
|
|
|
|
|
if($('.current.ribbon').length == 0){
|
|
|
|
|
$('.ribbon').first().addClass('current')
|
|
|
|
|
}
|
|
|
|
|
if($('.current.image').length == 0){
|
|
|
|
|
$('.current.ribbon').children('.image').first().addClass('current')
|
|
|
|
|
}
|
2012-09-24 19:40:06 +04:00
|
|
|
|
|
|
|
|
updateInfo()
|
2012-08-19 19:30:59 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function setupEvents(){
|
2012-08-30 04:26:59 +04:00
|
|
|
var updated = false
|
2012-08-30 03:43:24 +04:00
|
|
|
// persistence...
|
2012-09-01 02:39:10 +04:00
|
|
|
$(window).unload(ImageGrid.saveState)
|
2012-08-30 03:43:24 +04:00
|
|
|
$(document)
|
2012-09-03 04:11:09 +04:00
|
|
|
.on([
|
|
|
|
|
// main modifier events...
|
|
|
|
|
'shiftImageUp',
|
|
|
|
|
'shiftImageDown',
|
|
|
|
|
'shiftImageUpNewRibbon',
|
|
|
|
|
'shiftImageDownNewRibbon',
|
|
|
|
|
'reverseImageOrder',
|
|
|
|
|
'reverseRibbons'
|
|
|
|
|
].join(' '),
|
|
|
|
|
function(){
|
|
|
|
|
updated = true
|
|
|
|
|
})
|
|
|
|
|
.on([
|
|
|
|
|
// navigation events...
|
|
|
|
|
'nextImage prevImage',
|
|
|
|
|
'nextScreenImages',
|
|
|
|
|
'prevScreenImages',
|
|
|
|
|
'focusAboveRibbon',
|
2012-09-24 19:40:06 +04:00
|
|
|
'focusBelowRibbon',
|
|
|
|
|
'firstImage',
|
|
|
|
|
'lastImage'
|
2012-09-03 04:11:09 +04:00
|
|
|
].join(' '),
|
|
|
|
|
function(){
|
2012-09-24 19:40:06 +04:00
|
|
|
/*
|
2012-09-03 04:11:09 +04:00
|
|
|
updated = true
|
2012-09-24 19:40:06 +04:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
updateInfo()
|
2012-09-03 04:11:09 +04:00
|
|
|
})
|
2012-09-05 18:34:39 +04:00
|
|
|
// zooming...
|
|
|
|
|
$(document)
|
|
|
|
|
.on([
|
|
|
|
|
'scaleContainerUp',
|
|
|
|
|
'scaleContainerDown',
|
2012-09-23 20:39:31 +04:00
|
|
|
'fitNImages',
|
|
|
|
|
|
|
|
|
|
'focusAboveRibbon',
|
|
|
|
|
'focusBelowRibbon'
|
2012-09-05 18:34:39 +04:00
|
|
|
].join(' '),
|
|
|
|
|
function(e){
|
2012-09-23 20:39:31 +04:00
|
|
|
// call this after transitions are done...
|
|
|
|
|
if(ImageGrid.toggleTransitions('?') == 'on'){
|
|
|
|
|
$('.viewer')
|
|
|
|
|
.one("webkitTransitionEnd oTransitionEnd msTransitionEnd transitionend", function(){
|
|
|
|
|
updateRibbonImages($('.current.image'), true)
|
|
|
|
|
})
|
|
|
|
|
} else {
|
|
|
|
|
// update images on zooming...
|
|
|
|
|
updateRibbonImages($('.current.image'), true)
|
|
|
|
|
}
|
2012-09-05 18:34:39 +04:00
|
|
|
})
|
2012-08-30 04:26:59 +04:00
|
|
|
// save things if updated within a minute...
|
2012-08-30 18:44:07 +04:00
|
|
|
// XXX this gets very slow when saving a large data dump...
|
2012-08-30 04:26:59 +04:00
|
|
|
setInterval(function(){
|
|
|
|
|
if(updated){
|
|
|
|
|
ImageGrid.saveState()
|
|
|
|
|
updated = false
|
2012-09-03 04:03:07 +04:00
|
|
|
}},
|
|
|
|
|
// check every 2 minutes...
|
|
|
|
|
2*60*1000)
|
2012-08-30 04:26:59 +04:00
|
|
|
// autosave every ten minutes...
|
2012-08-30 04:28:57 +04:00
|
|
|
// XXX do we really need this?
|
|
|
|
|
//setInterval(ImageGrid.saveState, 600000)
|
2012-08-30 03:43:24 +04:00
|
|
|
|
2012-08-19 19:30:59 +04:00
|
|
|
// resize...
|
|
|
|
|
$(window).resize(function() {
|
|
|
|
|
$('.current.image').click()
|
|
|
|
|
})
|
|
|
|
|
// keyboard...
|
|
|
|
|
if(DEBUG){
|
|
|
|
|
$(document)
|
2012-09-10 03:02:34 +04:00
|
|
|
.keydown(makeKeyboardHandler(keybindings, function(k){alert(k)}))
|
2012-08-19 19:30:59 +04:00
|
|
|
} else {
|
|
|
|
|
$(document)
|
2012-09-10 03:02:34 +04:00
|
|
|
.keydown(makeKeyboardHandler(keybindings))
|
2012-08-19 19:30:59 +04:00
|
|
|
}
|
|
|
|
|
// swipe...
|
|
|
|
|
$('.viewer')
|
|
|
|
|
.swipe({
|
|
|
|
|
swipeLeft: ImageGrid.nextImage,
|
|
|
|
|
swipeRight: ImageGrid.prevImage,
|
2012-12-19 18:37:17 +04:00
|
|
|
|
2012-08-19 21:23:29 +04:00
|
|
|
swipeUp: ImageGrid.shiftImageUp,
|
2012-12-19 17:48:02 +04:00
|
|
|
swipeDown: ImageGrid.shiftImageDown,
|
2012-12-19 18:58:18 +04:00
|
|
|
})
|
2012-12-19 22:51:11 +04:00
|
|
|
// pinch...
|
|
|
|
|
// XXX do gradual animated zooming...
|
|
|
|
|
// XXX this does not work with swipe (report submitted)...
|
2012-12-19 18:58:18 +04:00
|
|
|
$('.viewer')
|
|
|
|
|
.swipe({
|
2012-12-19 21:49:01 +04:00
|
|
|
pinchIn: ImageGrid.scaleContainerUp,
|
|
|
|
|
pinchOut: ImageGrid.scaleContainerDown,
|
2012-12-19 18:37:17 +04:00
|
|
|
|
2012-12-19 18:58:18 +04:00
|
|
|
fingers: '2'
|
2012-08-19 19:30:59 +04:00
|
|
|
})
|
2012-12-19 18:37:17 +04:00
|
|
|
|
2012-08-19 19:30:59 +04:00
|
|
|
// dragging...
|
|
|
|
|
// XXX make this work seamlessly with touchSwipe...
|
|
|
|
|
// XXX cancel clicks while dragging...
|
|
|
|
|
// XXX this does not work on android...
|
2012-12-19 18:37:17 +04:00
|
|
|
//$('.field').draggable()
|
2012-08-19 19:30:59 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function setupControlElements(){
|
|
|
|
|
// images...
|
2012-08-22 02:00:23 +04:00
|
|
|
// NOTE: when the images are loaded, the actual handlers will be set by the loader...
|
|
|
|
|
setupImageEventHandlers($(".image"))
|
2012-06-08 18:30:54 +04:00
|
|
|
|
2012-08-22 21:46:37 +04:00
|
|
|
// make the indicator active...
|
|
|
|
|
$(".current-indicator div")
|
|
|
|
|
.click(function(){
|
|
|
|
|
$('.current.image').click()
|
|
|
|
|
})
|
|
|
|
|
.dblclick(function(){
|
|
|
|
|
ImageGrid.toggleSingleImageMode()
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
2012-06-17 16:46:46 +04:00
|
|
|
// buttons...
|
2012-08-22 00:06:54 +04:00
|
|
|
$('.screen-button.next-image').mousedown(ImageGrid.nextImage)
|
|
|
|
|
$('.screen-button.prev-image').mousedown(ImageGrid.prevImage)
|
2012-08-08 01:45:22 +04:00
|
|
|
// XXX rename classes to "shift-image-up" and "shift-image-down"...
|
2012-08-22 00:06:54 +04:00
|
|
|
$('.screen-button.demote').mousedown(ImageGrid.shiftImageUp)
|
|
|
|
|
$('.screen-button.promote').mousedown(ImageGrid.shiftImageDown)
|
|
|
|
|
$('.screen-button.zoom-in').mousedown(ImageGrid.scaleContainerUp)
|
|
|
|
|
$('.screen-button.zoom-out').mousedown(ImageGrid.scaleContainerDown)
|
2012-08-15 04:03:11 +04:00
|
|
|
// XXX
|
2012-08-22 23:37:09 +04:00
|
|
|
$('.screen-button.toggle-wide').mousedown(ImageGrid.fit21Images)
|
|
|
|
|
$('.screen-button.toggle-single').mousedown(function(){ImageGrid.toggleSingleImageMode()})
|
2012-08-22 00:06:54 +04:00
|
|
|
$('.screen-button.fit-three').mousedown(ImageGrid.fitThreeImages)
|
|
|
|
|
$('.screen-button.show-controls').mousedown(function(){ImageGrid.toggleControls('on')})
|
|
|
|
|
$('.screen-button.settings').mousedown(ImageGrid.showKeyboardBindings)
|
2012-06-17 16:46:46 +04:00
|
|
|
}
|
2012-06-08 18:30:54 +04:00
|
|
|
|
2012-06-16 21:52:23 +04:00
|
|
|
|
|
|
|
|
|
2012-08-09 06:25:09 +04:00
|
|
|
/**************************************************** Serialization **/
|
|
|
|
|
|
|
|
|
|
|
2012-08-22 02:00:23 +04:00
|
|
|
// setup image event handlers...
|
|
|
|
|
function setupImageEventHandlers(image){
|
|
|
|
|
return (image
|
|
|
|
|
.click(handleImageClick)
|
|
|
|
|
.dblclick(function(e){
|
|
|
|
|
$(this).click()
|
|
|
|
|
ImageGrid.toggleSingleImageMode()
|
|
|
|
|
}))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// build an image element...
|
2012-09-05 18:34:39 +04:00
|
|
|
function makeImage(order, set_order){
|
2012-08-22 02:00:23 +04:00
|
|
|
if(set_order == null){
|
|
|
|
|
set_order = setImageOrder
|
|
|
|
|
}
|
|
|
|
|
return (setupImageEventHandlers(
|
2012-09-04 01:06:35 +04:00
|
|
|
set_order($('<div class="image"/>')
|
2012-09-15 16:42:33 +04:00
|
|
|
//set_order($('<div class="image"><div class="image-overlay"/></div>')
|
2012-09-04 03:50:16 +04:00
|
|
|
, order)))
|
|
|
|
|
}
|
|
|
|
|
|
2012-09-05 18:34:39 +04:00
|
|
|
|
2012-09-15 16:42:33 +04:00
|
|
|
// NOTE: if there is no id image this will return null
|
2012-09-05 18:34:39 +04:00
|
|
|
function getImageData(id){
|
2012-09-04 03:50:16 +04:00
|
|
|
var json = ImageGrid.image_data
|
|
|
|
|
var ribbons = json.ribbons
|
|
|
|
|
|
|
|
|
|
for(var i=0; i<ribbons.length; i++){
|
|
|
|
|
var ribbon = ribbons[i]
|
|
|
|
|
if(ribbon[id] != null){
|
2012-09-05 18:34:39 +04:00
|
|
|
return ribbon[id]
|
2012-09-04 03:50:16 +04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-09-15 16:42:33 +04:00
|
|
|
// Get a preview url of apropriate size...
|
2012-09-05 18:34:39 +04:00
|
|
|
// NOTE: this is largely independent of ImageGrid.image_data structure,
|
|
|
|
|
// it needs only content...
|
|
|
|
|
function getURL(id, size){
|
|
|
|
|
if(size == null){
|
|
|
|
|
size = 0
|
|
|
|
|
}
|
|
|
|
|
var json = ImageGrid.image_data
|
|
|
|
|
var ribbons = json.ribbons
|
|
|
|
|
|
|
|
|
|
var image = getImageData(id)
|
|
|
|
|
// select appropriate preview...
|
|
|
|
|
if(image.preview != null){
|
|
|
|
|
var sizes = []
|
|
|
|
|
var keys = []
|
|
|
|
|
var max
|
|
|
|
|
for(var s in image.preview){
|
|
|
|
|
if(max == null || max <= s){
|
|
|
|
|
max = s
|
|
|
|
|
}
|
|
|
|
|
if(parseInt(s) >= size){
|
|
|
|
|
sizes.push(parseInt(s))
|
|
|
|
|
keys.push(s)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// we are bigger than any preview...
|
|
|
|
|
if(sizes.length < 1){
|
|
|
|
|
return image.preview[max]
|
|
|
|
|
} else {
|
|
|
|
|
var cur_size = Math.min.apply(Math, sizes)
|
|
|
|
|
return image.preview[keys[sizes.indexOf(cur_size)]]
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// legacy default...
|
|
|
|
|
return image.url
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-09-15 16:42:33 +04:00
|
|
|
function updateImage(img, size){
|
|
|
|
|
var id = img.attr('id')
|
|
|
|
|
var overlay = $('#'+id+' .image-overlay')
|
2012-09-20 18:28:31 +04:00
|
|
|
var original_url = img.css('background-image')
|
|
|
|
|
var new_url = 'url('+getURL(id, size)+')'
|
|
|
|
|
|
|
|
|
|
// don't do anything if the url has not changed...
|
|
|
|
|
if(new_url != 'none' && new_url == original_url){
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2012-09-15 16:42:33 +04:00
|
|
|
// create an overlay with the same image...
|
2012-09-20 18:28:31 +04:00
|
|
|
/*
|
2012-09-15 16:42:33 +04:00
|
|
|
overlay
|
|
|
|
|
.css({
|
2012-09-20 18:28:31 +04:00
|
|
|
'background-image': original_url,
|
|
|
|
|
'display': 'block'
|
2012-09-15 16:42:33 +04:00
|
|
|
})
|
2012-09-20 18:28:31 +04:00
|
|
|
*/
|
2012-09-15 16:42:33 +04:00
|
|
|
img
|
|
|
|
|
.css({
|
2012-09-20 18:28:31 +04:00
|
|
|
'background-image': new_url
|
2012-09-15 16:42:33 +04:00
|
|
|
})
|
2012-09-20 18:28:31 +04:00
|
|
|
/*
|
2012-09-15 16:42:33 +04:00
|
|
|
// when the new image loads, fadeout the overlay remove it...
|
2012-09-20 18:28:31 +04:00
|
|
|
// XXX this fires before the image is loaded...
|
2012-09-15 16:42:33 +04:00
|
|
|
.ready(function(){
|
|
|
|
|
overlay.fadeOut()
|
|
|
|
|
})
|
2012-09-20 18:28:31 +04:00
|
|
|
*/
|
2012-09-15 16:42:33 +04:00
|
|
|
}
|
|
|
|
|
|
2012-09-04 03:50:16 +04:00
|
|
|
|
|
|
|
|
|
2012-09-05 18:34:39 +04:00
|
|
|
var SCREEN_WIDTH_CACHE = 4
|
2012-09-04 03:50:16 +04:00
|
|
|
|
|
|
|
|
// XXX make this update only when the threshold is passed...
|
2012-09-05 18:34:39 +04:00
|
|
|
// XXX update images on zoom...
|
2012-09-04 03:50:16 +04:00
|
|
|
// NOTE: this is largely independent of ImageGrid.image_data...
|
2012-09-05 18:34:39 +04:00
|
|
|
function updateRibbonImages(img, force){
|
|
|
|
|
var r = getViewerWidthImages()
|
|
|
|
|
var size = getCurrentImageSize()
|
2012-09-04 03:50:16 +04:00
|
|
|
var R = r*SCREEN_WIDTH_CACHE
|
|
|
|
|
var images = img.parents('.ribbon').children('.image')
|
|
|
|
|
|
|
|
|
|
/* XXX for some reason this does not work...
|
|
|
|
|
// check the threshold -- one screen-width in any direction...
|
|
|
|
|
var i = images.filter('.loaded').index(img)
|
|
|
|
|
if(i >= 0 && Math.abs(i - images.filter('.loaded').length) < r){
|
|
|
|
|
console.log('skipping...', i, images.filter('.loaded').length)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
console.log('loading...', i, images.filter('.loaded').length)
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
var cur_i = images.index(img)
|
|
|
|
|
|
|
|
|
|
// load...
|
|
|
|
|
var loading = $([])
|
|
|
|
|
for(var i=Math.max(0, cur_i-R); i<=Math.min(images.length-1, cur_i+R); i++){
|
|
|
|
|
var img = $(images[i])
|
|
|
|
|
loading.push(img[0])
|
|
|
|
|
// update only the images that are not set...
|
2012-09-05 18:34:39 +04:00
|
|
|
// XXX update images on zoom...
|
2012-09-04 03:50:16 +04:00
|
|
|
var bg = img.css('background-image')
|
2012-09-05 18:34:39 +04:00
|
|
|
if(force || bg == 'none' || bg == null){
|
2012-09-15 16:42:33 +04:00
|
|
|
updateImage(img, size)
|
2012-09-04 03:50:16 +04:00
|
|
|
}
|
|
|
|
|
//img.not('.loaded').css({ 'background-image': 'url('+getURL(img.attr('id'))+')' })
|
|
|
|
|
// remove the processed images from the list...
|
|
|
|
|
images[i] = {}
|
|
|
|
|
}
|
|
|
|
|
// do the loading...
|
|
|
|
|
loading.not('.loaded')
|
|
|
|
|
.addClass('loaded')
|
|
|
|
|
// unload...
|
2012-09-05 18:34:39 +04:00
|
|
|
images.filter('.loaded')
|
|
|
|
|
.removeClass('loaded')
|
2012-09-04 03:50:16 +04:00
|
|
|
.css({ 'background-image': 'none' })
|
2012-08-22 02:00:23 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-08-29 19:14:57 +04:00
|
|
|
function loadImagesFromList(images){
|
2012-09-03 22:03:43 +04:00
|
|
|
var json = {
|
|
|
|
|
ribbons: [
|
|
|
|
|
{}
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
var ribbon = json.ribbons[0]
|
2012-06-08 18:30:54 +04:00
|
|
|
for(var i = 0; i < images.length; i++){
|
2012-09-03 22:03:43 +04:00
|
|
|
ribbon[i] = {
|
|
|
|
|
url: images[i]
|
|
|
|
|
}
|
2012-06-08 18:30:54 +04:00
|
|
|
}
|
2012-09-03 22:03:43 +04:00
|
|
|
return loadJSON(json)
|
2012-06-08 18:30:54 +04:00
|
|
|
}
|
|
|
|
|
|
2012-06-16 21:52:23 +04:00
|
|
|
|
|
|
|
|
|
2012-08-09 17:53:07 +04:00
|
|
|
/* bulid a JSON object from current state...
|
|
|
|
|
*
|
2012-08-09 06:25:09 +04:00
|
|
|
* format:
|
|
|
|
|
* {
|
2012-08-29 18:28:01 +04:00
|
|
|
* position: <image-id>,
|
2012-08-09 06:25:09 +04:00
|
|
|
* ribbons: [
|
2012-09-03 22:03:43 +04:00
|
|
|
* {
|
|
|
|
|
* <image-id>: {
|
|
|
|
|
* url: <image-URL>,
|
|
|
|
|
* },
|
|
|
|
|
* ...
|
|
|
|
|
* },
|
2012-08-09 06:25:09 +04:00
|
|
|
* ...
|
|
|
|
|
* ]
|
|
|
|
|
* }
|
|
|
|
|
*/
|
2012-08-11 03:38:42 +04:00
|
|
|
// XXX add incremental or partial updates...
|
2012-09-24 00:44:52 +04:00
|
|
|
function buildJSON(){
|
2012-09-04 03:50:16 +04:00
|
|
|
/* XXX can't return this yet as we are not updating this properly yet...
|
2012-09-03 22:03:43 +04:00
|
|
|
if(ImageGrid.image_data != null){
|
|
|
|
|
return ImageGrid.image_data
|
|
|
|
|
}
|
2012-09-04 03:50:16 +04:00
|
|
|
*/
|
2012-09-05 18:34:39 +04:00
|
|
|
var size = getCurrentImageSize()
|
2012-08-09 06:25:09 +04:00
|
|
|
var ribbons = $('.ribbon')
|
|
|
|
|
res = {
|
2012-08-29 18:28:01 +04:00
|
|
|
position: $('.current.image').attr('id'),
|
2012-08-09 06:25:09 +04:00
|
|
|
ribbons: []
|
|
|
|
|
}
|
|
|
|
|
for(var i=0; i < ribbons.length; i++){
|
|
|
|
|
var images = $(ribbons[i]).children('.image')
|
2012-09-01 02:27:31 +04:00
|
|
|
// skip empty ribbons...
|
|
|
|
|
if(images.length == 0){
|
|
|
|
|
continue
|
|
|
|
|
}
|
2012-08-09 17:53:07 +04:00
|
|
|
var ribbon = {}
|
2012-09-04 16:04:54 +04:00
|
|
|
res.ribbons.push(ribbon)
|
2012-08-09 06:25:09 +04:00
|
|
|
for(var j=0; j < images.length; j++){
|
|
|
|
|
var image = $(images[j])
|
2012-09-24 00:44:52 +04:00
|
|
|
var data = getImageData(image.attr('id'))
|
|
|
|
|
ribbon[data.id] = data
|
2012-08-09 06:25:09 +04:00
|
|
|
}
|
|
|
|
|
}
|
2012-09-05 18:34:39 +04:00
|
|
|
ImageGrid.image_data = res
|
2012-08-09 06:25:09 +04:00
|
|
|
return res
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-08-09 17:53:07 +04:00
|
|
|
// XXX might be good to add images in packs here, not one by one...
|
2012-09-24 00:44:52 +04:00
|
|
|
// make this work on detached elements...
|
2012-09-05 18:34:39 +04:00
|
|
|
function loadJSON(data, position, set_order){
|
2012-08-30 04:26:59 +04:00
|
|
|
if(position == null){
|
|
|
|
|
position = data.position
|
|
|
|
|
}
|
2012-08-11 02:59:11 +04:00
|
|
|
if(set_order == null){
|
|
|
|
|
set_order = setImageOrder
|
|
|
|
|
}
|
2012-08-09 06:25:09 +04:00
|
|
|
var ribbons = data.ribbons
|
2012-08-30 04:26:59 +04:00
|
|
|
if(ribbons == null){
|
|
|
|
|
return
|
|
|
|
|
}
|
2012-09-03 22:03:43 +04:00
|
|
|
|
|
|
|
|
// store the structure...
|
|
|
|
|
ImageGrid.image_data = data
|
|
|
|
|
|
2012-08-09 06:25:09 +04:00
|
|
|
var field = $('.field')
|
2012-09-24 00:44:52 +04:00
|
|
|
field.hide()
|
|
|
|
|
//var c = field.parent()
|
|
|
|
|
|
|
|
|
|
//field.detach()
|
2012-08-09 06:25:09 +04:00
|
|
|
|
|
|
|
|
// drop all old content...
|
2012-08-22 19:39:20 +04:00
|
|
|
field.children('.ribbon').remove()
|
2012-08-09 06:25:09 +04:00
|
|
|
|
2012-09-05 18:34:39 +04:00
|
|
|
var order = 0
|
|
|
|
|
|
2012-08-09 06:25:09 +04:00
|
|
|
for(var i=0; i < ribbons.length; i++){
|
|
|
|
|
var images = ribbons[i]
|
2012-09-01 02:27:31 +04:00
|
|
|
// skip empty ribbons...
|
|
|
|
|
if(images.length == 0){
|
|
|
|
|
continue
|
|
|
|
|
}
|
2012-08-09 06:25:09 +04:00
|
|
|
// create ribbon...
|
|
|
|
|
var ribbon = $('<div class="ribbon"></div>')
|
2012-09-05 18:34:39 +04:00
|
|
|
var new_images = {}
|
2012-08-09 17:53:07 +04:00
|
|
|
for(var j in images){
|
2012-09-04 01:06:35 +04:00
|
|
|
var image = images[j]
|
2012-09-05 19:35:47 +04:00
|
|
|
makeImage(j, set_order)
|
2012-08-22 02:00:23 +04:00
|
|
|
.appendTo(ribbon)
|
2012-09-05 18:34:39 +04:00
|
|
|
order++
|
2012-08-09 06:25:09 +04:00
|
|
|
}
|
2012-09-24 00:44:52 +04:00
|
|
|
ribbon.appendTo(field)
|
2012-08-09 06:25:09 +04:00
|
|
|
}
|
2012-09-24 00:44:52 +04:00
|
|
|
// sort images...
|
|
|
|
|
ImageGrid.sortImages()
|
2012-09-05 18:34:39 +04:00
|
|
|
console.log('loaded: ', order)
|
2012-08-30 04:26:59 +04:00
|
|
|
if(position != null && $('#' + position).length != 0){
|
|
|
|
|
$('#' + position).click()
|
2012-08-29 18:28:01 +04:00
|
|
|
} else {
|
|
|
|
|
$('.image').first().click()
|
|
|
|
|
}
|
2012-09-24 00:44:52 +04:00
|
|
|
|
|
|
|
|
//field.appendTo(c)
|
|
|
|
|
field.show()
|
2012-08-09 06:25:09 +04:00
|
|
|
}
|
|
|
|
|
|
2012-06-17 16:46:46 +04:00
|
|
|
|
2012-08-10 19:40:26 +04:00
|
|
|
|
|
|
|
|
|
2012-06-17 16:46:46 +04:00
|
|
|
/*************************************************** Event Handlers **/
|
2012-08-04 20:26:38 +04:00
|
|
|
|
2012-08-10 19:40:26 +04:00
|
|
|
// handle click for images...
|
|
|
|
|
function handleImageClick(){
|
2012-09-04 04:19:43 +04:00
|
|
|
var ribbons = $(this).parents('.ribbon').siblings('.ribbon')
|
2012-07-24 15:38:54 +04:00
|
|
|
// set classes...
|
|
|
|
|
$('.current').removeClass('current')
|
|
|
|
|
$(this)
|
|
|
|
|
.addClass('current')
|
|
|
|
|
.parents('.ribbon')
|
|
|
|
|
.addClass('current')
|
|
|
|
|
// position the field and ribbons...
|
|
|
|
|
centerSquare()
|
2012-08-22 19:39:20 +04:00
|
|
|
centerIndicator()
|
2012-07-31 17:41:54 +04:00
|
|
|
alignRibbons()
|
2012-09-04 04:19:43 +04:00
|
|
|
// update this ribbon...
|
2012-09-05 18:34:39 +04:00
|
|
|
updateRibbonImages($(this))
|
2012-09-04 04:19:43 +04:00
|
|
|
// update other ribbons...
|
2012-09-04 03:50:16 +04:00
|
|
|
var id = $(this).attr('id')
|
|
|
|
|
for(var i=0; i<ribbons.length; i++){
|
2012-09-04 04:19:43 +04:00
|
|
|
var img = getImageBefore(id, $(ribbons[i]))
|
|
|
|
|
// XXX revise: should we check if ribbon is empty if img is null??
|
2012-09-05 18:34:39 +04:00
|
|
|
updateRibbonImages(img?img:$(ribbons[i]).children('.image').first())
|
2012-09-04 03:50:16 +04:00
|
|
|
}
|
2012-07-31 17:41:54 +04:00
|
|
|
}
|
|
|
|
|
|
2012-08-04 20:26:38 +04:00
|
|
|
|
|
|
|
|
|
2012-08-19 21:42:14 +04:00
|
|
|
// if set to false the event handlers will always return false...
|
|
|
|
|
var KEYBOARD_HANDLER_PROPAGATE = false
|
|
|
|
|
|
|
|
|
|
/* Basic key format:
|
2012-08-08 23:19:40 +04:00
|
|
|
* <key-code> : <callback>,
|
|
|
|
|
* <key-code> : {
|
2012-08-10 19:40:26 +04:00
|
|
|
* 'default': <callback>,
|
2012-08-08 23:19:40 +04:00
|
|
|
* // a modifier can be any single modifier, like shift or a
|
|
|
|
|
* // combination of modifers like 'ctrl+shift', given in order
|
|
|
|
|
* // of priority.
|
|
|
|
|
* // supported modifiers are (in order of priority):
|
|
|
|
|
* // - ctrl
|
|
|
|
|
* // - alt
|
|
|
|
|
* // - shift
|
|
|
|
|
* <modifer>: [...]
|
|
|
|
|
* },
|
2012-08-19 19:30:59 +04:00
|
|
|
* <key-code> : [
|
|
|
|
|
* // this can be any type of handler except for an alias...
|
|
|
|
|
* <handler>,
|
|
|
|
|
* <doc>
|
|
|
|
|
* ],
|
2012-08-08 23:19:40 +04:00
|
|
|
* // alias...
|
|
|
|
|
* <key-code-a> : <key-code-b>,
|
2012-08-10 20:25:49 +04:00
|
|
|
*
|
|
|
|
|
* XXX might need to add meta information to generate sensible help...
|
2012-08-08 23:19:40 +04:00
|
|
|
*/
|
2012-09-10 03:02:34 +04:00
|
|
|
function makeKeyboardHandler(keybindings, unhandled){
|
2012-08-08 23:19:40 +04:00
|
|
|
if(unhandled == null){
|
|
|
|
|
unhandled = function(){return false}
|
|
|
|
|
}
|
|
|
|
|
return function(evt){
|
2012-09-10 04:23:48 +04:00
|
|
|
var did_handling = false
|
|
|
|
|
var res = null
|
2012-09-10 03:02:34 +04:00
|
|
|
for(var mode in keybindings){
|
|
|
|
|
if($(mode).length > 0){
|
|
|
|
|
var bindings = keybindings[mode]
|
2012-08-08 23:19:40 +04:00
|
|
|
|
2012-09-10 03:02:34 +04:00
|
|
|
var key = evt.keyCode
|
|
|
|
|
if(bindings.ignore != null && bindings.ignore.indexOf(key) != -1){
|
2012-09-10 04:23:48 +04:00
|
|
|
// return true
|
|
|
|
|
did_handling = true
|
|
|
|
|
continue
|
2012-09-10 03:02:34 +04:00
|
|
|
}
|
|
|
|
|
// XXX ugly...
|
|
|
|
|
var modifers = evt.ctrlKey ? 'ctrl' : ''
|
|
|
|
|
modifers += evt.altKey ? (modifers != '' ? '+alt' : 'alt') : ''
|
|
|
|
|
modifers += evt.shiftKey ? (modifers != '' ? '+shift' : 'shift') : ''
|
2012-08-08 23:19:40 +04:00
|
|
|
|
2012-09-10 03:02:34 +04:00
|
|
|
var handler = bindings[key]
|
|
|
|
|
|
|
|
|
|
// alias...
|
|
|
|
|
while (typeof(handler) == typeof(123)) {
|
|
|
|
|
handler = bindings[handler]
|
|
|
|
|
}
|
|
|
|
|
// no handler...
|
|
|
|
|
if(handler == null){
|
2012-09-10 04:23:48 +04:00
|
|
|
continue
|
2012-09-10 03:02:34 +04:00
|
|
|
}
|
|
|
|
|
// Array, lisp style with docs...
|
|
|
|
|
// XXX for some odd reason in chrome typeof([]) == typeof({})!!!
|
|
|
|
|
if(typeof(handler) == typeof([]) && handler.constructor.name == 'Array'){
|
|
|
|
|
// we do not care about docs here, so just get the handler...
|
|
|
|
|
handler = handler[0]
|
|
|
|
|
}
|
|
|
|
|
// complex handler...
|
|
|
|
|
if(typeof(handler) == typeof({})){
|
|
|
|
|
var callback = handler[modifers]
|
|
|
|
|
if(callback == null){
|
|
|
|
|
callback = handler['default']
|
|
|
|
|
}
|
|
|
|
|
if(callback != null){
|
2012-09-10 04:23:48 +04:00
|
|
|
res = callback()
|
|
|
|
|
did_handling = true
|
|
|
|
|
continue
|
2012-09-10 03:02:34 +04:00
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// simple callback...
|
2012-09-10 04:23:48 +04:00
|
|
|
res = handler()
|
|
|
|
|
did_handling = true
|
|
|
|
|
continue
|
2012-09-10 03:02:34 +04:00
|
|
|
}
|
2012-06-08 18:30:54 +04:00
|
|
|
}
|
2012-08-08 23:19:40 +04:00
|
|
|
}
|
2012-09-10 04:23:48 +04:00
|
|
|
if(!did_handling){
|
|
|
|
|
// key is unhandled by any modes...
|
|
|
|
|
return unhandled(key)
|
|
|
|
|
} else {
|
|
|
|
|
// XXX should we handle multiple hits???
|
|
|
|
|
return KEYBOARD_HANDLER_PROPAGATE&&res?true:false
|
|
|
|
|
}
|
2012-08-08 23:19:40 +04:00
|
|
|
}
|
2012-06-08 18:30:54 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2012-06-16 21:52:23 +04:00
|
|
|
|
2012-06-17 16:46:46 +04:00
|
|
|
|
2012-08-19 19:30:59 +04:00
|
|
|
/************************************************ Mode & UI Actions **/
|
2012-08-14 22:08:18 +04:00
|
|
|
|
2012-08-19 20:32:37 +04:00
|
|
|
ImageGrid.GROUP('Mode: All',
|
2012-08-29 19:14:57 +04:00
|
|
|
ImageGrid.ACTION({
|
|
|
|
|
title: 'Save current state.',
|
|
|
|
|
},
|
|
|
|
|
function saveState(){
|
2012-10-04 16:05:59 +04:00
|
|
|
ImageGrid.save_localstorage()
|
2012-08-29 19:14:57 +04:00
|
|
|
}),
|
2012-08-19 20:32:37 +04:00
|
|
|
ImageGrid.ACTION({
|
|
|
|
|
title: 'Get the background mode',
|
2012-08-20 14:21:01 +04:00
|
|
|
display: false,
|
2012-08-19 20:32:37 +04:00
|
|
|
},
|
|
|
|
|
function getBackgroundMode(){
|
|
|
|
|
var mode = null
|
|
|
|
|
var BACKGROUND_MODES = ImageGrid.option.BACKGROUND_MODES
|
|
|
|
|
// find a mode to set...
|
|
|
|
|
for(var i = 0; i < BACKGROUND_MODES.length; i++){
|
|
|
|
|
// we found our mode...
|
|
|
|
|
if( $('.' + BACKGROUND_MODES[i]).length > 0 ){
|
|
|
|
|
return BACKGROUND_MODES[i]
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return mode
|
|
|
|
|
}),
|
|
|
|
|
ImageGrid.ACTION({
|
|
|
|
|
title: 'Set the background mode',
|
2012-08-20 14:21:01 +04:00
|
|
|
doc: 'NOTE: passing null will set the default.',
|
|
|
|
|
display: false,
|
2012-08-19 20:32:37 +04:00
|
|
|
},
|
|
|
|
|
function setBackgroundMode(mode){
|
|
|
|
|
var BACKGROUND_MODES = ImageGrid.option.BACKGROUND_MODES
|
|
|
|
|
var cur = BACKGROUND_MODES.indexOf(mode)
|
2012-08-05 22:06:52 +04:00
|
|
|
|
2012-08-19 20:32:37 +04:00
|
|
|
// invalid mode...
|
|
|
|
|
if( cur == -1 && mode != null ){
|
|
|
|
|
return null
|
|
|
|
|
}
|
|
|
|
|
// set the mode...
|
|
|
|
|
if(mode != null){
|
|
|
|
|
$('.viewer').addClass(mode)
|
|
|
|
|
}
|
|
|
|
|
// remove all others...
|
|
|
|
|
for(var i = 0; i < BACKGROUND_MODES.length; i++){
|
|
|
|
|
if( i == cur ){
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
mode = BACKGROUND_MODES[i]
|
|
|
|
|
$('.' + mode).removeClass(mode)
|
|
|
|
|
}
|
|
|
|
|
}),
|
|
|
|
|
ImageGrid.ACTION({
|
|
|
|
|
title: 'Toggle background modes',
|
|
|
|
|
doc: 'Toggle through background theems: none -> dark -> black\n\n'+
|
|
|
|
|
'NOTE: modes are toggled independently for single image and '+
|
|
|
|
|
'rinbon modes',
|
2012-08-22 02:00:23 +04:00
|
|
|
type: 'toggle',
|
2012-08-19 20:32:37 +04:00
|
|
|
},
|
2012-08-22 02:00:23 +04:00
|
|
|
function toggleBackgroundModes(action){
|
|
|
|
|
if(action == '?'){
|
|
|
|
|
return ImageGrid.getBackgroundMode()
|
|
|
|
|
} else if(action != null){
|
|
|
|
|
return ImageGrid.setBackgroundMode(action)
|
2012-08-19 20:32:37 +04:00
|
|
|
} else {
|
2012-08-22 02:00:23 +04:00
|
|
|
var BACKGROUND_MODES = ImageGrid.option.BACKGROUND_MODES
|
|
|
|
|
var mode = ImageGrid.getBackgroundMode()
|
|
|
|
|
// default -> first
|
|
|
|
|
if(mode == null){
|
|
|
|
|
ImageGrid.setBackgroundMode(BACKGROUND_MODES[0])
|
|
|
|
|
// last -> default...
|
|
|
|
|
} else if(mode == BACKGROUND_MODES[BACKGROUND_MODES.length-1]){
|
|
|
|
|
ImageGrid.setBackgroundMode()
|
|
|
|
|
// next...
|
|
|
|
|
} else {
|
|
|
|
|
ImageGrid.setBackgroundMode(BACKGROUND_MODES[BACKGROUND_MODES.indexOf(mode)+1])
|
|
|
|
|
}
|
2012-08-19 20:32:37 +04:00
|
|
|
}
|
|
|
|
|
}),
|
2012-09-24 19:40:06 +04:00
|
|
|
ImageGrid.ACTION({
|
|
|
|
|
id: 'toggleInfo',
|
|
|
|
|
title: 'Single additional information',
|
|
|
|
|
type: 'toggle',
|
|
|
|
|
},
|
|
|
|
|
createCSSClassToggler('.viewer', 'display-info', updateInfo)),
|
2012-08-05 22:06:52 +04:00
|
|
|
|
2012-08-19 19:30:59 +04:00
|
|
|
ImageGrid.ACTION({
|
|
|
|
|
id: 'toggleControls',
|
2012-08-19 23:18:11 +04:00
|
|
|
title: 'Toggle keyboard-oriented interface',
|
2012-08-19 19:30:59 +04:00
|
|
|
doc: 'Toggle Touch/Keyboard UI controls.',
|
|
|
|
|
type: 'toggle',
|
|
|
|
|
},
|
|
|
|
|
createCSSClassToggler('.viewer', 'hidden-controls')),
|
|
|
|
|
ImageGrid.ACTION({
|
|
|
|
|
id: 'toggleTransitions',
|
|
|
|
|
title: 'Global transitions',
|
|
|
|
|
doc: 'Toggle global transitions.',
|
|
|
|
|
type: 'toggle',
|
|
|
|
|
},
|
2012-08-22 02:00:23 +04:00
|
|
|
createCSSClassToggler('.viewer', 'transitions-enabled')))
|
|
|
|
|
|
2012-08-19 20:12:40 +04:00
|
|
|
|
2012-08-22 02:00:23 +04:00
|
|
|
|
|
|
|
|
ImageGrid.GROUP('Configuration and Help',
|
2012-08-19 20:32:37 +04:00
|
|
|
ImageGrid.ACTION({
|
|
|
|
|
title: 'Close overlay'
|
|
|
|
|
},
|
|
|
|
|
function closeOverlay(){ $('.overlay').click() }),
|
2012-08-19 20:12:40 +04:00
|
|
|
// XXX use order and priority of options...
|
|
|
|
|
// XXX make history work for this...
|
|
|
|
|
// XXX should this be a toggle??
|
|
|
|
|
ImageGrid.ACTION({
|
|
|
|
|
title: 'Settings',
|
|
|
|
|
doc: 'Show setup interface.',
|
|
|
|
|
},
|
|
|
|
|
function showSetup(){
|
2012-08-20 02:12:33 +04:00
|
|
|
showOptionsUI(ImageGrid.option_props,
|
2012-08-22 02:00:23 +04:00
|
|
|
function(e){
|
|
|
|
|
// XXX need to update a value here...
|
|
|
|
|
return ImageGrid.option[e.name]
|
|
|
|
|
},
|
2012-08-20 01:06:08 +04:00
|
|
|
function(e){return e.click_handler})
|
2012-08-20 02:12:33 +04:00
|
|
|
}),
|
2012-09-10 03:02:34 +04:00
|
|
|
// XXX do not use global keybindings...
|
2012-08-20 02:12:33 +04:00
|
|
|
ImageGrid.ACTION({
|
|
|
|
|
title: 'Keyboard configuration',
|
|
|
|
|
doc: 'Show keyboard configuration interface.',
|
|
|
|
|
},
|
|
|
|
|
function showKeyboardBindings(){
|
|
|
|
|
// build reverse key index...
|
|
|
|
|
var bindings = {}
|
2012-09-10 03:02:34 +04:00
|
|
|
for(var m in keybindings){
|
|
|
|
|
var mode_bindings = keybindings[m]
|
2012-08-20 02:12:33 +04:00
|
|
|
|
2012-09-10 03:02:34 +04:00
|
|
|
// XXX do the doc for the mode...
|
|
|
|
|
// XXX
|
|
|
|
|
|
|
|
|
|
for(var k in mode_bindings){
|
|
|
|
|
// XXX skip doc attrs...
|
|
|
|
|
if(k == 'title' || k == 'doc' || k == 'ignore'){
|
|
|
|
|
continue
|
2012-08-20 02:12:33 +04:00
|
|
|
}
|
2012-09-10 03:02:34 +04:00
|
|
|
|
|
|
|
|
var id
|
|
|
|
|
var v = mode_bindings[k]
|
|
|
|
|
|
|
|
|
|
// alias...
|
|
|
|
|
while (typeof(v) == typeof(123)) {
|
|
|
|
|
v = mode_bindings[v]
|
|
|
|
|
}
|
|
|
|
|
// Array, lisp style with docs...
|
|
|
|
|
if(typeof(v) == typeof([]) && v.constructor.name == 'Array'){
|
|
|
|
|
// XXX what do we do here???
|
|
|
|
|
}
|
|
|
|
|
// function...
|
|
|
|
|
if(typeof(v) == typeof(function(){})){
|
|
|
|
|
id = v.id != null ? v.id : v.name
|
|
|
|
|
}
|
|
|
|
|
// complex handler...
|
|
|
|
|
// NOTE: this can contain several key bindings...
|
|
|
|
|
if(typeof(v) == typeof({})){
|
|
|
|
|
for(var m in v){
|
|
|
|
|
id = v[m].id != null ? v[m].id : v[m].name
|
|
|
|
|
if(bindings[id] == null){
|
|
|
|
|
bindings[id] = []
|
|
|
|
|
}
|
|
|
|
|
bindings[id].push((m=='default'?'':m+'+') + toKeyName(k))
|
|
|
|
|
}
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(bindings[id] == null){
|
|
|
|
|
bindings[id] = []
|
|
|
|
|
}
|
|
|
|
|
bindings[id].push(toKeyName(k))
|
2012-08-20 02:12:33 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
showOptionsUI(ImageGrid.actions,
|
|
|
|
|
function(e){
|
|
|
|
|
return (bindings[e.id]!=null?bindings[e.id]:'None')
|
|
|
|
|
.toString()
|
|
|
|
|
.replace(/,/g, ', ')
|
|
|
|
|
},
|
|
|
|
|
// XXX
|
|
|
|
|
function(e){})
|
2012-08-19 20:32:37 +04:00
|
|
|
}))
|
2012-08-10 18:31:32 +04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-08-19 19:30:59 +04:00
|
|
|
ImageGrid.GROUP('Mode: Single Image',
|
|
|
|
|
ImageGrid.ACTION({
|
|
|
|
|
id: 'toggleSingleImageMode',
|
|
|
|
|
title: 'Single image mode',
|
|
|
|
|
doc: 'Toggle single image mode.',
|
|
|
|
|
type: 'toggle',
|
|
|
|
|
},
|
|
|
|
|
createCSSClassToggler('.viewer', 'single-image-mode',
|
|
|
|
|
// pre...
|
|
|
|
|
function(action){
|
|
|
|
|
if(action == 'on'){
|
2012-08-19 20:32:37 +04:00
|
|
|
ImageGrid.option.NORMAL_MODE_BG = ImageGrid.getBackgroundMode()
|
2012-09-24 19:40:06 +04:00
|
|
|
ImageGrid.option.NORMAL_MODE_INFO = ImageGrid.toggleInfo('?')
|
2012-08-19 19:30:59 +04:00
|
|
|
ImageGrid.option.ORIGINAL_FIELD_SCALE = getElementScale($('.field'))
|
|
|
|
|
// do this only when coming out of single image mode...
|
|
|
|
|
} else if(ImageGrid.toggleSingleImageMode('?') == 'on'){
|
2012-08-19 20:32:37 +04:00
|
|
|
ImageGrid.option.SINGLE_IMAGE_MODE_BG = ImageGrid.getBackgroundMode()
|
2012-08-19 19:30:59 +04:00
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
// post...
|
|
|
|
|
function(action){
|
|
|
|
|
if(action == 'on'){
|
2012-08-19 20:12:40 +04:00
|
|
|
ImageGrid.fitImage()
|
2012-08-19 20:32:37 +04:00
|
|
|
ImageGrid.setBackgroundMode(ImageGrid.option.SINGLE_IMAGE_MODE_BG)
|
2012-09-24 19:40:06 +04:00
|
|
|
ImageGrid.toggleInfo('off')
|
2012-08-19 19:30:59 +04:00
|
|
|
} else {
|
2012-08-19 20:12:40 +04:00
|
|
|
ImageGrid.setContainerScale(ImageGrid.option.ORIGINAL_FIELD_SCALE)
|
2012-08-19 20:32:37 +04:00
|
|
|
ImageGrid.setBackgroundMode(ImageGrid.option.NORMAL_MODE_BG)
|
2012-09-24 19:40:06 +04:00
|
|
|
ImageGrid.toggleInfo(ImageGrid.option.NORMAL_MODE_INFO)
|
2012-08-19 19:30:59 +04:00
|
|
|
}
|
|
|
|
|
clickAfterTransitionsDone()
|
|
|
|
|
})),
|
|
|
|
|
// XXX for some reason this is backwords... (says 'on' when it's off ans 'off' when on)
|
|
|
|
|
// ...and needs an extra click to sync with state...
|
|
|
|
|
ImageGrid.ACTION({
|
|
|
|
|
id: 'toggleSingleImageModeTransitions',
|
|
|
|
|
title: 'Disable single image mode transitions',
|
|
|
|
|
doc: 'Toggle transitions in single image mode.',
|
|
|
|
|
type: 'toggle',
|
|
|
|
|
},
|
|
|
|
|
createCSSClassToggler('.viewer', 'no-single-image-transitions')))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ImageGrid.GROUP('Mode: Ribbon',
|
|
|
|
|
ImageGrid.ACTION({
|
|
|
|
|
id: 'toggleSingleRibbonMode',
|
|
|
|
|
title: 'Single ribbon mode',
|
|
|
|
|
doc: 'Show/hide other ribbons.',
|
|
|
|
|
type: 'toggle',
|
|
|
|
|
},
|
|
|
|
|
createCSSClassToggler('.viewer', 'single-ribbon-mode')),
|
2012-08-22 19:39:20 +04:00
|
|
|
ImageGrid.ACTION({
|
|
|
|
|
id: 'toggleCurrentRibbonOpacity',
|
|
|
|
|
title: 'Current ribbon opacity',
|
|
|
|
|
doc: 'Toggle other image transparancy/opacity in current ribbon.',
|
|
|
|
|
type: 'toggle',
|
|
|
|
|
},
|
|
|
|
|
createCSSClassToggler('.viewer', 'opaque-current-ribbon')),
|
|
|
|
|
ImageGrid.ACTION({
|
|
|
|
|
id: 'toggleIndicatorDot',
|
|
|
|
|
title: 'Dot indicator',
|
|
|
|
|
doc: 'Toggle indicator between dot and frame modes.\n\n'+
|
|
|
|
|
'NOTE: this is visible only when the indicator is visible.',
|
|
|
|
|
type: 'toggle',
|
|
|
|
|
},
|
|
|
|
|
createCSSClassToggler('.viewer', 'dot-indicator')),
|
2012-08-19 19:30:59 +04:00
|
|
|
|
|
|
|
|
// XXX this can be done in two ways:
|
|
|
|
|
// - keep all images when promoting, just add a class to them that
|
|
|
|
|
// will hide them until we enable their display...
|
|
|
|
|
// + very fast to show/hide
|
|
|
|
|
// - will complicate reversing ribbons allot
|
|
|
|
|
// - add/remove these images on demand
|
|
|
|
|
// + a tad complicated...
|
|
|
|
|
ImageGrid.ACTION({
|
|
|
|
|
id: 'toggleDisplayShiftedUpImages',
|
|
|
|
|
title: 'Display shifted up images',
|
|
|
|
|
doc: 'Toggle display of shifted images.',
|
|
|
|
|
display: false,
|
|
|
|
|
type: 'toggle',
|
|
|
|
|
},
|
|
|
|
|
createCSSClassToggler('.viewer', 'show-shifted-up-images')))
|
2012-08-10 18:31:32 +04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-08-02 17:54:40 +04:00
|
|
|
|
|
|
|
|
|
2012-08-19 19:30:59 +04:00
|
|
|
/********************************************************* Movement **/
|
2012-08-02 17:54:40 +04:00
|
|
|
|
2012-08-19 19:30:59 +04:00
|
|
|
ImageGrid.GROUP('Movement',
|
|
|
|
|
ImageGrid.ACTION({
|
|
|
|
|
title: 'Center origin',
|
|
|
|
|
doc: 'Set the transform-origin to the center of the current view.',
|
|
|
|
|
display: false,
|
|
|
|
|
},
|
|
|
|
|
function centerOrigin(){
|
|
|
|
|
var mt = parseFloat($('.field').css('margin-top'))
|
|
|
|
|
var ml = parseFloat($('.field').css('margin-left'))
|
|
|
|
|
var cml = parseFloat($('.current.ribbon').css('margin-left'))
|
|
|
|
|
|
|
|
|
|
var t = parseFloat($('.field').css('top'))
|
|
|
|
|
var l = parseFloat($('.field').css('left'))
|
|
|
|
|
var w = $('.field').width()
|
|
|
|
|
var h = $('.field').height()
|
|
|
|
|
var W = $('.container').width()
|
|
|
|
|
var H = $('.container').height()
|
|
|
|
|
|
|
|
|
|
var ot = -getCurrentVerticalOffset() + H/2 - t
|
|
|
|
|
var ol = -ml + W/2 - l
|
|
|
|
|
|
|
|
|
|
$('.field').css({
|
|
|
|
|
'transform-origin': ol + 'px ' + ot + 'px',
|
|
|
|
|
'-o-transform-origin': ol + 'px ' + ot + 'px',
|
|
|
|
|
'-moz-transform-origin': ol + 'px ' + ot + 'px',
|
|
|
|
|
'-webkit-transform-origin': ol + 'px ' + ot + 'px',
|
|
|
|
|
'-ms-transform-origin': ol + 'px ' + ot + 'px'
|
|
|
|
|
})
|
2012-08-10 18:31:32 +04:00
|
|
|
|
2012-08-19 19:30:59 +04:00
|
|
|
// XXX for debugging...
|
|
|
|
|
$('.origin-marker').css({
|
|
|
|
|
'top': ot,
|
|
|
|
|
'left': ol
|
|
|
|
|
})
|
|
|
|
|
}),
|
|
|
|
|
// XXX these work oddly when page is scaled in maxthon...
|
|
|
|
|
// XXX virtually identical, see of can be merged...
|
|
|
|
|
ImageGrid.ACTION({
|
|
|
|
|
title: 'Move view up',
|
|
|
|
|
},
|
|
|
|
|
function moveViewUp(){
|
|
|
|
|
var t = parseInt($('.field').css('top'))
|
|
|
|
|
$('.field').css({'top': t-(ImageGrid.option.MOVE_DELTA)})
|
|
|
|
|
}),
|
|
|
|
|
ImageGrid.ACTION({
|
|
|
|
|
title: 'Move view down',
|
|
|
|
|
},
|
|
|
|
|
function moveViewDown(){
|
|
|
|
|
var t = parseInt($('.field').css('top'))
|
|
|
|
|
$('.field').css({'top': t+(ImageGrid.option.MOVE_DELTA)})
|
|
|
|
|
}),
|
|
|
|
|
ImageGrid.ACTION({
|
|
|
|
|
title: 'Move view left',
|
|
|
|
|
},
|
|
|
|
|
function moveViewLeft(){
|
|
|
|
|
var l = parseInt($('.field').css('left'))
|
|
|
|
|
$('.field').css({'left': l-(ImageGrid.option.MOVE_DELTA)})
|
|
|
|
|
}),
|
|
|
|
|
ImageGrid.ACTION({
|
|
|
|
|
title: 'Move view right',
|
|
|
|
|
},
|
|
|
|
|
function moveViewRight(){
|
|
|
|
|
var l = parseInt($('.field').css('left'))
|
|
|
|
|
$('.field').css({'left': l+(ImageGrid.option.MOVE_DELTA)})
|
|
|
|
|
}),
|
2012-08-10 18:31:32 +04:00
|
|
|
|
2012-08-19 19:30:59 +04:00
|
|
|
ImageGrid.ACTION({
|
|
|
|
|
title: 'Center current image',
|
|
|
|
|
},
|
|
|
|
|
function centerCurrentImage(){
|
|
|
|
|
$('.field')
|
|
|
|
|
.css({
|
|
|
|
|
'top': 0,
|
|
|
|
|
'left': 0
|
|
|
|
|
})
|
|
|
|
|
// do this after animations are done...
|
|
|
|
|
.one("webkitTransitionEnd oTransitionEnd msTransitionEnd transitionend", ImageGrid.centerOrigin)
|
|
|
|
|
// this is repeated intentionally...
|
|
|
|
|
// ...needed for small shifts, while the after-animation event
|
|
|
|
|
// is for large moves.
|
|
|
|
|
ImageGrid.centerOrigin()
|
|
|
|
|
}))
|
2012-08-10 18:31:32 +04:00
|
|
|
|
|
|
|
|
|
2012-08-04 20:26:38 +04:00
|
|
|
|
2012-06-17 16:46:46 +04:00
|
|
|
/******************************************************* Navigation **/
|
|
|
|
|
|
2012-08-19 19:30:59 +04:00
|
|
|
ImageGrid.GROUP('Navigation',
|
|
|
|
|
// basic navigation...
|
|
|
|
|
ImageGrid.ACTION({
|
|
|
|
|
title: 'Go to first image',
|
|
|
|
|
},
|
|
|
|
|
function firstImage(){
|
|
|
|
|
return $('.current.ribbon').children('.image').first().click()
|
|
|
|
|
}),
|
|
|
|
|
ImageGrid.ACTION({
|
|
|
|
|
title: 'Go to previous image',
|
|
|
|
|
},
|
|
|
|
|
function prevImage(){
|
2012-09-07 13:48:47 +04:00
|
|
|
ImageGrid.option.LAST_MOVE_DIRECTION = 'prev'
|
2012-08-19 19:30:59 +04:00
|
|
|
return $('.current.image').prev('.image').click()
|
|
|
|
|
}),
|
|
|
|
|
ImageGrid.ACTION({
|
|
|
|
|
title: 'Go to next image',
|
|
|
|
|
},
|
|
|
|
|
function nextImage(){
|
2012-09-07 13:48:47 +04:00
|
|
|
ImageGrid.option.LAST_MOVE_DIRECTION = 'next'
|
2012-08-19 19:30:59 +04:00
|
|
|
return $('.current.image').next('.image').click()
|
|
|
|
|
}),
|
|
|
|
|
ImageGrid.ACTION({
|
|
|
|
|
title: 'Go to last image',
|
|
|
|
|
},
|
|
|
|
|
function lastImage(){
|
|
|
|
|
return $('.current.ribbon').children('.image').last().click()
|
|
|
|
|
}),
|
|
|
|
|
|
|
|
|
|
ImageGrid.ACTION({
|
|
|
|
|
title: 'Skip screen images',
|
|
|
|
|
doc: 'Skip screen-width images in specified direction',
|
|
|
|
|
display: false,
|
|
|
|
|
},
|
|
|
|
|
function skipScreenImages(direction){
|
|
|
|
|
// calculate screen width in images...
|
|
|
|
|
var W = $('.viewer').width()
|
|
|
|
|
var w = $('.current.image').width()
|
|
|
|
|
var scale = getElementScale($('.field'))
|
|
|
|
|
var n = Math.max(Math.floor(W/(w*scale))-1, 0)
|
|
|
|
|
|
|
|
|
|
var img = $('.current.image')[direction + 'All']('.image').eq(n)
|
|
|
|
|
if(img.length > 0){
|
|
|
|
|
return img.click()
|
|
|
|
|
} else if(direction == 'next'){
|
|
|
|
|
return ImageGrid.lastImage()
|
|
|
|
|
} else if(direction == 'prev'){
|
|
|
|
|
return ImageGrid.firstImage()
|
|
|
|
|
}
|
|
|
|
|
}),
|
|
|
|
|
ImageGrid.ACTION({
|
|
|
|
|
title: 'Skip next screen images',
|
|
|
|
|
},
|
|
|
|
|
function nextScreenImages(){ return ImageGrid.skipScreenImages('next') }),
|
|
|
|
|
ImageGrid.ACTION({
|
|
|
|
|
title: 'Skip screen images backwards',
|
|
|
|
|
},
|
|
|
|
|
function prevScreenImages(){ return ImageGrid.skipScreenImages('prev') }),
|
2012-08-04 18:57:54 +04:00
|
|
|
|
2012-08-19 19:30:59 +04:00
|
|
|
ImageGrid.ACTION({
|
|
|
|
|
title: 'Focus ribbon',
|
|
|
|
|
doc: 'Focus ribbon in specified direction',
|
|
|
|
|
display: false,
|
|
|
|
|
},
|
2012-09-24 00:44:52 +04:00
|
|
|
function focusRibbon(direction){
|
|
|
|
|
var id = $('.current.image').attr('id')
|
2012-08-19 19:30:59 +04:00
|
|
|
var prev = getImageBefore(id, $('.current.ribbon')[direction]('.ribbon'))
|
|
|
|
|
if(prev){
|
|
|
|
|
var next = prev.next()
|
|
|
|
|
// NOTE: direction is accounted for to make the up/down shifts
|
|
|
|
|
// symmetrical in the general case...
|
|
|
|
|
if(next.length == 0 || direction == 'next'){
|
|
|
|
|
return prev.click()
|
|
|
|
|
} else {
|
|
|
|
|
return next.click()
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
return $('.current.ribbon')[direction]('.ribbon').children('.image').first().click()
|
|
|
|
|
}
|
|
|
|
|
}),
|
|
|
|
|
ImageGrid.ACTION({
|
|
|
|
|
title: 'Focus ribbon above',
|
|
|
|
|
},
|
|
|
|
|
function focusAboveRibbon(){ return ImageGrid.focusRibbon('prev') }),
|
|
|
|
|
ImageGrid.ACTION({
|
|
|
|
|
title: 'Focus ribbon below',
|
|
|
|
|
},
|
|
|
|
|
function focusBelowRibbon(){ return ImageGrid.focusRibbon('next') }))
|
2012-08-02 17:54:40 +04:00
|
|
|
|
2012-06-08 18:30:54 +04:00
|
|
|
|
|
|
|
|
|
2012-06-17 16:46:46 +04:00
|
|
|
|
|
|
|
|
|
2012-08-10 18:31:32 +04:00
|
|
|
/********************************************************** Zooming **/
|
|
|
|
|
|
2012-08-19 20:12:40 +04:00
|
|
|
ImageGrid.GROUP('Zooming',
|
2012-09-03 00:15:48 +04:00
|
|
|
ImageGrid.ACTION({
|
|
|
|
|
title: 'Get container scale',
|
|
|
|
|
display: false,
|
|
|
|
|
},
|
|
|
|
|
function getContainerScale(){
|
|
|
|
|
return getElementScale($('.field'))
|
|
|
|
|
}),
|
2012-08-19 20:12:40 +04:00
|
|
|
ImageGrid.ACTION({
|
|
|
|
|
title: 'Scale container by factor',
|
2012-08-20 14:21:01 +04:00
|
|
|
display: false,
|
2012-08-19 20:12:40 +04:00
|
|
|
},
|
|
|
|
|
function scaleContainerBy(factor){
|
|
|
|
|
return ImageGrid.setContainerScale(getElementScale($('.field'))*factor)
|
|
|
|
|
}),
|
|
|
|
|
ImageGrid.ACTION({
|
|
|
|
|
title: 'Scale container up',
|
|
|
|
|
},
|
|
|
|
|
function scaleContainerUp(){
|
|
|
|
|
return ImageGrid.scaleContainerBy(ImageGrid.option.ZOOM_FACTOR)
|
|
|
|
|
}),
|
|
|
|
|
ImageGrid.ACTION({
|
|
|
|
|
title: 'Scale container down',
|
|
|
|
|
},
|
|
|
|
|
function scaleContainerDown(){
|
|
|
|
|
return ImageGrid.scaleContainerBy(1/ImageGrid.option.ZOOM_FACTOR)
|
|
|
|
|
}),
|
|
|
|
|
ImageGrid.ACTION({
|
|
|
|
|
title: 'Set container scale',
|
2012-08-19 21:34:31 +04:00
|
|
|
display: false,
|
2012-08-19 20:12:40 +04:00
|
|
|
},
|
|
|
|
|
function setContainerScale(scale){
|
|
|
|
|
return setElementScale($('.field'), scale)
|
|
|
|
|
}),
|
2012-08-10 18:31:32 +04:00
|
|
|
|
|
|
|
|
|
2012-08-19 20:12:40 +04:00
|
|
|
ImageGrid.ACTION({
|
|
|
|
|
title: 'Fit N images to container width/height',
|
2012-08-20 14:21:01 +04:00
|
|
|
display: false,
|
2012-08-19 20:12:40 +04:00
|
|
|
},
|
|
|
|
|
function fitNImages(n){
|
|
|
|
|
var H = $('.container').height()
|
|
|
|
|
var W = $('.container').width()
|
2012-08-10 18:31:32 +04:00
|
|
|
|
2012-08-19 20:12:40 +04:00
|
|
|
var h = $('.image.current').height()
|
|
|
|
|
// NOTE: this is cheating, need to get actual three widths...
|
|
|
|
|
var w = $('.image.current').width()*n
|
2012-08-10 18:31:32 +04:00
|
|
|
|
2012-08-19 20:12:40 +04:00
|
|
|
var f = Math.min(H/h, W/w)
|
2012-08-10 18:31:32 +04:00
|
|
|
|
2012-08-26 01:03:15 +04:00
|
|
|
ImageGrid.centerCurrentImage()
|
2012-08-19 20:12:40 +04:00
|
|
|
ImageGrid.setContainerScale(f)
|
|
|
|
|
}),
|
|
|
|
|
// the fit N image pack, for 1 <= N <= 9
|
2012-08-20 01:06:08 +04:00
|
|
|
ImageGrid.ACTION({ title: 'Fit 1 image' }, function fitImage(){ImageGrid.fitNImages(1)}),
|
|
|
|
|
ImageGrid.ACTION({ title: 'Fit 2 images' }, function fitTwoImages(){ImageGrid.fitNImages(2)}),
|
|
|
|
|
ImageGrid.ACTION({ title: 'Fit 3 images' }, function fitThreeImages(){ImageGrid.fitNImages(3)}),
|
|
|
|
|
ImageGrid.ACTION({ title: 'Fit 4 images' }, function fitFourImages(){ImageGrid.fitNImages(4)}),
|
|
|
|
|
ImageGrid.ACTION({ title: 'Fit 5 images' }, function fitFiveImages(){ImageGrid.fitNImages(5)}),
|
|
|
|
|
ImageGrid.ACTION({ title: 'Fit 6 images' }, function fitSixImages(){ImageGrid.fitNImages(6)}),
|
|
|
|
|
ImageGrid.ACTION({ title: 'Fit 7 images' }, function fitSevenImages(){ImageGrid.fitNImages(7)}),
|
|
|
|
|
ImageGrid.ACTION({ title: 'Fit 8 images' }, function fitEightImages(){ImageGrid.fitNImages(8)}),
|
2012-08-22 23:37:09 +04:00
|
|
|
ImageGrid.ACTION({ title: 'Fit 9 images' }, function fitNineImages(){ImageGrid.fitNImages(9)}),
|
|
|
|
|
ImageGrid.ACTION({ title: 'Fit 21 images' }, function fit21Images(){ImageGrid.fitNImages(21)})
|
2012-08-19 20:12:40 +04:00
|
|
|
)
|
2012-08-10 18:31:32 +04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-08-19 21:23:29 +04:00
|
|
|
/*************************************************** Ribbon Actions **/
|
2012-06-16 21:52:23 +04:00
|
|
|
// basic actions...
|
2012-08-04 19:47:13 +04:00
|
|
|
// NOTE: below 'direction' argument is meant in the html sence,
|
|
|
|
|
// i.e. next/prev...
|
2012-06-16 21:52:23 +04:00
|
|
|
|
2012-08-19 21:23:29 +04:00
|
|
|
ImageGrid.GROUP('Ribbon manipulations',
|
|
|
|
|
// XXX adding a ribbon above the current is still jumpy, need to devise
|
|
|
|
|
// a cleaner way to do this...
|
|
|
|
|
ImageGrid.ACTION({
|
2012-08-19 21:42:14 +04:00
|
|
|
title: 'Create a ribbon above/below current',
|
2012-08-19 21:34:31 +04:00
|
|
|
display: false,
|
2012-08-19 21:23:29 +04:00
|
|
|
},
|
|
|
|
|
function createRibbon(direction){
|
|
|
|
|
if(direction == 'next'){
|
|
|
|
|
var insert = 'insertAfter'
|
|
|
|
|
} else if(direction == 'prev') {
|
|
|
|
|
var insert = 'insertBefore'
|
|
|
|
|
} else {
|
|
|
|
|
return false
|
|
|
|
|
}
|
2012-08-14 17:26:46 +04:00
|
|
|
|
2012-08-19 21:23:29 +04:00
|
|
|
// adding a new ribbon above the current effectively pushes the
|
|
|
|
|
// whole view down, so we need to compensate for this.
|
|
|
|
|
// NOTE: the problem is partly caused by clicks fiering BEFORE the
|
|
|
|
|
// animation is done...
|
|
|
|
|
$('.field').addClass('unanimated')
|
|
|
|
|
|
|
|
|
|
if(direction == 'prev'){
|
|
|
|
|
$('.field').css({
|
|
|
|
|
'margin-top': parseInt($('.field').css('margin-top')) - $('.ribbon').outerHeight()
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
// the actual insert...
|
|
|
|
|
var res = $('<div class="ribbon"></div>')[insert]('.current.ribbon')
|
|
|
|
|
|
|
|
|
|
// restore the animated state...
|
2012-10-14 23:28:36 +04:00
|
|
|
// XXX this is a hack...
|
|
|
|
|
setTimeout(function(){$('.field').removeClass('unanimated')}, 10)
|
|
|
|
|
//$('.field').removeClass('unanimated')
|
2012-08-14 17:26:46 +04:00
|
|
|
|
2012-08-19 21:23:29 +04:00
|
|
|
return res
|
|
|
|
|
}),
|
|
|
|
|
// XXX this uses jquery animation...
|
|
|
|
|
// XXX one way to optimise this is to add the lesser ribbon to the
|
|
|
|
|
// greater disregarding their actual order...
|
|
|
|
|
// XXX think about using $(...).sortChildren(...) / sortImages()
|
|
|
|
|
ImageGrid.ACTION({
|
|
|
|
|
title: 'Merge current and direction ribbon.',
|
|
|
|
|
doc: 'NOTE: this will take all the elements from direction '+
|
2012-08-19 21:42:14 +04:00
|
|
|
'ribbon and add them to current.',
|
2012-08-19 21:34:31 +04:00
|
|
|
display: false,
|
2012-08-19 21:23:29 +04:00
|
|
|
},
|
2012-09-24 00:44:52 +04:00
|
|
|
function mergeRibbons(direction){
|
2012-08-19 21:23:29 +04:00
|
|
|
var current_ribbon = $('.current.ribbon')
|
|
|
|
|
var images = $('.current.ribbon')[direction]('.ribbon').children()
|
|
|
|
|
for(var i=0; i < images.length; i++){
|
|
|
|
|
var image = $(images[i])
|
|
|
|
|
// get previous element after which we need to put the current...
|
2012-09-24 00:44:52 +04:00
|
|
|
var prev_elem = getImageBefore(image.attr('id'), current_ribbon)
|
2012-08-19 21:23:29 +04:00
|
|
|
// check if we need to be before the first element...
|
|
|
|
|
if(prev_elem == null){
|
|
|
|
|
image
|
|
|
|
|
.detach()
|
|
|
|
|
.insertBefore(current_ribbon.children('.image').first())
|
|
|
|
|
} else {
|
|
|
|
|
image
|
|
|
|
|
.detach()
|
|
|
|
|
.insertAfter(prev_elem)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// animate...
|
|
|
|
|
$('.current.ribbon')[direction]('.ribbon')
|
|
|
|
|
.slideUp(function(){
|
|
|
|
|
$(this).remove()
|
|
|
|
|
$('.current.image').click()
|
|
|
|
|
})
|
|
|
|
|
}),
|
|
|
|
|
|
|
|
|
|
ImageGrid.ACTION({
|
|
|
|
|
title: 'Reverse ribbon order',
|
|
|
|
|
doc: 'NOTE: this is like flipping the field vertically.',
|
|
|
|
|
},
|
|
|
|
|
function reverseRibbons(){
|
|
|
|
|
// reverse...
|
|
|
|
|
$('.field').reverseChildren()
|
|
|
|
|
// compensate for offset cange...
|
|
|
|
|
$('.current.image').click()
|
|
|
|
|
}))
|
2012-08-14 17:26:46 +04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-08-19 21:23:29 +04:00
|
|
|
/**************************************************** Image Actions **/
|
|
|
|
|
ImageGrid.GROUP('Image manipulation',
|
|
|
|
|
ImageGrid.ACTION({
|
|
|
|
|
title: 'Shift image in direction',
|
2012-08-19 21:34:31 +04:00
|
|
|
display: false,
|
2012-08-19 21:23:29 +04:00
|
|
|
},
|
2012-09-24 00:44:52 +04:00
|
|
|
function shiftImage(direction){
|
2012-08-19 21:23:29 +04:00
|
|
|
if($('.current.ribbon')[direction]('.ribbon').length == 0){
|
|
|
|
|
ImageGrid.createRibbon(direction)
|
|
|
|
|
}
|
2012-08-14 20:13:36 +04:00
|
|
|
|
2012-08-19 21:23:29 +04:00
|
|
|
// get previous element after which we need to put the current...
|
|
|
|
|
var prev_elem = getImageBefore(
|
2012-09-24 00:44:52 +04:00
|
|
|
$('.current.image').attr('id'),
|
2012-09-07 13:48:47 +04:00
|
|
|
$('.current.ribbon')[direction]('.ribbon'))
|
2012-08-14 20:13:36 +04:00
|
|
|
|
2012-08-19 21:23:29 +04:00
|
|
|
// last image in ribbon, merge...
|
|
|
|
|
if($('.current.ribbon').children('.image').length == 1){
|
|
|
|
|
ImageGrid.mergeRibbons(direction)
|
|
|
|
|
} else {
|
|
|
|
|
img = $('.current.image')
|
2012-09-07 13:48:47 +04:00
|
|
|
// XXX how will this behave if we are at the last image in ribbon???
|
|
|
|
|
if((ImageGrid.option.LAST_MOVE_DIRECTION == 'prev' && img.prev('.image').length != 0)
|
|
|
|
|
|| (ImageGrid.option.LAST_MOVE_DIRECTION == 'next' && img.next('.image').length == 0)){
|
2012-08-19 21:23:29 +04:00
|
|
|
ImageGrid.prevImage()
|
|
|
|
|
} else {
|
|
|
|
|
ImageGrid.nextImage()
|
|
|
|
|
}
|
|
|
|
|
// do the actual move...
|
|
|
|
|
if(prev_elem){
|
|
|
|
|
// insert element after current...
|
|
|
|
|
img
|
|
|
|
|
.detach()
|
|
|
|
|
.insertAfter(prev_elem)
|
|
|
|
|
} else {
|
|
|
|
|
// empty ribbon or fisrt element...
|
|
|
|
|
img
|
|
|
|
|
.detach()
|
|
|
|
|
.prependTo($('.current.ribbon')[direction]('.ribbon'))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
$('.current.image').click()
|
|
|
|
|
}),
|
2012-08-19 21:34:31 +04:00
|
|
|
// shift image...
|
2012-08-19 21:23:29 +04:00
|
|
|
ImageGrid.ACTION({ title: 'Shift image up', },
|
|
|
|
|
function shiftImageUp(){ return ImageGrid.shiftImage('prev') }),
|
|
|
|
|
ImageGrid.ACTION({ title: 'Shift image down', },
|
|
|
|
|
function shiftImageDown(){ return ImageGrid.shiftImage('next') }),
|
2012-08-19 21:34:31 +04:00
|
|
|
|
|
|
|
|
// shift image to new ribbon...
|
2012-08-19 21:23:29 +04:00
|
|
|
ImageGrid.ACTION({
|
2012-08-19 21:34:31 +04:00
|
|
|
title: 'Shift image up to new ribbon',
|
2012-08-19 21:23:29 +04:00
|
|
|
},
|
2012-08-19 21:34:31 +04:00
|
|
|
function shiftImageUpNewRibbon(){
|
|
|
|
|
ImageGrid.createRibbon('prev')
|
|
|
|
|
ImageGrid.shiftImageUp()
|
2012-08-19 21:23:29 +04:00
|
|
|
}),
|
2012-08-19 21:34:31 +04:00
|
|
|
ImageGrid.ACTION({
|
|
|
|
|
title: 'Shift image down to new ribbon',
|
|
|
|
|
},
|
|
|
|
|
function shiftImageDownNewRibbon(){
|
|
|
|
|
ImageGrid.createRibbon('next')
|
|
|
|
|
ImageGrid.shiftImageDown()
|
|
|
|
|
}),
|
|
|
|
|
|
|
|
|
|
// sorting...
|
2012-08-19 21:23:29 +04:00
|
|
|
ImageGrid.ACTION({
|
2012-08-20 14:21:01 +04:00
|
|
|
title: 'Sort images via criteria',
|
|
|
|
|
doc: 'Use the cmp function to update image id\'s and resort.',
|
2012-08-19 21:34:31 +04:00
|
|
|
display: false,
|
2012-08-19 21:23:29 +04:00
|
|
|
},
|
|
|
|
|
function sortImagesVia(cmp){
|
2012-09-05 19:35:47 +04:00
|
|
|
$('.ribbon').sortChildren(cmp)
|
|
|
|
|
updateRibbonImages($('.current.image').click())
|
2012-08-19 21:23:29 +04:00
|
|
|
}),
|
2012-08-19 21:34:31 +04:00
|
|
|
ImageGrid.ACTION({
|
2012-08-20 14:21:01 +04:00
|
|
|
title: 'Sort images',
|
|
|
|
|
doc: 'Sort images in all ribbons\n\n'+
|
|
|
|
|
'NOTE: this will only realign three ribbons.'
|
2012-08-19 21:34:31 +04:00
|
|
|
},
|
|
|
|
|
function sortImages(){
|
2012-09-24 00:44:52 +04:00
|
|
|
ImageGrid.sortImagesVia(cmpImageOrder)
|
2012-08-19 21:34:31 +04:00
|
|
|
}),
|
2012-08-19 21:23:29 +04:00
|
|
|
ImageGrid.ACTION({
|
2012-08-20 14:21:01 +04:00
|
|
|
title: 'Reverse order of images',
|
|
|
|
|
doc: 'this will reverse image order in all ribbons.',
|
2012-08-19 21:23:29 +04:00
|
|
|
},
|
|
|
|
|
function reverseImageOrder(){
|
|
|
|
|
// this is done by reversing their id attr
|
2012-09-24 00:44:52 +04:00
|
|
|
ImageGrid.sortImagesVia(function(a, b){return cmpImageOrder(b, a)})
|
|
|
|
|
}),
|
|
|
|
|
ImageGrid.ACTION({
|
|
|
|
|
title: 'Sort images by ID',
|
|
|
|
|
},
|
|
|
|
|
function sortImagesById(){
|
|
|
|
|
getImageOrder = getImageId
|
|
|
|
|
ImageGrid.sortImages()
|
|
|
|
|
}),
|
|
|
|
|
ImageGrid.ACTION({
|
|
|
|
|
title: 'Sort images by date',
|
|
|
|
|
},
|
|
|
|
|
function sortImagesByDate(){
|
|
|
|
|
getImageOrder = getImageDate
|
|
|
|
|
ImageGrid.sortImages()
|
2012-08-19 21:23:29 +04:00
|
|
|
}),
|
|
|
|
|
ImageGrid.ACTION({
|
|
|
|
|
title: 'Sort images by their full path',
|
|
|
|
|
},
|
|
|
|
|
// XXX this should use a normalized path...
|
|
|
|
|
function sortImagesByPath(){
|
2012-09-24 00:44:52 +04:00
|
|
|
getImageOrder = getImagePath
|
|
|
|
|
ImageGrid.sortImages()
|
2012-08-19 21:23:29 +04:00
|
|
|
}))
|
2012-08-14 20:12:30 +04:00
|
|
|
|
|
|
|
|
|
2012-08-14 18:08:45 +04:00
|
|
|
// XXX group images in ribbon and merge down/up
|
|
|
|
|
//
|
|
|
|
|
// grouping will make the images in a ribbon adjacent to each
|
|
|
|
|
// other...
|
|
|
|
|
//
|
|
|
|
|
// the group's position will be the same as current images i.e.
|
|
|
|
|
// between the below/above two images...
|
|
|
|
|
|
|
|
|
|
// XXX shift group/image right/left...
|
|
|
|
|
|
2012-08-14 17:26:46 +04:00
|
|
|
|
2012-08-07 21:12:22 +04:00
|
|
|
|
2012-08-15 04:03:11 +04:00
|
|
|
|
2012-07-14 00:00:16 +04:00
|
|
|
/*********************************************************************/
|
2012-06-08 18:30:54 +04:00
|
|
|
// vim:set ts=4 sw=4 nowrap :
|