mirror of
https://github.com/flynx/ImageGrid.git
synced 2025-10-30 02:40:08 +00:00
more refactoring, rearanged sort-related functions and re-written image/gid distance related code...
Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
parent
d2a065b09a
commit
1bc2fb64f8
463
ui/data.js
463
ui/data.js
@ -172,119 +172,6 @@ function concatZip(){
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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, 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 comparison from gid...
|
|
||||||
//
|
|
||||||
// XXX make this faster...
|
|
||||||
// XXX this is fun, but do we actually need this?
|
|
||||||
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 imageDateCmp(a, b, get, data){
|
|
||||||
data = data == null ? IMAGES : data
|
|
||||||
if(get != null){
|
|
||||||
a = get(a)
|
|
||||||
b = get(b)
|
|
||||||
}
|
|
||||||
return data[b].ctime - data[a].ctime
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// NOTE: this expects gids...
|
|
||||||
function imageNameCmp(a, b, get, data){
|
|
||||||
data = data == null ? IMAGES : data
|
|
||||||
if(get != null){
|
|
||||||
a = get(a)
|
|
||||||
b = get(b)
|
|
||||||
}
|
|
||||||
a = data[a].path.split('/').pop()
|
|
||||||
b = data[b].path.split('/').pop()
|
|
||||||
if(a == b){
|
|
||||||
return 0
|
|
||||||
} else if(a < b){
|
|
||||||
return -1
|
|
||||||
} else {
|
|
||||||
return +1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Get the first sequence of numbers in the file name...
|
// Get the first sequence of numbers in the file name...
|
||||||
function getImageNameSeq(gid, data){
|
function getImageNameSeq(gid, data){
|
||||||
data = data == null ? IMAGES : data
|
data = data == null ? IMAGES : data
|
||||||
@ -293,6 +180,7 @@ function getImageNameSeq(gid, data){
|
|||||||
return r == null ? n : parseInt(r[1])
|
return r == null ? n : parseInt(r[1])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Get the first sequence of numbers in the file name but only if it is
|
// Get the first sequence of numbers in the file name but only if it is
|
||||||
// at the filename start...
|
// at the filename start...
|
||||||
function getImageNameLeadingSeq(gid, data){
|
function getImageNameLeadingSeq(gid, data){
|
||||||
@ -303,62 +191,69 @@ function getImageNameLeadingSeq(gid, data){
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Compare images by sequence number (in filename) or by filename
|
function getGIDDistance(a, b, get, data){
|
||||||
//
|
data = data == null ? DATA : data
|
||||||
// Examples:
|
var order = data.order
|
||||||
// "1 file name", "012-file", "file 123 name", "DSC_1234"
|
|
||||||
//
|
|
||||||
// NOTE: if there are more than one sequence numbers in a filename then
|
|
||||||
// only the first is considered.
|
|
||||||
// NOTE: images with sequence number always precede images with plain
|
|
||||||
// filenames...
|
|
||||||
function imageSeqOrNameCmp(a, b, get, data, get_seq){
|
|
||||||
data = data == null ? IMAGES : data
|
|
||||||
get_seq = get_seq == null ? getImageNameSeq : get_seq
|
|
||||||
if(get != null){
|
if(get != null){
|
||||||
a = get(a)
|
a = get(a)
|
||||||
b = get(b)
|
b = get(b)
|
||||||
}
|
}
|
||||||
|
a = order.indexOf(a)
|
||||||
|
b = order.indexOf(b)
|
||||||
|
return Math.abs(a - b)
|
||||||
|
}
|
||||||
|
|
||||||
var aa = get_seq(a, data)
|
|
||||||
var bb = get_seq(b, data)
|
|
||||||
|
|
||||||
// special case: seq, name
|
// NOTE: this is a constructor to cache the generated index as it is
|
||||||
if(typeof(aa) == typeof(123) && typeof(bb) == typeof('str')){ return -1 }
|
// quite slow to construct, but needs to be current...
|
||||||
// special case: name, seq
|
function makeGIDRibbonDistanceGetter(gid, data){
|
||||||
if(typeof(aa) == typeof('str') && typeof(bb) == typeof(123)){ return +1 }
|
data = data == null ? DATA : data
|
||||||
|
|
||||||
// get the names if there are no sequence numbers...
|
// make a cmp index...
|
||||||
// NOTE: at this point both a and b are either numbers or NaN's...
|
var ribbons = $.map(DATA.ribbons, function(r, i){
|
||||||
a = isNaN(aa) ? data[a].path.split('/').pop() : aa
|
// sort each ribbon by distance from closest gid...
|
||||||
b = isNaN(bb) ? data[b].path.split('/').pop() : bb
|
//return [r.slice().sort(makeGIDDistanceCmp(getGIDBefore(gid, i)))]
|
||||||
|
return [r.slice().sort(makeGIDDistanceCmp(gid))]
|
||||||
|
})
|
||||||
|
var gids = $.map(ribbons, function(e){ return [e[0]] })
|
||||||
|
var ri = gids.indexOf(gid)
|
||||||
|
|
||||||
// do the actual comparison
|
// the basic calculator...
|
||||||
if(a == b){
|
return function(gid){
|
||||||
return 0
|
var r = ribbons[getGIDRibbonIndex(gid, {ribbons: ribbons})]
|
||||||
} else if(a < b){
|
var x = r.indexOf(gid)
|
||||||
return -1
|
var y = Math.abs(gids.indexOf(r[0]) - ri)
|
||||||
} else {
|
|
||||||
return +1
|
// calculate real distance...
|
||||||
|
return Math.sqrt(x*x + y*y)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort images XP-style
|
|
||||||
|
// Get distance between two gids taking into account ribbons...
|
||||||
//
|
//
|
||||||
// This will consider sequence numbers if they are at the start of the
|
// This is essentially a 2D distance between two gids in data.
|
||||||
// filename.
|
|
||||||
//
|
|
||||||
// Examples:
|
|
||||||
// "1 file name", "012-file"
|
|
||||||
//
|
//
|
||||||
// NOTE: images with sequence number always precede images with plain
|
// NOTE: to get lots of distances from a specific image use
|
||||||
// filenames...
|
// makeGIDDistanceCmp(...) for faster results...
|
||||||
function imageXPStyleFileNameCmp(a, b, get, data){
|
function getGIDRibbonDistance(a, b, data){
|
||||||
return imageSeqOrNameCmp(a, b, get, data, getImageNameLeadingSeq)
|
return makeDistanceFromGIDGetter(a, data)(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function cmp(a, b, get){
|
||||||
|
if(get == null){
|
||||||
|
return a - b
|
||||||
|
}
|
||||||
|
return get(a) - get(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Generic ordering via DATA.order
|
||||||
|
//
|
||||||
// NOTE: this expects gids...
|
// NOTE: this expects gids...
|
||||||
|
// NOTE: this is not in sort.js because it is a generic base sort method
|
||||||
function imageOrderCmp(a, b, get, data){
|
function imageOrderCmp(a, b, get, data){
|
||||||
data = data == null ? DATA : data
|
data = data == null ? DATA : data
|
||||||
if(get != null){
|
if(get != null){
|
||||||
@ -482,128 +377,22 @@ Array.prototype.binSearch = function(target, cmp, get){
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Orientation translation...
|
|
||||||
function orientationExif2ImageGrid(orientation){
|
|
||||||
return {
|
|
||||||
orientation: {
|
|
||||||
0: 0,
|
|
||||||
1: 0,
|
|
||||||
2: 0,
|
|
||||||
3: 180,
|
|
||||||
4: 0,
|
|
||||||
5: 90,
|
|
||||||
6: 90,
|
|
||||||
7: 90,
|
|
||||||
8: 270,
|
|
||||||
}[orientation],
|
|
||||||
flipped: {
|
|
||||||
0: null,
|
|
||||||
1: null,
|
|
||||||
2: ['horizontal'],
|
|
||||||
3: null,
|
|
||||||
4: ['vertical'],
|
|
||||||
5: ['vertical'],
|
|
||||||
6: null,
|
|
||||||
7: ['horizontal'],
|
|
||||||
8: null,
|
|
||||||
}[orientation]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Base URL interface...
|
|
||||||
//
|
|
||||||
// NOTE: changing a base URL will trigger a baseURLChanged event...
|
|
||||||
function setBaseURL(url){
|
|
||||||
var old_url = BASE_URL
|
|
||||||
url = url.replace(/\/*$/, '/')
|
|
||||||
BASE_URL = url
|
|
||||||
$('.viewer').trigger('baseURLChanged', [old_url, url])
|
|
||||||
return url
|
|
||||||
}
|
|
||||||
function getBaseURL(){
|
|
||||||
return BASE_URL
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Normalize the path...
|
|
||||||
//
|
|
||||||
// This will:
|
|
||||||
// - convert windows absolute paths 'X:\...' -> 'file:///X:/...'
|
|
||||||
// - if mode is 'absolute':
|
|
||||||
// - return absolute paths as-is
|
|
||||||
// - base relative paths on base/BASE_URL, returning an absolute
|
|
||||||
// path
|
|
||||||
// - if mode is relative:
|
|
||||||
// - if absolute path is based on base/BASE_URL make a relative
|
|
||||||
// to base path out of it buy cutting the base out.
|
|
||||||
// - return absolute paths as-is
|
|
||||||
// - return relative paths as-is
|
|
||||||
//
|
|
||||||
// NOTE: mode can be either 'absolute' (default) or 'relative'...
|
|
||||||
function normalizePath(url, base, mode){
|
|
||||||
base = base == null ? getBaseURL() : base
|
|
||||||
//mode = /^\./.test(base) && mode == null ? 'relative' : null
|
|
||||||
mode = mode == null ? 'absolute' : mode
|
|
||||||
|
|
||||||
res = ''
|
|
||||||
|
|
||||||
// windows path...
|
|
||||||
// - replace all '\\' with '/'...
|
|
||||||
url = url.replace(/\\/g, '/')
|
|
||||||
// - replace 'X:/...' with 'file:///X:/...'
|
|
||||||
if(/^[A-Z]:\//.test(url)){
|
|
||||||
url = 'file:///' + url
|
|
||||||
}
|
|
||||||
|
|
||||||
// we got absolute path...
|
|
||||||
if(/^(file|http|https):\/\/.*$/.test(url)){
|
|
||||||
// check if we start with base, and remove it if so...
|
|
||||||
if(mode == 'relative' && url.substring(0, base.length) == base){
|
|
||||||
url = url.substring(base.length - 1)
|
|
||||||
res = url[0] == '/' ? url.substring(1) : url
|
|
||||||
|
|
||||||
// if it's a different path, return as-is
|
|
||||||
} else if(mode == 'absolute'){
|
|
||||||
res = url
|
|
||||||
}
|
|
||||||
|
|
||||||
// make an absolute path...
|
|
||||||
} else if(mode == 'absolute') {
|
|
||||||
// if base ends and url starts with '.' avoid making it a '..'
|
|
||||||
if(base[base.length-1] == '.' && url[0] == '.'){
|
|
||||||
res = base + url.substring(1)
|
|
||||||
// avoid creating '//'...
|
|
||||||
} else if(base[base.length-1] != '/' && url[0] != '/'){
|
|
||||||
res = base + '/' + url
|
|
||||||
} else {
|
|
||||||
res = base + url
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// get the actual path...
|
|
||||||
res = res.replace('${CACHE_DIR}', CACHE_DIR)
|
|
||||||
|
|
||||||
// XXX legacy support...
|
|
||||||
res = res.replace('.ImageGridCache', CACHE_DIR)
|
|
||||||
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// like getRibbonIndex but get the index only via DATA...
|
// like getRibbonIndex but get the index only via DATA...
|
||||||
function getDataRibbonIndex(gid, data){
|
function getGIDRibbonIndex(gid, data){
|
||||||
gid = gid == null ? getImageGID() : gid
|
gid = gid == null ? getImageGID() : gid
|
||||||
data = data == null ? DATA : data
|
data = data == null ? DATA : data
|
||||||
|
|
||||||
for(var i=0; i < data.ribbons.length; i++){
|
var ribbons = data.ribbons
|
||||||
if(data.ribbons[i].indexOf(gid) >= 0){
|
|
||||||
|
for(var i=0; i < ribbons.length; i++){
|
||||||
|
if(ribbons[i].indexOf(gid) >= 0){
|
||||||
return i
|
return i
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Same as getImageBefore(...), but uses gids and searches in DATA...
|
// Same as getImageBefore(...), but uses gids and searches in DATA...
|
||||||
//
|
//
|
||||||
// NOTE: this uses it's own predicate...
|
// NOTE: this uses it's own predicate...
|
||||||
@ -612,7 +401,7 @@ function getGIDBefore(gid, ribbon, search, data){
|
|||||||
data = data == null ? DATA : data
|
data = data == null ? DATA : data
|
||||||
// XXX get a ribbon without getting into DOM...
|
// XXX get a ribbon without getting into DOM...
|
||||||
// ...dependency leek...
|
// ...dependency leek...
|
||||||
ribbon = ribbon == null ? getDataRibbonIndex(gid, data) : ribbon
|
ribbon = ribbon == null ? getGIDRibbonIndex(gid, data) : ribbon
|
||||||
search = search == null ? binSearch : search
|
search = search == null ? binSearch : search
|
||||||
//search = search == null ? match2(linSearch, binSearch) : search
|
//search = search == null ? match2(linSearch, binSearch) : search
|
||||||
ribbon = data.ribbons[ribbon]
|
ribbon = data.ribbons[ribbon]
|
||||||
@ -698,6 +487,86 @@ function getImageGIDs(from, count, ribbon, inclusive){
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Base URL interface...
|
||||||
|
//
|
||||||
|
// NOTE: changing a base URL will trigger a baseURLChanged event...
|
||||||
|
function getBaseURL(){
|
||||||
|
return BASE_URL
|
||||||
|
}
|
||||||
|
function setBaseURL(url){
|
||||||
|
var old_url = BASE_URL
|
||||||
|
url = url.replace(/\/*$/, '/')
|
||||||
|
BASE_URL = url
|
||||||
|
$('.viewer').trigger('baseURLChanged', [old_url, url])
|
||||||
|
return url
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Normalize the path...
|
||||||
|
//
|
||||||
|
// This will:
|
||||||
|
// - convert windows absolute paths 'X:\...' -> 'file:///X:/...'
|
||||||
|
// - if mode is 'absolute':
|
||||||
|
// - return absolute paths as-is
|
||||||
|
// - base relative paths on base/BASE_URL, returning an absolute
|
||||||
|
// path
|
||||||
|
// - if mode is relative:
|
||||||
|
// - if absolute path is based on base/BASE_URL make a relative
|
||||||
|
// to base path out of it buy cutting the base out.
|
||||||
|
// - return absolute paths as-is
|
||||||
|
// - return relative paths as-is
|
||||||
|
//
|
||||||
|
// NOTE: mode can be either 'absolute' (default) or 'relative'...
|
||||||
|
function normalizePath(url, base, mode){
|
||||||
|
base = base == null ? getBaseURL() : base
|
||||||
|
//mode = /^\./.test(base) && mode == null ? 'relative' : null
|
||||||
|
mode = mode == null ? 'absolute' : mode
|
||||||
|
|
||||||
|
res = ''
|
||||||
|
|
||||||
|
// windows path...
|
||||||
|
// - replace all '\\' with '/'...
|
||||||
|
url = url.replace(/\\/g, '/')
|
||||||
|
// - replace 'X:/...' with 'file:///X:/...'
|
||||||
|
if(/^[A-Z]:\//.test(url)){
|
||||||
|
url = 'file:///' + url
|
||||||
|
}
|
||||||
|
|
||||||
|
// we got absolute path...
|
||||||
|
if(/^(file|http|https):\/\/.*$/.test(url)){
|
||||||
|
// check if we start with base, and remove it if so...
|
||||||
|
if(mode == 'relative' && url.substring(0, base.length) == base){
|
||||||
|
url = url.substring(base.length - 1)
|
||||||
|
res = url[0] == '/' ? url.substring(1) : url
|
||||||
|
|
||||||
|
// if it's a different path, return as-is
|
||||||
|
} else if(mode == 'absolute'){
|
||||||
|
res = url
|
||||||
|
}
|
||||||
|
|
||||||
|
// make an absolute path...
|
||||||
|
} else if(mode == 'absolute') {
|
||||||
|
// if base ends and url starts with '.' avoid making it a '..'
|
||||||
|
if(base[base.length-1] == '.' && url[0] == '.'){
|
||||||
|
res = base + url.substring(1)
|
||||||
|
// avoid creating '//'...
|
||||||
|
} else if(base[base.length-1] != '/' && url[0] != '/'){
|
||||||
|
res = base + '/' + url
|
||||||
|
} else {
|
||||||
|
res = base + url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the actual path...
|
||||||
|
res = res.replace('${CACHE_DIR}', CACHE_DIR)
|
||||||
|
|
||||||
|
// XXX legacy support...
|
||||||
|
res = res.replace('.ImageGridCache', CACHE_DIR)
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Select best preview by size...
|
// Select best preview by size...
|
||||||
//
|
//
|
||||||
// NOTE: this will use the original if everything else is smaller...
|
// NOTE: this will use the original if everything else is smaller...
|
||||||
@ -725,27 +594,32 @@ function getBestPreview(gid, size){
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Resort the ribbons by DATA.order and re-render...
|
// Orientation translation...
|
||||||
//
|
function orientationExif2ImageGrid(orientation){
|
||||||
// NOTE: due to how the format is structured, to sort the images one
|
return {
|
||||||
// only needs to sort DATA.order and call this.
|
orientation: {
|
||||||
function updateRibbonOrder(no_reload_viewer){
|
0: 0,
|
||||||
for(var i=0; i < DATA.ribbons.length; i++){
|
1: 0,
|
||||||
DATA.ribbons[i].sort(imageOrderCmp)
|
2: 0,
|
||||||
|
3: 180,
|
||||||
|
4: 0,
|
||||||
|
5: 90,
|
||||||
|
6: 90,
|
||||||
|
7: 90,
|
||||||
|
8: 270,
|
||||||
|
}[orientation],
|
||||||
|
flipped: {
|
||||||
|
0: null,
|
||||||
|
1: null,
|
||||||
|
2: ['horizontal'],
|
||||||
|
3: null,
|
||||||
|
4: ['vertical'],
|
||||||
|
5: ['vertical'],
|
||||||
|
6: null,
|
||||||
|
7: ['horizontal'],
|
||||||
|
8: null,
|
||||||
|
}[orientation]
|
||||||
}
|
}
|
||||||
if(!no_reload_viewer){
|
|
||||||
reloadViewer()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// get list of gids sorted by proximity to current gid
|
|
||||||
//
|
|
||||||
// NOTE: the distance used is the actual 2D distance...
|
|
||||||
function getClosestGIDs(gid){
|
|
||||||
gid = gid == null ? getImageGID() : gid
|
|
||||||
//return DATA.order.slice().sort(makeImageGIDDistanceCmp(gid))
|
|
||||||
return DATA.order.slice().sort(makeImageRibbonDistanceCmp(gid))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1276,9 +1150,9 @@ function updateImages(size, cmp){
|
|||||||
// sorted run...
|
// sorted run...
|
||||||
if(UPDATE_SORT_ENABLED && cmp != false){
|
if(UPDATE_SORT_ENABLED && cmp != false){
|
||||||
cmp = cmp == null ?
|
cmp = cmp == null ?
|
||||||
makeImageGIDDistanceCmp(getImageGID(), getImageGID)
|
makeGIDDistanceCmp(getImageGID(), getImageGID)
|
||||||
// XXX this is more correct but is slow...
|
// XXX this is more correct but is slow...
|
||||||
//makeImageRibbonDistanceCmp(getImageGID(), getImageGID)
|
//makeGIDRibbonDistanceCmp(getImageGID(), getImageGID)
|
||||||
: cmp
|
: cmp
|
||||||
deferred.resolve($('.image')
|
deferred.resolve($('.image')
|
||||||
// sort images by distance from current, so as to update what
|
// sort images by distance from current, so as to update what
|
||||||
@ -1588,6 +1462,21 @@ function getPrevLocation(){
|
|||||||
* Actions...
|
* Actions...
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// Sort the ribbons by DATA.order and re-render...
|
||||||
|
//
|
||||||
|
// NOTE: due to how the format is structured, to sort the images one
|
||||||
|
// only needs to sort DATA.order and call this.
|
||||||
|
function updateRibbonOrder(no_reload_viewer){
|
||||||
|
for(var i=0; i < DATA.ribbons.length; i++){
|
||||||
|
DATA.ribbons[i].sort(imageOrderCmp)
|
||||||
|
}
|
||||||
|
if(!no_reload_viewer){
|
||||||
|
reloadViewer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/******************************************************* Extension ***/
|
/******************************************************* Extension ***/
|
||||||
|
|
||||||
// Open image in an external editor/viewer
|
// Open image in an external editor/viewer
|
||||||
|
|||||||
154
ui/sort.js
154
ui/sort.js
@ -12,7 +12,159 @@ var OVERFLOW_GAP = PROXIMITY * 5
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
/********************************************************* Sorting ***/
|
/**********************************************************************
|
||||||
|
* Helpers
|
||||||
|
*/
|
||||||
|
|
||||||
|
// create a generic distance comparison function
|
||||||
|
//
|
||||||
|
// NOTE: both distances are measured from a fixed point (start)...
|
||||||
|
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.
|
||||||
|
//
|
||||||
|
// NOTE: this calculates the distance in a flat sequence...
|
||||||
|
function makeGIDDistanceCmp(gid, get, data){
|
||||||
|
return function(a, b){
|
||||||
|
return getGIDDistance(gid, a, get, data) - getGIDDistance(gid, b, get, data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 2D distance from a specified gid comparison...
|
||||||
|
//
|
||||||
|
// XXX make this faster...
|
||||||
|
// XXX this is fun, but do we actually need this?
|
||||||
|
function makeGIDRibbonDistanceCmp(gid, get, data){
|
||||||
|
data = data == null ? DATA : data
|
||||||
|
|
||||||
|
var _getDistance = makeGIDRibbonDistanceGetter(gid)
|
||||||
|
|
||||||
|
if(get == null){
|
||||||
|
return function(a, b){
|
||||||
|
return _getDistance(a) - _getDistance(b)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return function(a, b){
|
||||||
|
return _getDistance(get(a)) - _getDistance(get(b))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// NOTE: this expects gids...
|
||||||
|
function imageDateCmp(a, b, get, data){
|
||||||
|
data = data == null ? IMAGES : data
|
||||||
|
if(get != null){
|
||||||
|
a = get(a)
|
||||||
|
b = get(b)
|
||||||
|
}
|
||||||
|
return data[b].ctime - data[a].ctime
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// NOTE: this expects gids...
|
||||||
|
function imageNameCmp(a, b, get, data){
|
||||||
|
data = data == null ? IMAGES : data
|
||||||
|
if(get != null){
|
||||||
|
a = get(a)
|
||||||
|
b = get(b)
|
||||||
|
}
|
||||||
|
a = data[a].path.split('/').pop()
|
||||||
|
b = data[b].path.split('/').pop()
|
||||||
|
if(a == b){
|
||||||
|
return 0
|
||||||
|
} else if(a < b){
|
||||||
|
return -1
|
||||||
|
} else {
|
||||||
|
return +1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Compare images by sequence number (in filename) or by filename
|
||||||
|
//
|
||||||
|
// Examples:
|
||||||
|
// "1 file name", "012-file", "file 123 name", "DSC_1234"
|
||||||
|
//
|
||||||
|
// NOTE: if there are more than one sequence numbers in a filename then
|
||||||
|
// only the first is considered.
|
||||||
|
// NOTE: images with sequence number always precede images with plain
|
||||||
|
// filenames...
|
||||||
|
function imageSeqOrNameCmp(a, b, get, data, get_seq){
|
||||||
|
data = data == null ? IMAGES : data
|
||||||
|
get_seq = get_seq == null ? getImageNameSeq : get_seq
|
||||||
|
if(get != null){
|
||||||
|
a = get(a)
|
||||||
|
b = get(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
var aa = get_seq(a, data)
|
||||||
|
var bb = get_seq(b, data)
|
||||||
|
|
||||||
|
// special case: seq, name
|
||||||
|
if(typeof(aa) == typeof(123) && typeof(bb) == typeof('str')){ return -1 }
|
||||||
|
// special case: name, seq
|
||||||
|
if(typeof(aa) == typeof('str') && typeof(bb) == typeof(123)){ return +1 }
|
||||||
|
|
||||||
|
// get the names if there are no sequence numbers...
|
||||||
|
// NOTE: at this point both a and b are either numbers or NaN's...
|
||||||
|
a = isNaN(aa) ? data[a].path.split('/').pop() : aa
|
||||||
|
b = isNaN(bb) ? data[b].path.split('/').pop() : bb
|
||||||
|
|
||||||
|
// do the actual comparison
|
||||||
|
if(a == b){
|
||||||
|
return 0
|
||||||
|
} else if(a < b){
|
||||||
|
return -1
|
||||||
|
} else {
|
||||||
|
return +1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Compate images by name XP-style
|
||||||
|
//
|
||||||
|
// This will consider sequence numbers if they are at the start of the
|
||||||
|
// filename.
|
||||||
|
//
|
||||||
|
// Examples:
|
||||||
|
// "1 file name", "012-file"
|
||||||
|
//
|
||||||
|
// NOTE: images with sequence number always precede images with plain
|
||||||
|
// filenames...
|
||||||
|
function imageXPStyleFileNameCmp(a, b, get, data){
|
||||||
|
return imageSeqOrNameCmp(a, b, get, data, getImageNameLeadingSeq)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Get list of gids sorted by proximity to current gid
|
||||||
|
//
|
||||||
|
// NOTE: the distance used is the actual 2D distance...
|
||||||
|
function getClosestGIDs(gid){
|
||||||
|
gid = gid == null ? getImageGID() : gid
|
||||||
|
//return DATA.order.slice().sort(makeGIDDistanceCmp(gid))
|
||||||
|
return DATA.order.slice().sort(makeGIDRibbonDistanceCmp(gid))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**********************************************************************
|
||||||
|
* Actions
|
||||||
|
*/
|
||||||
|
|
||||||
function reverseImageOrder(){
|
function reverseImageOrder(){
|
||||||
DATA.order.reverse()
|
DATA.order.reverse()
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user