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] screen next/prev
[_] marks next/prev
[_] 28% editing
[_] 26% editing
[_] 0% ribbon
[_] merge up/down
[_] flatten
[_] sort
[_] 66% image
[_] 57% image
[X] shift up/down
shift left/right
[_] shift left/right
[X] mark/unmark (selection)
[X] rotate left/right
[_] crop

View File

@ -7,15 +7,13 @@
//var DEBUG = DEBUG != null ? DEBUG : true
var DATA_ATTR = 'DATA'
var LOAD_SCREENS = 6
//var LOAD_THRESHOLD = 2
var DEFAULT_SCREEN_IMAGES = 4
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'
// A stub image, also here for documentation...
@ -59,12 +57,8 @@ var DATA = {
var IMAGES = {}
var IMAGES_UPDATED = []
var DATA_ATTR = 'DATA'
var MARKED = []
var IMAGE_CACHE = []
var SETTINGS = {
'theme': null,
'screen-images-ribbon-mode': null,
@ -75,24 +69,127 @@ var SETTINGS = {
var BASE_URL = '.'
var IMAGE_CACHE = []
/**********************************************************************
* Helpers
*/
// NOTE: this expects gids...
function imageDateCmp(a, b, data){
data = data == null ? IMAGES : data
return data[b].ctime - data[a].ctime
function makeDistanceCmp(start, get){
if(get == null){
return function(a, b){
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...
function imageNameCmp(a, b, data){
function imageDateCmp(a, b, get, data){
data = data == null ? IMAGES : data
a = data[b].path.split('/').pop()
b = data[a].path.split('/').pop()
if(get == null){
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){
return 0
} 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
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
//
// This will return:
@ -117,10 +218,10 @@ function imageOrderCmp(a, b, data){
// - -1 if a is less 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
// (see isBetween(...) for an example)
function cmp(a, i, lst){
function lcmp(a, i, lst){
var b = lst[i]
if(a == b){
return 0
@ -167,7 +268,7 @@ function isBetween(a, i, lst){
//
// NOTE: this is here for testing reasons only...
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++){
if(check(target, i, lst) == 0){
@ -189,7 +290,7 @@ Array.prototype.linSearch = function(target, cmp){
// return_position to true.
// NOTE: by default this will use cmp as a predicate.
function binSearch(target, lst, check, return_position){
check = check == null ? cmp : check
check = check == null ? lcmp : check
var h = 0
var t = lst.length - 1
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,
// 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.
function updateImages(size){
size = size == null ? getVisibleImageSize('max') : size
return $('.image').each(function(){
updateImage($(this), null, size)
})
//
// NOTE: this will prioritize images by distance from current image...
//
// 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...
// XXX sort images in cache by closeness to current image...
function preCacheRibbonImages(ribbon){
var i = getRibbonIndex(ribbon)
var size = getVisibleImageSize('max')
@ -793,6 +934,8 @@ function preCacheRibbonImages(ribbon){
var gids = getImageGIDs(first, -cache_frame_size)
.concat(getImageGIDs(last, cache_frame_size))
// XXX sort gids...
var cache = []
IMAGE_CACHE[i] = cache
$.each(gids, function(i, e){
@ -1381,7 +1524,6 @@ function setupDataBindings(viewer){
// NOTE: if this is greater than the number of images currently
// loaded, it might lead to odd effects...
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)
threshold = threshold < 1 ? 1 : threshold
@ -1465,7 +1607,6 @@ function setupDataBindings(viewer){
}
// update previews...
// XXX make this update only what needs updating...
updateImages()
})
@ -1473,26 +1614,23 @@ function setupDataBindings(viewer){
.on('focusingImage', function(evt, image){
image = $(image)
DATA.current = getImageGID(image)
updateGlobalImageInfo(image)
})
// basic image manipulation...
// XXX after this we need to save the images...
.on('rotatingLeft rotatingRight', function(evt, image){
$(image).each(function(i, e){
var img = $(this)
var gid = getImageGID(img)
var orientation = img.attr('orientation')
// change the image orientation status and add to
// updated list...
IMAGES[gid].orientation = orientation
if(IMAGES_UPDATED.indexOf(gid) == -1){
IMAGES_UPDATED.push(gid)
}
})
updateGlobalImageInfo(image)
})
@ -1509,8 +1647,6 @@ function setupDataBindings(viewer){
} else {
MARKED.splice(MARKED.indexOf(gid), 1)
}
updateGlobalImageInfo(img)
})
.on('removeingRibbonMarks', function(evt, ribbon){
$.each(DATA.ribbons[getRibbonIndex(ribbon)], function(_, e){
@ -1554,10 +1690,31 @@ function setupDataBindings(viewer){
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;
}
/********************************************************** Viewer ***/
.viewer {
position: relative;
@ -26,6 +28,7 @@ body {
}
/****************************************************** Ribbon set ***/
.ribbon-set {
position: absolute;
@ -50,6 +53,7 @@ body {
}
/********************************************************** Ribbon ***/
.ribbon {
position: relative;
@ -345,6 +349,8 @@ body {
background: red;
}
/*************************************************** Global status ***/
.global-status {
display: block;

View File

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