some refactoring + some work on making updateImages(...) non-blocking...

Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
Alex A. Naanou 2013-05-31 20:10:23 +04:00
parent 5580540649
commit 0e7c96e703
4 changed files with 217 additions and 51 deletions

View File

@ -5,14 +5,14 @@ Roadmap
[X] ribbon next/prev [X] ribbon next/prev
[X] screen next/prev [X] screen next/prev
[_] marks next/prev [_] marks next/prev
[_] 28% editing [_] 26% editing
[_] 0% ribbon [_] 0% ribbon
[_] merge up/down [_] merge up/down
[_] flatten [_] flatten
[_] sort [_] sort
[_] 66% image [_] 57% image
[X] shift up/down [X] shift up/down
shift left/right [_] shift left/right
[X] mark/unmark (selection) [X] mark/unmark (selection)
[X] rotate left/right [X] rotate left/right
[_] crop [_] crop

View File

@ -7,15 +7,13 @@
//var DEBUG = DEBUG != null ? DEBUG : true //var DEBUG = DEBUG != null ? DEBUG : true
var DATA_ATTR = 'DATA'
var LOAD_SCREENS = 6 var LOAD_SCREENS = 6
//var LOAD_THRESHOLD = 2
var DEFAULT_SCREEN_IMAGES = 4 var DEFAULT_SCREEN_IMAGES = 4
var MAX_SCREEN_IMAGES = 12 var MAX_SCREEN_IMAGES = 12
// if set to true each image will have basic info written to its html
var IMAGE_INFO = false
var CACHE_DIR = '.ImageGridCache' var CACHE_DIR = '.ImageGridCache'
// A stub image, also here for documentation... // A stub image, also here for documentation...
@ -59,12 +57,8 @@ var DATA = {
var IMAGES = {} var IMAGES = {}
var IMAGES_UPDATED = [] var IMAGES_UPDATED = []
var DATA_ATTR = 'DATA'
var MARKED = [] var MARKED = []
var IMAGE_CACHE = []
var SETTINGS = { var SETTINGS = {
'theme': null, 'theme': null,
'screen-images-ribbon-mode': null, 'screen-images-ribbon-mode': null,
@ -75,24 +69,127 @@ var SETTINGS = {
var BASE_URL = '.' var BASE_URL = '.'
var IMAGE_CACHE = []
/********************************************************************** /**********************************************************************
* Helpers * Helpers
*/ */
// NOTE: this expects gids... function makeDistanceCmp(start, get){
function imageDateCmp(a, b, data){ if(get == null){
data = data == null ? IMAGES : data return function(a, b){
return data[b].ctime - data[a].ctime return Math.abs(start - a) - Math.abs(start - b)
}
} else {
start = get(start)
return function(a, b){
return Math.abs(start - get(a)) - Math.abs(start - get(b))
}
}
}
// Make a cmp function to compare two gids by distance from gid.
/*
function makeImageGIDDistanceCmp(gid, order){
order = order == null ? DATA.order : order
var i = order.indexOf(gid)
return function(a, b){
return (Math.abs(i - order.indexOf(a))
- Math.abs(i - order.indexOf(b)))
}
}
*/
function makeImageGIDDistanceCmp(gid, get, order){
order = order == null ? DATA.order : order
return makeDistanceCmp(gid, get == null ?
function(a){
return order.indexOf(a)
}
: function(a){
return order.indexOf(get(a))
})
}
// NOTE: essentially this is a 2D distance compatison from gid...
//
// XXX make this faster...
function makeImageRibbonDistanceCmp(gid, get, data, images){
data = data == null ? DATA : data
images = images == null ? IMAGES : images
// make a cmp index...
var ribbons = $.map(DATA.ribbons, function(r, i){
// sort each ribbon by distance from closest gid...
//return [r.slice().sort(makeImageGIDDistanceCmp(getGIDBefore(gid, i)))]
return [r.slice().sort(makeImageGIDDistanceCmp(gid))]
})
var gids = $.map(ribbons, function(e){ return [e[0]] })
var ri = gids.indexOf(gid)
function _getRibbon(gid){
for(var i=0; i < ribbons.length; i++){
if(ribbons[i].indexOf(gid) >= 0){
return ribbons[i]
}
}
}
function _getDistance(a){
var r = _getRibbon(a)
var x = r.indexOf(a)
var y = Math.abs(gids.indexOf(r[0]) - ri)
// NOTE: this is cheating...
//return x + y
// calculate real distance...
return Math.sqrt(x*x + y*y)
}
if(get == null){
return function(a, b){
return _getDistance(a) - _getDistance(b)
}
} else {
return function(a, b){
return _getDistance(get(a)) - _getDistance(get(b))
}
}
}
function cmp(a, b, get){
if(get == null){
return a - b
}
return get(a) - get(b)
} }
// NOTE: this expects gids... // NOTE: this expects gids...
function imageNameCmp(a, b, data){ function imageDateCmp(a, b, get, data){
data = data == null ? IMAGES : data data = data == null ? IMAGES : data
a = data[b].path.split('/').pop() if(get == null){
b = data[a].path.split('/').pop() return data[b].ctime - data[a].ctime
} else {
return data[get(b)].ctime - data[get(a)].ctime
}
}
// NOTE: this expects gids...
function imageNameCmp(a, b, get, data){
data = data == null ? IMAGES : data
if(get == null){
a = data[b].path.split('/').pop()
b = data[a].path.split('/').pop()
} else {
a = data[get(b)].path.split('/').pop()
b = data[get(a)].path.split('/').pop()
}
if(a == b){ if(a == b){
return 0 return 0
} else if(a < b){ } else if(a < b){
@ -102,14 +199,18 @@ function imageNameCmp(a, b, data){
} }
} }
function imageOrderCmp(a, b, data){
// NOTE: this expects gids...
function imageOrderCmp(a, b, get, data){
data = data == null ? DATA : data data = data == null ? DATA : data
return data.order.indexOf(a) - data.order.indexOf(b) if(get == null){
return data.order.indexOf(a) - data.order.indexOf(b)
} else {
return data.order.indexOf(get(a)) - data.order.indexOf(get(b))
}
} }
// Check if a is at position i in lst // Check if a is at position i in lst
// //
// This will return: // This will return:
@ -117,10 +218,10 @@ function imageOrderCmp(a, b, data){
// - -1 if a is less than position i // - -1 if a is less than position i
// - +1 if a is greater than position i // - +1 if a is greater than position i
// //
// NOTE: the signature is different from the traditional cmp(a, b) so as // NOTE: the signature is different from the traditional lcmp(a, b) so as
// to enable more complex comparisons involving adjacent elements // to enable more complex comparisons involving adjacent elements
// (see isBetween(...) for an example) // (see isBetween(...) for an example)
function cmp(a, i, lst){ function lcmp(a, i, lst){
var b = lst[i] var b = lst[i]
if(a == b){ if(a == b){
return 0 return 0
@ -167,7 +268,7 @@ function isBetween(a, i, lst){
// //
// NOTE: this is here for testing reasons only... // NOTE: this is here for testing reasons only...
function linSearch(target, lst, check, return_position){ function linSearch(target, lst, check, return_position){
check = check == null ? cmp : check check = check == null ? lcmp : check
for(var i=0; i < lst.length; i++){ for(var i=0; i < lst.length; i++){
if(check(target, i, lst) == 0){ if(check(target, i, lst) == 0){
@ -189,7 +290,7 @@ Array.prototype.linSearch = function(target, cmp){
// return_position to true. // return_position to true.
// NOTE: by default this will use cmp as a predicate. // NOTE: by default this will use cmp as a predicate.
function binSearch(target, lst, check, return_position){ function binSearch(target, lst, check, return_position){
check = check == null ? cmp : check check = check == null ? lcmp : check
var h = 0 var h = 0
var t = lst.length - 1 var t = lst.length - 1
var m, res var m, res
@ -313,7 +414,7 @@ function getGIDBefore(gid, ribbon, search){
} }
// Get a "count" of GIDs starting with a given gid ("from") // Get "count" of GIDs starting with a given gid ("from")
// //
// NOTE: this will not include the 'from' GID in the resulting list, // NOTE: this will not include the 'from' GID in the resulting list,
// unless inclusive is set to true. // unless inclusive is set to true.
@ -577,12 +678,51 @@ function updateImage(image, gid, size){
} }
var UPDATE_SORT_ENABLED = false
var UPDATE_SYNC = true
// Same as updateImage(...) but will update all images. // Same as updateImage(...) but will update all images.
function updateImages(size){ //
size = size == null ? getVisibleImageSize('max') : size // NOTE: this will prioritize images by distance from current image...
return $('.image').each(function(){ //
updateImage($(this), null, size) // XXX need to run this in the background...
}) function updateImages(size, cmp){
var deferred = $.Deferred()
function _worker(){
size = size == null ? getVisibleImageSize('max') : size
// sorted run...
if(UPDATE_SORT_ENABLED && cmp != false){
cmp = cmp == null ?
makeImageGIDDistanceCmp(getImageGID(), getImageGID)
// XXX this is more correct but is slow...
//makeImageRibbonDistanceCmp(getImageGID(), getImageGID)
: cmp
deferred.resolve($('.image')
// sort images by distance from current, so as to update what
// the user is looking at first...
.sort(cmp)
.each(function(){
updateImage($(this), null, size)
}))
// do a fast run w.o. sorting images...
} else {
deferred.resolve($('.image')
.each(function(){
updateImage($(this), null, size)
}))
}
}
if(UPDATE_SYNC){
_worker()
} else {
setTimeout(_worker, 0)
}
return deferred
} }
@ -781,6 +921,7 @@ function loadSettings(){
*/ */
// NOTE: this will always overwrite the previous cache set for a ribbon... // NOTE: this will always overwrite the previous cache set for a ribbon...
// XXX sort images in cache by closeness to current image...
function preCacheRibbonImages(ribbon){ function preCacheRibbonImages(ribbon){
var i = getRibbonIndex(ribbon) var i = getRibbonIndex(ribbon)
var size = getVisibleImageSize('max') var size = getVisibleImageSize('max')
@ -793,6 +934,8 @@ function preCacheRibbonImages(ribbon){
var gids = getImageGIDs(first, -cache_frame_size) var gids = getImageGIDs(first, -cache_frame_size)
.concat(getImageGIDs(last, cache_frame_size)) .concat(getImageGIDs(last, cache_frame_size))
// XXX sort gids...
var cache = [] var cache = []
IMAGE_CACHE[i] = cache IMAGE_CACHE[i] = cache
$.each(gids, function(i, e){ $.each(gids, function(i, e){
@ -1381,7 +1524,6 @@ function setupDataBindings(viewer){
// NOTE: if this is greater than the number of images currently // NOTE: if this is greater than the number of images currently
// loaded, it might lead to odd effects... // loaded, it might lead to odd effects...
var frame_size = Math.ceil((screen_size * LOAD_SCREENS) / 2) var frame_size = Math.ceil((screen_size * LOAD_SCREENS) / 2)
//var threshold = Math.ceil(screen_size * LOAD_THRESHOLD)
var threshold = Math.floor(frame_size / 2) var threshold = Math.floor(frame_size / 2)
threshold = threshold < 1 ? 1 : threshold threshold = threshold < 1 ? 1 : threshold
@ -1465,7 +1607,6 @@ function setupDataBindings(viewer){
} }
// update previews... // update previews...
// XXX make this update only what needs updating...
updateImages() updateImages()
}) })
@ -1473,26 +1614,23 @@ function setupDataBindings(viewer){
.on('focusingImage', function(evt, image){ .on('focusingImage', function(evt, image){
image = $(image) image = $(image)
DATA.current = getImageGID(image) DATA.current = getImageGID(image)
updateGlobalImageInfo(image)
}) })
// basic image manipulation... // basic image manipulation...
// XXX after this we need to save the images...
.on('rotatingLeft rotatingRight', function(evt, image){ .on('rotatingLeft rotatingRight', function(evt, image){
$(image).each(function(i, e){ $(image).each(function(i, e){
var img = $(this) var img = $(this)
var gid = getImageGID(img) var gid = getImageGID(img)
var orientation = img.attr('orientation') var orientation = img.attr('orientation')
// change the image orientation status and add to
// updated list...
IMAGES[gid].orientation = orientation IMAGES[gid].orientation = orientation
if(IMAGES_UPDATED.indexOf(gid) == -1){ if(IMAGES_UPDATED.indexOf(gid) == -1){
IMAGES_UPDATED.push(gid) IMAGES_UPDATED.push(gid)
} }
}) })
updateGlobalImageInfo(image)
}) })
@ -1509,8 +1647,6 @@ function setupDataBindings(viewer){
} else { } else {
MARKED.splice(MARKED.indexOf(gid), 1) MARKED.splice(MARKED.indexOf(gid), 1)
} }
updateGlobalImageInfo(img)
}) })
.on('removeingRibbonMarks', function(evt, ribbon){ .on('removeingRibbonMarks', function(evt, ribbon){
$.each(DATA.ribbons[getRibbonIndex(ribbon)], function(_, e){ $.each(DATA.ribbons[getRibbonIndex(ribbon)], function(_, e){
@ -1554,10 +1690,31 @@ function setupDataBindings(viewer){
preCacheRibbonImages(ribbon) preCacheRibbonImages(ribbon)
}) })
// info...
.on([
'focusingImage',
'rotatingLeft',
'rotateingRight',
'togglingMark'
].join(' '),
function(evt, image){
updateGlobalImageInfo($(image))
})
.on([
'removeingAllMarks',
'removeingRibbonMarks',
'markingAll',
'markingRibbon',
'invertingMarks'
].join(' '),
function(){
updateGlobalImageInfo()
})
} }
/********************************************************************** /**********************************************************************
* vim:set ts=4 sw=4 spell : */ * vim:set ts=4 sw=4 spell : */

View File

@ -8,6 +8,8 @@ body {
margin: 0px; margin: 0px;
} }
/********************************************************** Viewer ***/ /********************************************************** Viewer ***/
.viewer { .viewer {
position: relative; position: relative;
@ -26,6 +28,7 @@ body {
} }
/****************************************************** Ribbon set ***/ /****************************************************** Ribbon set ***/
.ribbon-set { .ribbon-set {
position: absolute; position: absolute;
@ -50,6 +53,7 @@ body {
} }
/********************************************************** Ribbon ***/ /********************************************************** Ribbon ***/
.ribbon { .ribbon {
position: relative; position: relative;
@ -345,6 +349,8 @@ body {
background: red; background: red;
} }
/*************************************************** Global status ***/ /*************************************************** Global status ***/
.global-status { .global-status {
display: block; display: block;

View File

@ -117,17 +117,18 @@ function removeImageMarks(mode){
// remove marks from current ribbon (default)... // remove marks from current ribbon (default)...
if(mode == 'ribbon' || mode == null){ if(mode == 'ribbon' || mode == null){
var ribbon = getRibbon() var ribbon = getRibbon()
$('.viewer').trigger('removeingRibbonMarks', [ribbon]) var res = ribbon
return ribbon
.find('.marked') .find('.marked')
.removeClass('marked') .removeClass('marked')
$('.viewer').trigger('removeingRibbonMarks', [ribbon])
// remove all marks... // remove all marks...
} else if(mode == 'all'){ } else if(mode == 'all'){
$('.viewer').trigger('removeingAllMarks') var res = $('.marked')
return $('.marked')
.removeClass('marked') .removeClass('marked')
$('.viewer').trigger('removeingAllMarks')
} }
return res
} }
@ -135,25 +136,27 @@ function markAll(mode){
// remove marks from current ribbon (default)... // remove marks from current ribbon (default)...
if(mode == 'ribbon' || mode == null){ if(mode == 'ribbon' || mode == null){
var ribbon = getRibbon() var ribbon = getRibbon()
$('.viewer').trigger('markingRibbon', [ribbon]) var res = ribbon
return ribbon
.find('.image:not(.marked)') .find('.image:not(.marked)')
.addClass('marked') .addClass('marked')
$('.viewer').trigger('markingRibbon', [ribbon])
} else if(mode == 'all'){ } else if(mode == 'all'){
var res = $('.image:not(.marked)').addClass('marked')
$('.viewer').trigger('markingAll') $('.viewer').trigger('markingAll')
return $('.image:not(.marked)').addClass('marked')
} }
return res
} }
// NOTE: this only does it's work in the current ribbon... // NOTE: this only does it's work in the current ribbon...
function invertImageMarks(){ function invertImageMarks(){
var ribbon = getRibbon() var ribbon = getRibbon()
$('.viewer').trigger('invertingMarks', [ribbon]) var res = ribbon
return ribbon
.find('.image') .find('.image')
.toggleClass('marked') .toggleClass('marked')
$('.viewer').trigger('invertingMarks', [ribbon])
return res
} }