diff --git a/ui/data.js b/ui/data.js
index 33aff077..20e67bdb 100755
--- a/ui/data.js
+++ b/ui/data.js
@@ -1092,578 +1092,6 @@ 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 = '{}'
- }
- var base = localStorage[attr + '_BASE_URL']
- base = base == null ? '.' : base
- return {
- data: JSON.parse(data),
- base_url: base,
- }
-}
-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 reloadViewer()
-}
-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 reloadViewer()
-}
-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...
-*/
-
-// load the target-specific handlers...
-// CEF
-if(window.CEF_dumpJSON != null){
- var dumpJSON = CEF_dumpJSON
- var listDir = CEF_listDir
- var removeFile = CEF_removeFile
- var runSystem = CEF_runSystem
-}
-
-
-
-/********************************************************* Helpers ***/
-
-// Report deferred progress
-//
-// This uses showStatus(...) and showErrorStatus(...) to report actions.
-//
-// Will use showErrorStatus(...) iff "Error" is the last argument of the
-// progress/notify action, removing it (string 'Error') from the arguments.
-//
-// Will return the original deferred.
-function statusNotify(prefix, loader){
- if(loader == null){
- loader = prefix
- prefix = null
- }
- return loader
- .progress(function(){
- var args = Array.apply(null, arguments)
- if(prefix != null && prefix != ''){
- args.splice(0, 0, prefix)
- }
- if(args.indexOf('Error') >= 0){
- args.pop()
- return showErrorStatus(args.join(': '))
- }
- return showStatus(args.join(': '))
- })
-}
-
-
-// Bubble up actions in the deferred chain
-//
-// Will chain progress/notify and if only_progress is not set, also
-// done/resolve and fail/reject from "from" to "to" deferred objects.
-//
-// Will add prefix to the list of arguments of progress/notify and
-// fail/reject (if not disabled), unless it is set to null.
-//
-// Will return "from" object.
-function bubbleProgress(prefix, from, to, only_progress){
- from
- .progress(function(){
- var args = Array.apply(null, arguments)
- prefix != null && args.splice(0, 0, prefix)
- to.notify.apply(to, args)
- })
-
- if(only_progress == null){
- from
- .done(function(){
- var args = Array.apply(null, arguments)
- to.resolve.apply(to, args)
- })
- .fail(function(){
- var args = Array.apply(null, arguments)
- prefix != null && args.splice(0, 0, prefix)
- to.reject.apply(to, args)
- })
- }
-
- return from
-}
-
-
-// Semi-generic deferred file loader
-//
-// if pattern is given, then search for the latest (ordered last) file
-// and load that.
-// else load the dfl file.
-//
-// if diff_pattern is given, then merge all matching files in order
-// (first to last) with the loaded "main" file
-//
-// NOTE: if neither of dfl, pattern or diff_pattern are given, then this
-// is essentially the same as $.getJSON(...)
-// NOTE: this needs listDir(...) to search for latest versions of files.
-function loadLatestFile(path, dfl, pattern, diff_pattern){
- dfl = dfl == null ? path.split(/[\/\\]/).pop() : dfl
- path = path == dfl ? '.' : path
-
- var res = $.Deferred()
-
- // can't find diffs if can't list dirs...
- if(window.listDir == null && (pattern != null || diff_pattern != null)){
- res.notify('Unsupported', 'directory listing.')
- return res.reject('listDir unsupported.')
- }
-
- // find the latest...
- if(pattern != null){
- pattern = RegExp(pattern)
- var file = $.map(listDir(path), function(e){
- return pattern.test(e) ? e : null
- }).sort().reverse()[0]
- }
- var file = file == null ? dfl : file
-
- var diff_data = {}
- var diff = true
-
- // collect and merge diffs...
- if(diff_pattern != null){
- diff_pattern = RegExp(diff_pattern)
- var diff_data = [diff_data]
- var diffs_names = $.map(listDir(path), function(e){
- return diff_pattern.test(e) ? e : null
- }).sort()
- diff = $.when.apply(null, $.map(diffs_names, function(e, i){
- return $.getJSON(path +'/'+ e)
- .done(function(data){
- diff_data[i+1] = data
- res.notify('Loaded', e)
- })
- .fail(function(){
- // XXX should we kill the load here???
- res.notify('Loading', e, 'Error')
- })
- }))
- // NOTE: .then(...) handlers get different signature args
- // depending on the number of arguments to .when(...)...
- .then(function(){
- $.extend.apply(null, diff_data)
- diff_data = diff_data[0]
- })
- }
-
- // load the main file and merge the diff with it...
- $.when(diff, $.getJSON(path +'/'+ file))
- .done(function(_, json){
- json = json[0]
-
- res.notify('Loaded', file)
-
- // merge diffs...
- if(Object.keys(diff_data).length != 0){
- $.extend(json, diff_data)
- res.notify('Merged')
- }
-
- res.resolve(json)
- })
- .fail(function(){
- res.notify('Loading', file, 'Error')
-
- return res.reject(file)
- })
-
- return res
-}
-
-
-
-/*********************************************************************/
-
-// Load images from file
-//
-// This will also merge all diff files.
-function loadFileImages(path, no_load_diffs){
- no_load_diffs = window.listDir == null ? true : no_load_diffs
-
- var res = $.Deferred()
-
- // default locations...
- if(path == null){
- var base = normalizePath(CACHE_DIR)
- var loader = loadLatestFile(base,
- IMAGES_FILE_DEFAULT,
- IMAGES_FILE_PATTERN,
- IMAGES_DIFF_FILE_PATTERN)
-
- // explicit path...
- // XXX need to account for paths without a CACHE_DIR
- } else {
- path = normalizePath(path)
- var base = path.split(CACHE_DIR)[0]
- base += '/'+ CACHE_DIR
-
- // XXX is this correct???
- var loader = loadLatestFile(base,
- path.split(base)[0],
- RegExp(path.split(base)[0]))
- }
-
- bubbleProgress('Images', loader, res)
-
- res.done(function(images){
- IMAGES = images
- })
-
- return res
-}
-
-
-// Save current images list to file
-//
-// If not name is given this will merge all the diffs and save a "clean"
-// (full) images.json file. Also removing the diff files.
-//
-// NOTE: if an explicit name is given then this will not remove anything.
-// NOTE: this will uses CACHE_DIR as the location if no name is given.
-function saveFileImages(name){
- var remove_diffs = (name == null)
- name = name == null ? normalizePath(CACHE_DIR +'/'+ Date.timeStamp()) : name
-
- if(window.dumpJSON == null){
- showErrorStatus('Can\'t save to file.')
- return
- }
-
- // remove the diffs...
- if(remove_diffs){
- $.each($.map(listDir(normalizePath(CACHE_DIR)), function(e){
- return IMAGES_DIFF_FILE_PATTERN.test(e) ? e : null
- }), function(i, e){
- showStatus('removeing:', e)
- removeFile(normalizePath(CACHE_DIR +'/'+ e))
- })
- IMAGES_UPDATED = []
- }
-
- // XXX use the pattern...
- dumpJSON(name + '-images.json', IMAGES)
- //DATA.image_file = normalizePath(name + '-images.json', null, 'relative')
-}
-
-
-// Load image marks form file
-function loadFileMarks(path){
- var res = $.Deferred()
- // default locations...
- if(path == null){
- var base = normalizePath(CACHE_DIR)
- var loader = loadLatestFile(base,
- MARKED_FILE_DEFAULT,
- MARKED_FILE_PATTERN)
-
- // explicit path...
- // XXX need to account for paths without a CACHE_DIR
- } else {
- path = normalizePath(path)
- var base = path.split(CACHE_DIR)[0]
- base += '/'+ CACHE_DIR
-
- // XXX is this correct???
- var loader = loadLatestFile(base,
- path.split(base)[0],
- RegExp(path.split(base)[0]))
- }
-
- bubbleProgress('Marks', loader, res)
-
- res.done(function(images){
- MARKED = images
- })
-
- return res
-}
-
-
-// Save image marks to file
-function saveFileMarks(name){
- name = name == null ? normalizePath(CACHE_DIR +'/'+ Date.timeStamp()) : name
-
- dumpJSON(name + '-marked.json', MARKED)
-}
-
-
-// Load images, ribbons and marks from cache
-//
-// XXX add support for explicit filenames...
-function loadFileState(path, prefix){
- prefix = prefix == null ? 'Data' : prefix
- prefix = prefix === false ? null : prefix
-
- // XXX explicit data file path...
- if(/\.json$/i.test(path)){
- // XXX at this
- var base = path.split(CACHE_DIR)[0]
- base = base == path ? '.' : base
- } else {
- var base = path.split(CACHE_DIR)[0]
- base = base == path ? '.' : base
- }
-
- var res = $.Deferred()
-
- bubbleProgress(prefix,
- loadLatestFile(path,
- DATA_FILE_DEFAULT,
- DATA_FILE_PATTERN), res, true)
- .done(function(json){
- BASE_URL = base
-
- // legacy format...
- if(json.version == null){
- json = convertDataGen1(json)
- DATA = json.data
- IMAGES = json.images
- MARKED = []
- reloadViewer()
- res.resolve()
-
- // version 2.0
- } else if(json.version == '2.0') {
- DATA = json
- $.when(
- // XXX load config...
- // load images...
- bubbleProgress(prefix,
- loadFileImages(DATA.image_file == null ?
- normalizePath(DATA.image_file, base)
- : null), res, true),
- // load marks if available...
- bubbleProgress(prefix,
- loadFileMarks(), res, true))
- .done(function(){
- reloadViewer()
- res.resolve()
- })
- // XXX fail???
-
- // unknown format...
- } else {
- res.reject('unknown format.')
- }
- })
- .fail(function(){
- res.reject('Loading', path, 'Error')
- })
-
- return res
-}
-
-
-// Save, ribbons and marks to cache
-//
-// NOTE: this will NOT save images, that operation must be explicitly
-// performed by saveFileImages(...)
-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)
- // XXX do we need to do this???
- saveFileMarks(name)
-
- // 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 = []
- }
-}
-
-
-// Load a directory as-is
-//
-// XXX check if we need to pass down sorting settings to the generators...
-function loadRawDir(path, prefix){
- prefix = prefix == null ? 'Data' : prefix
- prefix = prefix === false ? null : prefix
-
- var files = listDir(path)
-
- var res = $.Deferred()
-
- var image_paths = $.map(files, function(e){
- return IMAGE_PATTERN.test(e) ? e : null
- })
-
- if(image_paths.length == 0){
- // no images in path...
- res.notify(prefix, 'Load', path, 'Error')
- return res.reject()
- }
-
- BASE_URL = path
-
- IMAGES = imagesFromUrls(image_paths)
- res.notify(prefix, 'Loaded', 'Images.')
-
- DATA = dataFromImages(IMAGES)
- res.notify(prefix, 'Loaded', 'Data.')
-
- updateRibbonsFromFavDirs()
- res.notify(prefix, 'Loaded', 'Fav dirs.')
-
- MARKED = []
-
- reloadViewer()
-
- return res.resolve()
-}
-
-
-// Load a path
-//
-// This will try and do one of the following in order:
-// 1) look for a cache and load it,
-// 2) load data from within the directory
-// 3) load a directory as-is
-// load fav dirs
-//
-function loadDir(path, prefix){
- prefix = prefix == null ? 'Data' : prefix
- prefix = prefix === false ? null : prefix
-
- path = normalizePath(path)
- var orig_path = path
- var data
-
- var res = $.Deferred()
-
- res.notify(prefix, 'Loading', path)
-
- var files = listDir(path)
-
- if(files == null){
- //showErrorStatus('No files in path: ' + path)
- res.notify('load_error', path)
- return res.reject()
- }
-
- // see if there is a cache...
- if(files.indexOf(CACHE_DIR) >= 0){
- path = path +'/'+ CACHE_DIR
- }
-
- bubbleProgress(prefix,
- loadFileState(path, false), res, true)
- .done(function(){
- res.resolve()
- })
- .fail(function(){
- bubbleProgress('Raw directory', loadRawDir(orig_path), res)
- })
-
- return res
-}
-
-
-// Load ribbon structure from fav directory tree
-//
-// XXX loads duplicate images....
-function updateRibbonsFromFavDirs(){
- DATA.ribbons = ribbonsFromFavDirs(null, null, imageOrderCmp)
- sortImagesByDate()
- reloadViewer()
-}
-
-
-
/**********************************************************************
* Actions...
*/
@@ -1683,6 +1111,12 @@ function openImage(){
}
+// XXX
+function openImageWith(prog){
+ // XXX
+}
+
+
/********************************************************** Sorting **/
diff --git a/ui/files.js b/ui/files.js
new file mode 100755
index 00000000..bbb3dc2e
--- /dev/null
+++ b/ui/files.js
@@ -0,0 +1,493 @@
+/**********************************************************************
+*
+*
+*
+**********************************************************************/
+
+//var DEBUG = DEBUG != null ? DEBUG : true
+
+
+// load the target-specific handlers...
+// CEF
+if(window.CEF_dumpJSON != null){
+ var dumpJSON = CEF_dumpJSON
+ var listDir = CEF_listDir
+ var removeFile = CEF_removeFile
+ var runSystem = CEF_runSystem
+}
+
+
+/**********************************************************************
+* File storage (Extension API -- CEF/PhoneGap/...)
+*
+* XXX need to cleanup this section...
+*/
+
+
+/********************************************************* Helpers ***/
+
+// Report deferred progress
+//
+// This uses showStatus(...) and showErrorStatus(...) to report actions.
+//
+// Will use showErrorStatus(...) iff "Error" is the last argument of the
+// progress/notify action, removing it (string 'Error') from the arguments.
+//
+// Will return the original deferred.
+function statusNotify(prefix, loader){
+ if(loader == null){
+ loader = prefix
+ prefix = null
+ }
+ return loader
+ .progress(function(){
+ var args = Array.apply(null, arguments)
+ if(prefix != null && prefix != ''){
+ args.splice(0, 0, prefix)
+ }
+ if(args.indexOf('Error') >= 0){
+ args.pop()
+ return showErrorStatus(args.join(': '))
+ }
+ return showStatus(args.join(': '))
+ })
+}
+
+
+// Bubble up actions in the deferred chain
+//
+// Will chain progress/notify and if only_progress is not set, also
+// done/resolve and fail/reject from "from" to "to" deferred objects.
+//
+// Will add prefix to the list of arguments of progress/notify and
+// fail/reject (if not disabled), unless it is set to null.
+//
+// Will return "from" object.
+function bubbleProgress(prefix, from, to, only_progress){
+ from
+ .progress(function(){
+ var args = Array.apply(null, arguments)
+ prefix != null && args.splice(0, 0, prefix)
+ to.notify.apply(to, args)
+ })
+
+ if(only_progress == null){
+ from
+ .done(function(){
+ var args = Array.apply(null, arguments)
+ to.resolve.apply(to, args)
+ })
+ .fail(function(){
+ var args = Array.apply(null, arguments)
+ prefix != null && args.splice(0, 0, prefix)
+ to.reject.apply(to, args)
+ })
+ }
+
+ return from
+}
+
+
+// Semi-generic deferred file loader
+//
+// if pattern is given, then search for the latest (ordered last) file
+// and load that.
+// else load the dfl file.
+//
+// if diff_pattern is given, then merge all matching files in order
+// (first to last) with the loaded "main" file
+//
+// NOTE: if neither of dfl, pattern or diff_pattern are given, then this
+// is essentially the same as $.getJSON(...)
+// NOTE: this needs listDir(...) to search for latest versions of files.
+function loadLatestFile(path, dfl, pattern, diff_pattern){
+ dfl = dfl == null ? path.split(/[\/\\]/).pop() : dfl
+ path = path == dfl ? '.' : path
+
+ var res = $.Deferred()
+
+ // can't find diffs if can't list dirs...
+ if(window.listDir == null && (pattern != null || diff_pattern != null)){
+ res.notify('Unsupported', 'directory listing.')
+ return res.reject('listDir unsupported.')
+ }
+
+ // find the latest...
+ if(pattern != null){
+ pattern = RegExp(pattern)
+ var file = $.map(listDir(path), function(e){
+ return pattern.test(e) ? e : null
+ }).sort().reverse()[0]
+ }
+ var file = file == null ? dfl : file
+
+ var diff_data = {}
+ var diff = true
+
+ // collect and merge diffs...
+ if(diff_pattern != null){
+ diff_pattern = RegExp(diff_pattern)
+ var diff_data = [diff_data]
+ var diffs_names = $.map(listDir(path), function(e){
+ return diff_pattern.test(e) ? e : null
+ }).sort()
+ diff = $.when.apply(null, $.map(diffs_names, function(e, i){
+ return $.getJSON(path +'/'+ e)
+ .done(function(data){
+ diff_data[i+1] = data
+ res.notify('Loaded', e)
+ })
+ .fail(function(){
+ // XXX should we kill the load here???
+ res.notify('Loading', e, 'Error')
+ })
+ }))
+ // NOTE: .then(...) handlers get different signature args
+ // depending on the number of arguments to .when(...)...
+ .then(function(){
+ $.extend.apply(null, diff_data)
+ diff_data = diff_data[0]
+ })
+ }
+
+ // load the main file and merge the diff with it...
+ $.when(diff, $.getJSON(path +'/'+ file))
+ .done(function(_, json){
+ json = json[0]
+
+ res.notify('Loaded', file)
+
+ // merge diffs...
+ if(Object.keys(diff_data).length != 0){
+ $.extend(json, diff_data)
+ res.notify('Merged')
+ }
+
+ res.resolve(json)
+ })
+ .fail(function(){
+ res.notify('Loading', file, 'Error')
+
+ return res.reject(file)
+ })
+
+ return res
+}
+
+
+
+/*********************************************************************/
+
+// Load images from file
+//
+// This will also merge all diff files.
+function loadFileImages(path, no_load_diffs){
+ no_load_diffs = window.listDir == null ? true : no_load_diffs
+
+ var res = $.Deferred()
+
+ // default locations...
+ if(path == null){
+ var base = normalizePath(CACHE_DIR)
+ var loader = loadLatestFile(base,
+ IMAGES_FILE_DEFAULT,
+ IMAGES_FILE_PATTERN,
+ IMAGES_DIFF_FILE_PATTERN)
+
+ // explicit path...
+ // XXX need to account for paths without a CACHE_DIR
+ } else {
+ path = normalizePath(path)
+ var base = path.split(CACHE_DIR)[0]
+ base += '/'+ CACHE_DIR
+
+ // XXX is this correct???
+ var loader = loadLatestFile(base,
+ path.split(base)[0],
+ RegExp(path.split(base)[0]))
+ }
+
+ bubbleProgress('Images', loader, res)
+
+ res.done(function(images){
+ IMAGES = images
+ })
+
+ return res
+}
+
+
+// Save current images list to file
+//
+// If not name is given this will merge all the diffs and save a "clean"
+// (full) images.json file. Also removing the diff files.
+//
+// NOTE: if an explicit name is given then this will not remove anything.
+// NOTE: this will uses CACHE_DIR as the location if no name is given.
+function saveFileImages(name){
+ var remove_diffs = (name == null)
+ name = name == null ? normalizePath(CACHE_DIR +'/'+ Date.timeStamp()) : name
+
+ if(window.dumpJSON == null){
+ showErrorStatus('Can\'t save to file.')
+ return
+ }
+
+ // remove the diffs...
+ if(remove_diffs){
+ $.each($.map(listDir(normalizePath(CACHE_DIR)), function(e){
+ return IMAGES_DIFF_FILE_PATTERN.test(e) ? e : null
+ }), function(i, e){
+ showStatus('removeing:', e)
+ removeFile(normalizePath(CACHE_DIR +'/'+ e))
+ })
+ IMAGES_UPDATED = []
+ }
+
+ // XXX use the pattern...
+ dumpJSON(name + '-images.json', IMAGES)
+ //DATA.image_file = normalizePath(name + '-images.json', null, 'relative')
+}
+
+
+// Load image marks form file
+function loadFileMarks(path){
+ var res = $.Deferred()
+ // default locations...
+ if(path == null){
+ var base = normalizePath(CACHE_DIR)
+ var loader = loadLatestFile(base,
+ MARKED_FILE_DEFAULT,
+ MARKED_FILE_PATTERN)
+
+ // explicit path...
+ // XXX need to account for paths without a CACHE_DIR
+ } else {
+ path = normalizePath(path)
+ var base = path.split(CACHE_DIR)[0]
+ base += '/'+ CACHE_DIR
+
+ // XXX is this correct???
+ var loader = loadLatestFile(base,
+ path.split(base)[0],
+ RegExp(path.split(base)[0]))
+ }
+
+ bubbleProgress('Marks', loader, res)
+
+ res.done(function(images){
+ MARKED = images
+ })
+
+ return res
+}
+
+
+// Save image marks to file
+function saveFileMarks(name){
+ name = name == null ? normalizePath(CACHE_DIR +'/'+ Date.timeStamp()) : name
+
+ dumpJSON(name + '-marked.json', MARKED)
+}
+
+
+// Load images, ribbons and marks from cache
+//
+// XXX add support for explicit filenames...
+function loadFileState(path, prefix){
+ prefix = prefix == null ? 'Data' : prefix
+ prefix = prefix === false ? null : prefix
+
+ // XXX explicit data file path...
+ if(/\.json$/i.test(path)){
+ // XXX at this
+ var base = path.split(CACHE_DIR)[0]
+ base = base == path ? '.' : base
+ } else {
+ var base = path.split(CACHE_DIR)[0]
+ base = base == path ? '.' : base
+ }
+
+ var res = $.Deferred()
+
+ bubbleProgress(prefix,
+ loadLatestFile(path,
+ DATA_FILE_DEFAULT,
+ DATA_FILE_PATTERN), res, true)
+ .done(function(json){
+ BASE_URL = base
+
+ // legacy format...
+ if(json.version == null){
+ json = convertDataGen1(json)
+ DATA = json.data
+ IMAGES = json.images
+ MARKED = []
+ reloadViewer()
+ res.resolve()
+
+ // version 2.0
+ } else if(json.version == '2.0') {
+ DATA = json
+ $.when(
+ // XXX load config...
+ // load images...
+ bubbleProgress(prefix,
+ loadFileImages(DATA.image_file == null ?
+ normalizePath(DATA.image_file, base)
+ : null), res, true),
+ // load marks if available...
+ bubbleProgress(prefix,
+ loadFileMarks(), res, true))
+ .done(function(){
+ reloadViewer()
+ res.resolve()
+ })
+ // XXX fail???
+
+ // unknown format...
+ } else {
+ res.reject('unknown format.')
+ }
+ })
+ .fail(function(){
+ res.reject('Loading', path, 'Error')
+ })
+
+ return res
+}
+
+
+// Save, ribbons and marks to cache
+//
+// NOTE: this will NOT save images, that operation must be explicitly
+// performed by saveFileImages(...)
+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)
+ // XXX do we need to do this???
+ saveFileMarks(name)
+
+ // 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 = []
+ }
+}
+
+
+// Load a directory as-is
+//
+// XXX check if we need to pass down sorting settings to the generators...
+function loadRawDir(path, prefix){
+ prefix = prefix == null ? 'Data' : prefix
+ prefix = prefix === false ? null : prefix
+
+ var files = listDir(path)
+
+ var res = $.Deferred()
+
+ var image_paths = $.map(files, function(e){
+ return IMAGE_PATTERN.test(e) ? e : null
+ })
+
+ if(image_paths.length == 0){
+ // no images in path...
+ res.notify(prefix, 'Load', path, 'Error')
+ return res.reject()
+ }
+
+ BASE_URL = path
+
+ IMAGES = imagesFromUrls(image_paths)
+ res.notify(prefix, 'Loaded', 'Images.')
+
+ DATA = dataFromImages(IMAGES)
+ res.notify(prefix, 'Loaded', 'Data.')
+
+ updateRibbonsFromFavDirs()
+ res.notify(prefix, 'Loaded', 'Fav dirs.')
+
+ MARKED = []
+
+ reloadViewer()
+
+ return res.resolve()
+}
+
+
+// Load a path
+//
+// This will try and do one of the following in order:
+// 1) look for a cache and load it,
+// 2) load data from within the directory
+// 3) load a directory as-is
+// load fav dirs
+//
+function loadDir(path, prefix){
+ prefix = prefix == null ? 'Data' : prefix
+ prefix = prefix === false ? null : prefix
+
+ path = normalizePath(path)
+ var orig_path = path
+ var data
+
+ var res = $.Deferred()
+
+ res.notify(prefix, 'Loading', path)
+
+ var files = listDir(path)
+
+ if(files == null){
+ //showErrorStatus('No files in path: ' + path)
+ res.notify('load_error', path)
+ return res.reject()
+ }
+
+ // see if there is a cache...
+ if(files.indexOf(CACHE_DIR) >= 0){
+ path = path +'/'+ CACHE_DIR
+ }
+
+ bubbleProgress(prefix,
+ loadFileState(path, false), res, true)
+ .done(function(){
+ res.resolve()
+ })
+ .fail(function(){
+ bubbleProgress('Raw directory', loadRawDir(orig_path), res)
+ })
+
+ return res
+}
+
+
+// Load ribbon structure from fav directory tree
+//
+// XXX loads duplicate images....
+function updateRibbonsFromFavDirs(){
+ DATA.ribbons = ribbonsFromFavDirs(null, null, imageOrderCmp)
+ sortImagesByDate()
+ reloadViewer()
+}
+
+
+
+/**********************************************************************
+* vim:set ts=4 sw=4 : */
diff --git a/ui/index.html b/ui/index.html
index bc1b783a..2950ed69 100755
--- a/ui/index.html
+++ b/ui/index.html
@@ -57,6 +57,8 @@ less = {
+
+
diff --git a/ui/localstorage.js b/ui/localstorage.js
new file mode 100755
index 00000000..39316609
--- /dev/null
+++ b/ui/localstorage.js
@@ -0,0 +1,101 @@
+/**********************************************************************
+*
+*
+*
+**********************************************************************/
+
+//var DEBUG = DEBUG != null ? DEBUG : true
+
+
+/**********************************************************************
+* localStorage
+*
+* XXX should we use jStorage here?
+*/
+
+function loadLocalStorageData(attr){
+ attr = attr == null ? DATA_ATTR : attr
+ var data = localStorage[attr]
+ if(data == null){
+ data = '{}'
+ }
+ var base = localStorage[attr + '_BASE_URL']
+ base = base == null ? '.' : base
+ return {
+ data: JSON.parse(data),
+ base_url: base,
+ }
+}
+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 reloadViewer()
+}
+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 reloadViewer()
+}
+function saveLocalStorage(attr){
+ attr = attr == null ? DATA_ATTR : attr
+ saveLocalStorageData(attr)
+ saveLocalStorageImages(attr)
+}
+
+
+
+/**********************************************************************
+* vim:set ts=4 sw=4 : */