lots of work on file loading and saving, now almost up to spec, still needs refactoring...

Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
Alex A. Naanou 2013-05-28 02:17:24 +04:00
parent 2999d4bcab
commit 8d446609fd
5 changed files with 453 additions and 274 deletions

View File

@ -23,8 +23,11 @@
[_] BUG: changing window size in single image modes messes things up...
| until we cycle to ribbon mode and back...
[_] ASAP: load/view un-cached directories...
[_] show only one ribbon mode
| should this have up/down navigation?
[_] slideshow mode...
[_] import fav dirs (wo. index)...
[_] image sorting (reverse/date/name/...)
[_] add ability to save/load ranges of images and the structures around them
| e.g.load image 100 to current ribbon -> will load 100 images
| for current ribbon and all the in between images from other

View File

@ -57,10 +57,8 @@ var DATA = {
// the images object, this is indexed by image GID and contains all
// the needed data...
// XXX should we split this out?
var IMAGES = {}
// True if images is modified and needs saving...
var IMAGES_DIRTY = false
var IMAGES_UPDATED = []
var DATA_ATTR = 'DATA'
@ -252,15 +250,17 @@ function getGIDBefore(gid, ribbon, search){
}
// Get a "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.
// NOTE: count can be either negative or positive, this will indicate
// load direction...
// NOTE: this will not include the 'from' GID in the resulting list...
// NOTE: this can calculate the ribbon number if an image can be only
// in one ribbon...
// NOTE: this can calculate the ribbon number where the image is located.
// NOTE: if an image can be in more than one ribbon, one MUST suply the
// correct ribbon number...
//
// XXX do we need more checking???
// XXX inclusive can not be false, only null or true...
function getImageGIDs(from, count, ribbon, inclusive){
if(count == 0){
return []
@ -292,8 +292,6 @@ function getImageGIDs(from, count, ribbon, inclusive){
// Select best preview by size...
//
// NOTE: this will use the original if everything else is smaller...
//
// XXX make this both relative and absolute URL compatible...
function getBestPreview(gid, size){
size = size == null ? getVisibleImageSize('max') : size
var s
@ -311,7 +309,6 @@ function getBestPreview(gid, size){
}
}
return {
//url: url,
url: normalizePath(url),
size: preview_size
}
@ -320,7 +317,7 @@ function getBestPreview(gid, size){
// Normalize the path...
//
// This will do:
// This will:
// - convert windows absolute paths 'X:\...' -> 'file:///X:/...'
// - if mode is 'absolute':
// - return absolute paths as-is
@ -333,8 +330,6 @@ function getBestPreview(gid, size){
// - return relative paths as-is
//
// NOTE: mode can be either 'absolute' (default) or 'relative'...
//
// XXX need to account for '.' base
function normalizePath(url, base, mode){
mode = mode == null ? 'absolute' : mode
base = base == null ? BASE_URL : base
@ -347,7 +342,7 @@ function normalizePath(url, base, mode){
url = 'file:///' + url
}
// absolute path...
// 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){
@ -375,6 +370,56 @@ function normalizePath(url, base, mode){
/**********************************************************************
* Format conversion
*/
// Convert legacy Gen1 data format to current Gen3 (v2.0)
function convertDataGen1(data, cmp){
var res = {
data: {
version: '2.0',
current: null,
ribbons: [],
order: [],
},
images: {}
}
cmp = cmp == null ?
function(a, b){
return imageDateCmp(a, b, res.images)
}
: cmp
var ribbons = res.data.ribbons
var order = res.data.order
var images = res.images
// position...
res.data.current = data.position
// ribbons and images...
$.each(data.ribbons, function(i, input_images){
var ribbon = []
ribbons.push(ribbon)
for(var id in input_images){
var image = input_images[id]
ribbon.push(id)
order.push(id)
images[id] = image
}
ribbon.sort(cmp)
})
order.sort(cmp)
// XXX STUB
res.data.current = order[0]
return res
}
/**********************************************************************
* Loaders
*/
@ -535,6 +580,8 @@ function loadImagesAround(ref_gid, count, ribbon){
}
// Roll ribbon and load new images in the updated section.
//
// NOTE: this is signature-compatible with rollRibbon...
// NOTE: this will load data ONLY if it is available, otherwise this
// will have no effect...
@ -604,50 +651,6 @@ function loadData(images_per_screen){
}
function convertDataGen1(data, cmp){
var res = {
data: {
version: '2.0',
current: null,
ribbons: [],
order: [],
},
images: {}
}
cmp = cmp == null ?
function(a, b){
return imageDateCmp(a, b, res.images)
}
: cmp
var ribbons = res.data.ribbons
var order = res.data.order
var images = res.images
// position...
res.data.current = data.position
// ribbons and images...
$.each(data.ribbons, function(i, input_images){
var ribbon = []
ribbons.push(ribbon)
for(var id in input_images){
var image = input_images[id]
ribbon.push(id)
order.push(id)
images[id] = image
}
ribbon.sort(cmp)
})
order.sort(cmp)
// XXX STUB
res.data.current = order[0]
return res
}
function loadSettings(){
toggleTheme(SETTINGS['theme'])
@ -663,218 +666,6 @@ function loadSettings(){
/**********************************************************************
* localStorage
*
* XXX should we use jStorage here?
*/
function loadLocalStorageData(attr){
attr = attr == null ? DATA_ATTR : attr
var data = localStorage[attr]
if(data == null){
data = '{}'
}
return {
data: JSON.parse(data),
base_url: localStorage[attr + '_BASE_URL'],
}
}
function saveLocalStorageData(attr){
attr = attr == null ? DATA_ATTR : attr
localStorage[attr] = JSON.stringify(DATA)
localStorage[attr + '_BASE_URL'] = BASE_URL
}
function loadLocalStorageImages(attr){
attr = attr == null ? DATA_ATTR : attr
attr += '_IMAGES'
var images = localStorage[attr]
if(images == null){
images = '{}'
}
return JSON.parse(images)
}
function saveLocalStorageImages(attr){
attr = attr == null ? DATA_ATTR : attr
attr += '_IMAGES'
localStorage[attr] = JSON.stringify(IMAGES)
}
// generic save/load...
function loadLocalStorage(attr){
attr = attr == null ? DATA_ATTR : attr
var d = loadLocalStorageData(attr)
BASE_URL = d.base_url
DATA = d.data
IMAGES = loadLocalStorageImages(attr)
return loadData()
}
function saveLocalStorage(attr){
attr = attr == null ? DATA_ATTR : attr
saveLocalStorageData(attr)
saveLocalStorageImages(attr)
}
function loadLocalStorageMarks(attr){
attr = attr == null ? DATA_ATTR : attr
attr += '_MARKED'
var marked = localStorage[attr]
if(marked == null){
marked = '[]'
}
MARKED = JSON.parse(marked)
return loadData()
}
function saveLocalStorageMarks(attr){
attr = attr == null ? DATA_ATTR : attr
attr += '_MARKED'
localStorage[attr] = JSON.stringify(MARKED)
}
function loadLocalStorageSettings(attr){
attr = attr == null ? DATA_ATTR : attr
attr += '_SETTINGS'
SETTINGS = JSON.parse(localStorage[attr])
loadSettings()
}
function saveLocalStorageSettings(attr){
attr = attr == null ? DATA_ATTR : attr
attr += '_SETTINGS'
localStorage[attr] = JSON.stringify(SETTINGS)
}
/**********************************************************************
* Extension API (CEF/PhoneGap/...)
*/
function loadFileImages(path, callback){
return $.getJSON(path)
.done(function(json){
IMAGES = json
localStorage[DATA_ATTR + '_IMAGES_FILE'] = path
console.log('Loaded IMAGES...')
callback != null && callback()
})
.fail(function(){
console.error('ERROR LOADING:', path)
})
}
function loadFile(data_path, image_path, callback){
var base = data_path.split(CACHE_DIR)[0]
base = base == data_path ? '.' : base
// CEF
return $.getJSON(data_path)
.done(function(json){
BASE_URL = base
// legacy format...
if(json.version == null){
json = convertDataGen1(json)
DATA = json.data
IMAGES = json.images
// XXX load marked data...
MARKED = []
loadData()
// version 2.0
// XXX needs a more flexible protocol...
} else if(json.version == '2.0') {
DATA = json
if(image_path != null){
loadFileImages(normalizePath(image_path, base))
.done(function(){
loadData()
callback != null && callback()
})
} else if(DATA.image_file != null) {
loadFileImages(normalizePath(DATA.image_file, base))
.done(function(){
loadData()
callback != null && callback()
})
}
// unknown format...
} else {
console.error('unknown format.')
return
}
})
.fail(function(){
console.error('ERROR LOADING:', data_path)
})
}
function saveFile(name){
// CEF
if(window.CEF_dumpJSON != null){
if(DATA.image_file == null){
DATA.image_file = name + '-images.json'
}
//CEF_dumpJSON(DATA.image_file, IMAGES)
// XXX this will overwrite the images...
//CEF_dumpJSON(name + '-images.json', IMAGES)
//DATA.image_file = name + '-images.json'
CEF_dumpJSON(name + '-data.json', DATA)
CEF_dumpJSON(name + '-marked.json', MARKED)
// PhoneGap
} else if(false) {
// XXX
}
}
function openImage(){
// CEF
if(window.CEF_runSystem != null){
// XXX if path is not present try and open the biggest preview...
return CEF_runSystem(normalizePath(IMAGES[getImageGID()].path, BASE_URL))
// PhoneGap
} else if(false) {
// XXX
}
}
// XXX need revision...
function loadDir(path){
return loadFile(BASE_URL +'/data.json')
.fail(function(){
loadFile(BASE_URL +'/'+ CACHE_DIR +'/data.json')
.fail(function(){
// XXX load plain images...
// XXX
})
})
}
/**********************************************************************
* Image caching...
*/
@ -913,6 +704,358 @@ function preCacheAllRibbons(){
/**********************************************************************
* localStorage
*
* XXX should we use jStorage here?
*/
function loadLocalStorageData(attr){
attr = attr == null ? DATA_ATTR : attr
var data = localStorage[attr]
if(data == null){
data = '{}'
}
return {
data: JSON.parse(data),
base_url: localStorage[attr + '_BASE_URL'],
}
}
function saveLocalStorageData(attr){
attr = attr == null ? DATA_ATTR : attr
localStorage[attr] = JSON.stringify(DATA)
localStorage[attr + '_BASE_URL'] = BASE_URL
}
function loadLocalStorageImages(attr){
attr = attr == null ? DATA_ATTR : attr
attr += '_IMAGES'
var images = localStorage[attr]
if(images == null){
images = '{}'
}
return JSON.parse(images)
}
function saveLocalStorageImages(attr){
attr = attr == null ? DATA_ATTR : attr
attr += '_IMAGES'
localStorage[attr] = JSON.stringify(IMAGES)
}
function loadLocalStorageMarks(attr){
attr = attr == null ? DATA_ATTR : attr
attr += '_MARKED'
var marked = localStorage[attr]
if(marked == null){
marked = '[]'
}
MARKED = JSON.parse(marked)
return loadData()
}
function saveLocalStorageMarks(attr){
attr = attr == null ? DATA_ATTR : attr
attr += '_MARKED'
localStorage[attr] = JSON.stringify(MARKED)
}
function loadLocalStorageSettings(attr){
attr = attr == null ? DATA_ATTR : attr
attr += '_SETTINGS'
SETTINGS = JSON.parse(localStorage[attr])
loadSettings()
}
function saveLocalStorageSettings(attr){
attr = attr == null ? DATA_ATTR : attr
attr += '_SETTINGS'
localStorage[attr] = JSON.stringify(SETTINGS)
}
// generic save/load...
function loadLocalStorage(attr){
attr = attr == null ? DATA_ATTR : attr
var d = loadLocalStorageData(attr)
BASE_URL = d.base_url
DATA = d.data
IMAGES = loadLocalStorageImages(attr)
return loadData()
}
function saveLocalStorage(attr){
attr = attr == null ? DATA_ATTR : attr
saveLocalStorageData(attr)
saveLocalStorageImages(attr)
}
/**********************************************************************
* File storage (Extension API -- CEF/PhoneGap/...)
*
* XXX need to cleanup this section...
*/
// CEF
if(window.CEF_dumpJSON != null){
var dumpJSON = CEF_dumpJSON
var listDir = CEF_listDir
var removeFile = CEF_removeFile
var runSystem = CEF_runSystem
// PhoneGap
} else if(false) {
// XXX
var dumpJSON = null
var listDir = null
var removeFile = null
var runSystem = null
}
function loadFileImages(path, no_load_diffs, callback){
if(window.listDir == null){
no_load_diffs = true
}
if(path == null){
var base = normalizePath(CACHE_DIR)
var path = $.map(listDir(base), function(e){
return /.*-images.json$/.test(e) ? e : null
}).sort().reverse()[0]
path = path == null ? 'images.json' : path
console.log('Loading:', path)
path = base +'/'+ path
} else {
path = normalizePath(path)
// XXX need to account for paths without a CACHE_DIR
var base = path.split(CACHE_DIR)[0]
base += '/'+ CACHE_DIR
}
var diff_data = {}
var diff = true
// XXX what are we going to do if base == path, i.e. no cache dir???
// XXX no error handling if one of the diff loads fail...
if(!no_load_diffs){
var diffs = [diff_data]
var diffs_names = $.map(listDir(base), function(e){
return /.*-images-diff.json$/.test(e) ? e : null
}).sort()
diff = $.when.apply(null, $.map(diffs_names, function(e, i){
return $.getJSON(normalizePath(base +'/'+ e))
.done(function(data){
diffs[i+1] = data
console.log('Loaded:', e)
})
}))
.then(function(){
$.extend.apply(null, diffs)
})
}
return $.when(diff, $.getJSON(path))
.done(function(_, json){
json = json[0]
$.extend(json, diff_data)
IMAGES = json
//localStorage[DATA_ATTR + '_IMAGES_FILE'] = path
console.log('Loaded IMAGES...')
callback != null && callback()
})
.fail(function(){
console.error('ERROR LOADING:', path)
})
}
// XXX make this load a default data filename...
// XXX look into the CACHE_DIR if not explicitly given...
function loadFileState(data_path, image_path, callback){
var base = data_path.split(CACHE_DIR)[0]
base = base == data_path ? '.' : base
return $.getJSON(data_path)
.done(function(json){
BASE_URL = base
// legacy format...
if(json.version == null){
json = convertDataGen1(json)
DATA = json.data
IMAGES = json.images
// XXX load marked data...
MARKED = []
loadData()
// version 2.0
// XXX needs a more flexible protocol...
} else if(json.version == '2.0') {
DATA = json
if(image_path != null){
loadFileImages(normalizePath(image_path, base))
.done(function(){
loadData()
callback != null && callback()
})
} else if(DATA.image_file != null) {
loadFileImages(normalizePath(DATA.image_file, base))
.done(function(){
loadData()
callback != null && callback()
})
} else {
loadFileImages(null)
.done(function(){
loadData()
callback != null && callback()
})
}
// unknown format...
} else {
console.error('unknown format.')
return
}
})
.fail(function(){
console.error('ERROR LOADING:', data_path)
})
}
// Save current images list...
//
// NOTE: this will save the merged images and remove the diff files...
// NOTE: if an explicit name is given then this will not remove anything.
// NOTE: if not explicit name is given this will save to the current
// cache dir.
function saveFileImages(name){
var remove_diffs = (name == null)
name = name == null ? normalizePath(CACHE_DIR +'/'+ Date.timeStamp()) : name
// CEF
if(window.dumpJSON == null){
console.error('Can\'t save to file.')
return
}
// remove the diffs...
if(remove_diffs){
$.each($.map(listDir(normalizePath(CACHE_DIR)), function(e){
return /.*-images-diff.json$/.test(e) ? e : null
}), function(i, e){
console.log('removeing:', e)
removeFile(normalizePath(CACHE_DIR +'/'+ e))
})
IMAGES_UPDATED = []
}
dumpJSON(name + '-images.json', IMAGES)
//DATA.image_file = normalizePath(name + '-images.json', null, 'relative')
}
function saveFileState(name, no_normalize_path){
name = name == null ? Date.timeStamp() : name
if(!no_normalize_path){
name = normalizePath(CACHE_DIR +'/'+ name)
// write .image_file only if saving data to a non-cache dir...
// XXX check if this is correct...
} else {
if(DATA.image_file == null){
DATA.image_file = name + '-images.json'
}
}
dumpJSON(name + '-data.json', DATA)
dumpJSON(name + '-marked.json', MARKED)
// save the updated images...
if(IMAGES_UPDATED.length > 0){
var updated = {}
$.each(IMAGES_UPDATED, function(i, e){
updated[e] = IMAGES[e]
})
dumpJSON(name + '-images-diff.json', updated)
IMAGES_UPDATED = []
}
}
// Open image in an external editor/viewer
//
// NOTE: this will open the default editor/viewer.
function openImage(){
// CEF
if(window.runSystem == null){
console.error('Can\'t run external programs.')
return
}
// XXX if path is not present try and open the biggest preview...
return runSystem(normalizePath(IMAGES[getImageGID()].path, BASE_URL))
}
// XXX need revision...
function loadDir(path){
if(window.CEF_listDir != null){
var listDir = CEF_listDir
// PhoneGap
} else if(false) {
// XXX
} else {
no_load_diffs = true
}
path = normalizePath(path)
var files = listDir(path)
var data = $.map(files, function(e){
return /.*-data.json$/.test(e) ? e : null
}).sort().reverse()[0]
data = (data == null && files.indexOf('data.json') >= 0) ? 'data.json' : data
// look in the cache dir...
if(data == null){
path += '/' + CACHE_DIR
files = listDir(path)
data = $.map(listDir(path), function(e){
return /.*-data.json$/.test(e) ? e : null
}).sort().reverse()[0]
data = (data == null && files.indexOf('data.json') >= 0) ? 'data.json' : data
}
console.log('Loading:', data)
data = path + '/' + data
return loadFileState(data)
}
/**********************************************************************
* Setup
*/
@ -1070,7 +1213,9 @@ function setupDataBindings(viewer){
var orientation = img.attr('orientation')
IMAGES[gid].orientation = orientation
IMAGES_DIRTY = true
if(IMAGES_UPDATED.indexOf(gid) == -1){
IMAGES_UPDATED.push(gid)
}
})
})
@ -1137,4 +1282,4 @@ function setupDataBindings(viewer){
/**********************************************************************
* vim:set ts=4 sw=4 : */
* vim:set ts=4 sw=4 spell : */

View File

@ -458,8 +458,9 @@ $(function(){
//setElementOrigin($('.ribbon-set'), 'top', 'left')
// we have an image file...
if((DATA_ATTR + '_IMAGES_FILE') in localStorage){
var loading = loadFileImages(localStorage[DATA_ATTR + '_IMAGES_FILE'])
if((DATA_ATTR + '_BASE_URL') in localStorage){
BASE_URL = localStorage[DATA_ATTR + '_BASE_URL']
var loading = loadFileImages()
.done(function(){
var d = loadLocalStorageData()
DATA = d.data

View File

@ -212,6 +212,8 @@ var KEYBOARD_CONFIG = {
saveLocalStorageMarks()
saveLocalStorageSettings()
saveFileState()
})
},
Z: {

View File

@ -597,6 +597,34 @@ var cancelAnimationFrame = (window.cancelRequestAnimationFrame
|| clearTimeout)
Date.prototype.getTimeStamp = function(){
var y = this.getFullYear()
var M = this.getMonth()+1
M = M < 10 ? '0'+M : M
var D = this.getDate()
D = D < 10 ? '0'+D : D
var H = this.getHours()
H = H < 10 ? '0'+H : H
var m = this.getMinutes()
m = m < 10 ? '0'+m : m
return ''+y+M+D+H+m
}
Date.prototype.setTimeStamp = function(ts){
this.setFullYear(ts.slice(0, 4))
this.setMonth(ts.slice(4, 6)*1-1)
this.setDate(ts.slice(6, 8))
this.setHours(ts.slice(8, 10))
this.setMinutes(ts.slice(10, 12))
return this
}
Date.timeStamp = function(){
return (new Date()).getTimeStamp()
}
Date.fromTimeStamp = function(ts){
return (new Date()).setTimeStamp(ts)
}
/**********************************************************************