mirror of
https://github.com/flynx/ImageGrid.git
synced 2025-10-29 02:10:08 +00:00
action .mode -> .browseMode, reworked .sync(..) (ctrl_R)...
Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
parent
d912ce2ebe
commit
78cb4bdff8
@ -124,7 +124,7 @@ var NWHostActions = actions.Actions({
|
||||
// XXX add ability to use devtools on background page (node context)...
|
||||
// XXX get the devtools stage...
|
||||
showDevTools: ['Interface|Development/Show Dev Tools',
|
||||
{browseMode: 'toggleBrowseActionKeys'},
|
||||
{mode: 'toggleBrowseActionKeys'},
|
||||
function(action){
|
||||
if(action == '?'){
|
||||
// XXX get the devtools stage...
|
||||
@ -145,7 +145,7 @@ var NWHostActions = actions.Actions({
|
||||
}],
|
||||
|
||||
toggleSplashScreen: ['Interface/',
|
||||
{browseMode: 'toggleBrowseActionKeys'},
|
||||
{mode: 'toggleBrowseActionKeys'},
|
||||
function(){
|
||||
}],
|
||||
})
|
||||
@ -246,7 +246,7 @@ var ElectronHostActions = actions.Actions({
|
||||
|
||||
// XXX should this be a toggler???
|
||||
showDevTools: ['Interface|Development/Show Dev Tools',
|
||||
{browseMode: 'toggleBrowseActionKeys'},
|
||||
{mode: 'toggleBrowseActionKeys'},
|
||||
function(action){
|
||||
var w = electron.remote.getCurrentWindow()
|
||||
|
||||
@ -277,7 +277,7 @@ var ElectronHostActions = actions.Actions({
|
||||
|
||||
// XXX make this a real toggler...
|
||||
toggleSplashScreen: ['Interface/',
|
||||
{browseMode: 'toggleBrowseActionKeys'},
|
||||
{mode: 'toggleBrowseActionKeys'},
|
||||
function(action){
|
||||
var splash = this.splash = (!this.splash || this.splash.isDestroyed()) ?
|
||||
electron.remote.getGlobal('splash')
|
||||
@ -560,7 +560,7 @@ var WindowedAppControlActions = actions.Actions({
|
||||
}],
|
||||
|
||||
toggleSplashScreenShowing: ['Interface/Splash screen on start',
|
||||
{browseMode: 'toggleBrowseActionKeys'},
|
||||
{mode: 'toggleBrowseActionKeys'},
|
||||
core.makeConfigToggler('show-splash-screen',
|
||||
['on', 'off'],
|
||||
function(action){
|
||||
|
||||
@ -537,7 +537,7 @@ actions.Actions({
|
||||
.focusImage(0)
|
||||
.focusImage(0, 'global')
|
||||
`,
|
||||
{browseMode: function(target){
|
||||
{mode: function(target){
|
||||
return this.data.getImageOrder('ribbon', target) == 0 && 'disabled' }},
|
||||
function(all){ this.focusImage(0, all == null ? 'ribbon' : 'global') }],
|
||||
lastImage: ['Navigate/Last image in current ribbon',
|
||||
@ -549,7 +549,7 @@ actions.Actions({
|
||||
|
||||
NOTE: this is symmetrical to .firstImage(..) see docs for that.
|
||||
`,
|
||||
{browseMode: function(target){
|
||||
{mode: function(target){
|
||||
return this.data.getImageOrder('ribbon', target)
|
||||
== this.data.getImageOrder('ribbon', -1) && 'disabled' }},
|
||||
function(all){ this.focusImage(-1, all == null ? 'ribbon' : 'global') }],
|
||||
@ -561,7 +561,7 @@ actions.Actions({
|
||||
Shorthand for:
|
||||
.firstImage('global')
|
||||
`,
|
||||
{browseMode: function(){
|
||||
{mode: function(){
|
||||
return this.data.getImageOrder() == 0 && 'disabled' }},
|
||||
function(){ this.firstImage(true) }],
|
||||
lastGlobalImage: ['Navigate/Last image globally',
|
||||
@ -572,7 +572,7 @@ actions.Actions({
|
||||
|
||||
NOTE: this symmetrical to .firstGlobalImage(..) see docs for that.
|
||||
`,
|
||||
{browseMode: function(){
|
||||
{mode: function(){
|
||||
return this.data.getImageOrder() == this.data.getImageOrder(-1) && 'disabled' }},
|
||||
function(){ this.lastImage(true) }],
|
||||
|
||||
@ -584,7 +584,7 @@ actions.Actions({
|
||||
NOTE: this also modifies .direction
|
||||
NOTE: this is .symmetrical to .nextImage(..) see it for docs.
|
||||
`,
|
||||
{browseMode: 'firstImage'},
|
||||
{mode: 'firstImage'},
|
||||
function(a, mode){
|
||||
// keep track of traverse direction...
|
||||
this.direction = 'left'
|
||||
@ -627,7 +627,7 @@ actions.Actions({
|
||||
|
||||
NOTE: this also modifies .direction
|
||||
`,
|
||||
{browseMode: 'lastImage'},
|
||||
{mode: 'lastImage'},
|
||||
function(a, mode){
|
||||
// keep track of traverse direction...
|
||||
this.direction = 'right'
|
||||
@ -693,19 +693,19 @@ actions.Actions({
|
||||
}],
|
||||
|
||||
firstRibbon: ['Navigate/First ribbon',
|
||||
{browseMode: function(target){
|
||||
{mode: function(target){
|
||||
return this.data.getRibbonOrder(target) == 0 && 'disabled'}},
|
||||
function(){ this.focusRibbon('first') }],
|
||||
lastRibbon: ['Navigate/Last ribbon',
|
||||
{browseMode: function(target){
|
||||
{mode: function(target){
|
||||
return this.data.getRibbonOrder(target)
|
||||
== this.data.getRibbonOrder(-1) && 'disabled'}},
|
||||
function(){ this.focusRibbon('last') }],
|
||||
prevRibbon: ['Navigate/Previous ribbon',
|
||||
{browseMode: 'firstRibbon'},
|
||||
{mode: 'firstRibbon'},
|
||||
function(){ this.focusRibbon('before') }],
|
||||
nextRibbon: ['Navigate/Next ribbon',
|
||||
{browseMode: 'lastRibbon'},
|
||||
{mode: 'lastRibbon'},
|
||||
function(){ this.focusRibbon('after') }],
|
||||
})
|
||||
|
||||
@ -720,6 +720,7 @@ core.ImageGridFeatures.Feature({
|
||||
'serialization',
|
||||
],
|
||||
suggested: [
|
||||
'sync',
|
||||
'edit',
|
||||
//'tags',
|
||||
//'sort',
|
||||
@ -734,8 +735,7 @@ core.ImageGridFeatures.Feature({
|
||||
function(res){
|
||||
// we save .current unconditionally (if it exists)...
|
||||
if(res.raw.data){
|
||||
res.index.current = res.raw.data.current
|
||||
}
|
||||
res.index.current = res.raw.data.current }
|
||||
|
||||
var changes = res.changes
|
||||
|
||||
@ -743,10 +743,11 @@ core.ImageGridFeatures.Feature({
|
||||
return
|
||||
}
|
||||
|
||||
// data...
|
||||
if((changes === true || changes.data) && res.raw.data){
|
||||
res.index.data = res.raw.data
|
||||
}
|
||||
// basic sections...
|
||||
// NOTE: config is local config...
|
||||
;['config', 'data'].forEach(function(section){
|
||||
if((changes === true || chages[section]) && res.raw[section]){
|
||||
res.index[section] = res.raw[section] } })
|
||||
|
||||
// images (full)...
|
||||
if(res.raw.images
|
||||
@ -756,11 +757,11 @@ core.ImageGridFeatures.Feature({
|
||||
// images-diff...
|
||||
} else if(changes && changes.images){
|
||||
var diff = res.index['images-diff'] = {}
|
||||
changes.images.forEach(function(gid){
|
||||
diff[gid] = res.raw.images[gid]
|
||||
})
|
||||
}
|
||||
changes.images
|
||||
.forEach(function(gid){
|
||||
diff[gid] = res.raw.images[gid] }) }
|
||||
}],
|
||||
// XXX restore local .config....
|
||||
['prepareIndexForLoad',
|
||||
function(res, json, base_path){
|
||||
// build data and images...
|
||||
@ -773,7 +774,9 @@ core.ImageGridFeatures.Feature({
|
||||
|
||||
var img = images.Images(json.images)
|
||||
|
||||
// this is needed for loading multiple indexes...
|
||||
// handle base-path...
|
||||
// XXX do we actually need this???
|
||||
// ...this is also done in 'location'
|
||||
if(base_path){
|
||||
d.base_path = base_path
|
||||
// XXX STUB remove ASAP...
|
||||
@ -829,7 +832,7 @@ actions.Actions({
|
||||
|
||||
// NOTE: resetting this option will clear the last direction...
|
||||
toggleShiftsAffectDirection: ['Interface/Shifts affect direction',
|
||||
{browseMode: 'toggleBrowseActionKeys'},
|
||||
{mode: 'toggleBrowseActionKeys'},
|
||||
core.makeConfigToggler('shifts-affect-direction',
|
||||
['off', 'on'],
|
||||
function(action){
|
||||
@ -847,7 +850,7 @@ actions.Actions({
|
||||
state.base = this.base },
|
||||
undo: function(state){
|
||||
this.setBaseRibbon(state.base) },
|
||||
browseMode: function(target){
|
||||
mode: function(target){
|
||||
return this.current_ribbon == this.base && 'disabled' }},
|
||||
function(target){ this.data.setBase(target) }],
|
||||
|
||||
@ -954,7 +957,7 @@ actions.Actions({
|
||||
}],
|
||||
shiftImageLeft: ['Edit|Sort|Image/Shift image left', {
|
||||
undo: undoShift('shiftImageRight'),
|
||||
browseMode: 'prevImage'},
|
||||
mode: 'prevImage'},
|
||||
function(target){
|
||||
if(target == null){
|
||||
this.direction = 'left'
|
||||
@ -964,7 +967,7 @@ actions.Actions({
|
||||
}],
|
||||
shiftImageRight: ['Edit|Sort|Image/Shift image right', {
|
||||
undo: undoShift('shiftImageLeft'),
|
||||
browseMode: 'nextImage'},
|
||||
mode: 'nextImage'},
|
||||
function(target){
|
||||
if(target == null){
|
||||
this.direction = 'right'
|
||||
@ -975,7 +978,7 @@ actions.Actions({
|
||||
|
||||
shiftRibbonUp: ['Ribbon|Edit|Sort/Shift ribbon up', {
|
||||
undo: undoShift('shiftRibbonDown'),
|
||||
browseMode: 'prevRibbon'},
|
||||
mode: 'prevRibbon'},
|
||||
function(target){
|
||||
this.data.shiftRibbonUp(target)
|
||||
// XXX is this the right way to go/???
|
||||
@ -983,7 +986,7 @@ actions.Actions({
|
||||
}],
|
||||
shiftRibbonDown: ['Ribbon|Edit|Sort/Shift ribbon down', {
|
||||
undo: undoShift('shiftRibbonUp'),
|
||||
browseMode: 'nextRibbon'},
|
||||
mode: 'nextRibbon'},
|
||||
function(target){
|
||||
this.data.shiftRibbonDown(target)
|
||||
// XXX is this the right way to go/???
|
||||
@ -1046,16 +1049,16 @@ actions.Actions({
|
||||
this.data.getImages(
|
||||
this.data.getRibbon(ribbon || 'current'))) }],
|
||||
mergeRibbonUp: ['Edit|Ribbon/Merge ribbon up',
|
||||
{browseMode: function(){
|
||||
{mode: function(){
|
||||
return this.data.ribbon_order[0] == this.current_ribbon && 'disabled' }},
|
||||
'mergeRibbon: "up" ...'],
|
||||
mergeRibbonDown: ['Edit|Ribbon/Merge ribbon down',
|
||||
{browseMode: function(){
|
||||
{mode: function(){
|
||||
return this.data.ribbon_order.slice(-1)[0] == this.current_ribbon && 'disabled' }},
|
||||
'mergeRibbon: "down" ...'],
|
||||
// XXX should this accept a list of ribbons to flatten???
|
||||
flattenRibbons: ['Edit|Ribbon/Flatten',
|
||||
{browseMode: function(){
|
||||
{mode: function(){
|
||||
return this.data.ribbon_order.length <= 1 && 'disabled' }},
|
||||
function(){
|
||||
var ribbons = this.data.ribbons
|
||||
@ -1245,16 +1248,16 @@ core.ImageGridFeatures.Feature({
|
||||
var ImageGroupActions =
|
||||
module.ImageGroupActions = actions.Actions({
|
||||
expandGroup: ['Group/Expand group',
|
||||
{browseMode: 'ungroup'},
|
||||
{mode: 'ungroup'},
|
||||
function(target){ this.data.expandGroup(target || this.current) }],
|
||||
collapseGroup: ['Group/Collapse group', {
|
||||
journal: true,
|
||||
browseMode: 'ungroup'},
|
||||
mode: 'ungroup'},
|
||||
function(target){ this.data.collapseGroup(target || this.current) }],
|
||||
|
||||
cropGroup: ['Crop|Group/Crop group', {
|
||||
journal: true,
|
||||
browseMode: 'ungroup'},
|
||||
mode: 'ungroup'},
|
||||
function(target){ this.crop(this.data.cropGroup(target || this.current)) }],
|
||||
})
|
||||
|
||||
@ -1285,7 +1288,7 @@ module.ImageEditGroupActions = actions.Actions({
|
||||
function(gids, group){ this.data.group(gids, group) }],
|
||||
ungroup: ['Group|Edit/Ungroup images',
|
||||
{journal: true},
|
||||
{browseMode: function(){
|
||||
{mode: function(){
|
||||
return this.data.getGroup() == null && 'disabled' }},
|
||||
function(gids, group){ this.data.ungroup(gids, group) }],
|
||||
|
||||
@ -1500,7 +1503,7 @@ module.CropActions = actions.Actions({
|
||||
}
|
||||
}],
|
||||
uncrop: ['Crop/Uncrop',
|
||||
{browseMode: function(){ return this.cropped || 'disabled' }},
|
||||
{mode: function(){ return this.cropped || 'disabled' }},
|
||||
function(level, restore_current, keep_crop_order){
|
||||
level = level || 1
|
||||
|
||||
@ -1538,13 +1541,13 @@ module.CropActions = actions.Actions({
|
||||
}
|
||||
}],
|
||||
uncropAll: ['Crop/Uncrop all',
|
||||
{browseMode: 'uncrop'},
|
||||
{mode: 'uncrop'},
|
||||
function(restore_current){ this.uncrop('all', restore_current) }],
|
||||
// XXX see if we need to do this on this level??
|
||||
// ...might be a good idea to do this in data...
|
||||
uncropAndKeepOrder: ['Crop|Edit/Uncrop keeping image order', {
|
||||
journal: true,
|
||||
browseMode: 'uncrop'},
|
||||
mode: 'uncrop'},
|
||||
function(level, restore_current){ this.uncrop(level, restore_current, true) }],
|
||||
// XXX same as uncrop but will also try and merge changes...
|
||||
// - the order is simple and already done above...
|
||||
@ -1552,7 +1555,7 @@ module.CropActions = actions.Actions({
|
||||
// only problem here is how to deal with new ribbons...
|
||||
mergeCrop: ['- Crop|Edit/Merge crop', {
|
||||
journal: true,
|
||||
browseMode: 'uncrop'},
|
||||
mode: 'uncrop'},
|
||||
function(){
|
||||
// XXX
|
||||
}],
|
||||
@ -1573,7 +1576,7 @@ module.CropActions = actions.Actions({
|
||||
|
||||
// XXX not sure if we actually need this...
|
||||
cropFlatten: ['Crop/$Flatten',
|
||||
{browseMode: function(){
|
||||
{mode: function(){
|
||||
return this.data.ribbon_order.length <= 1 && 'disabled' }},
|
||||
function(list){ this.data.length > 0 && this.crop(list, true) }],
|
||||
cropRibbon: ['Crop|Ribbon/Crop $ribbon',
|
||||
@ -1733,7 +1736,7 @@ module.CropActions = actions.Actions({
|
||||
core.doc`
|
||||
`,
|
||||
{
|
||||
browseMode: 'uncrop',
|
||||
mode: 'uncrop',
|
||||
getUndoState: function(d){
|
||||
d.placements = this.data.getImagePositions(d.args[0]) },
|
||||
undo: function(d){
|
||||
@ -1796,7 +1799,7 @@ module.CropActions = actions.Actions({
|
||||
|
||||
NOTE: this is a shorthand for .removeFromCrop(..) but only supports
|
||||
ribbon removal.`,
|
||||
{browseMode: 'uncrop',},
|
||||
{mode: 'uncrop',},
|
||||
function(gids){
|
||||
var that = this
|
||||
gids = gids || this.current_ribbon
|
||||
|
||||
@ -745,7 +745,7 @@ var CollectionActions = actions.Actions({
|
||||
.collectionToTop(gid)
|
||||
-> this
|
||||
`,
|
||||
{browseMode: 'uncollect'},
|
||||
{mode: 'uncollect'},
|
||||
function(collection){
|
||||
collection = collection || this.collection
|
||||
collection = this.collectionGIDs[collection] || collection
|
||||
@ -984,7 +984,7 @@ var CollectionActions = actions.Actions({
|
||||
NOTE: this will remove any gid, be it image or ribbon.
|
||||
`,
|
||||
{
|
||||
browseMode: function(){ return !this.collection && 'disabled' },
|
||||
mode: function(){ return !this.collection && 'disabled' },
|
||||
// XXX two ways to go:
|
||||
// - .collect(..) + .data.placeImage(..)
|
||||
// - rewrite .collect(..) to use .data.placeImage(..) (like: .addToCrop(..))
|
||||
@ -1079,7 +1079,7 @@ var CollectionActions = actions.Actions({
|
||||
i.e. each gid given will be resolved to a ribbon which will be
|
||||
removed.
|
||||
`,
|
||||
{browseMode: 'uncollect'},
|
||||
{mode: 'uncollect'},
|
||||
function(gids, collection){
|
||||
var that = this
|
||||
gids = gids || 'current'
|
||||
@ -1282,7 +1282,7 @@ var CollectionActions = actions.Actions({
|
||||
// Config and interface stuff...
|
||||
//
|
||||
toggleCollectionCropRetention: ['Interface/Collection crop save mode',
|
||||
{browseMode: 'toggleBrowseActionKeys'},
|
||||
{mode: 'toggleBrowseActionKeys'},
|
||||
core.makeConfigToggler(
|
||||
'collection-save-crop-state',
|
||||
[
|
||||
@ -2718,7 +2718,7 @@ var UICollectionActions = actions.Actions({
|
||||
collectionGetterWrapper(function(title){ this.loadCollection(title) })],
|
||||
loadMainCollection: ['Collections/Exit collection view',
|
||||
{
|
||||
browseMode: 'uncollect',
|
||||
mode: 'uncollect',
|
||||
// prevent this from showing up in .uiDialogs list...
|
||||
__dialog__: false,
|
||||
},
|
||||
@ -2750,7 +2750,7 @@ var UICollectionActions = actions.Actions({
|
||||
|
||||
// XXX do we need this???
|
||||
cropImagesInCollection: ['Collections|Crop/Crop images in collection...',
|
||||
{browseMode: function(){
|
||||
{mode: function(){
|
||||
return (!this.collections
|
||||
|| Object.keys(this.collections).length == 0)
|
||||
&& 'disabled' }},
|
||||
@ -2764,7 +2764,7 @@ var UICollectionActions = actions.Actions({
|
||||
})
|
||||
}, null, false)],
|
||||
cropOutImagesInCollection: ['Collections|Crop/Remove collection images from crop...',
|
||||
{browseMode: 'cropImagesInCollection'},
|
||||
{mode: 'cropImagesInCollection'},
|
||||
mixedModeCollectionAction(function(title){
|
||||
var that = this
|
||||
this.ensureCollection(title)
|
||||
@ -2900,7 +2900,7 @@ var CollectionMarksActions = actions.Actions({
|
||||
function(collection){
|
||||
return this.collect(this.marked, collection) }],
|
||||
uncollectMarked: ['Collections|Mark/Remove marked from collection',
|
||||
{browseMode: function(){
|
||||
{mode: function(){
|
||||
return (!this.collection || this.marked.length == 0) && 'disabled' }},
|
||||
function(collection){
|
||||
return this.uncollect(this.marked, collection) }],
|
||||
@ -2910,7 +2910,7 @@ var CollectionMarksActions = actions.Actions({
|
||||
function(collection){
|
||||
return this.collectTagged('bookmark', collection) }],
|
||||
uncollectBookmarked: ['Collections|Bookmark/Remove bookmarked from collection',
|
||||
{browseMode: function(){
|
||||
{mode: function(){
|
||||
return (!this.collection || this.bookmarked.length == 0) && 'disabled' }},
|
||||
function(collection){
|
||||
return this.uncollectTagged('bookmark', collection) }],
|
||||
@ -2939,7 +2939,7 @@ var UICollectionMarksActions = actions.Actions({
|
||||
// UI...
|
||||
// XXX should these be a separate feature???
|
||||
markImagesInCollection: ['Collections|Mark/$Mark images in collection...',
|
||||
{browseMode: 'cropImagesInCollection'},
|
||||
{mode: 'cropImagesInCollection'},
|
||||
mixedModeCollectionAction(function(title){
|
||||
var that = this
|
||||
this.ensureCollection(title)
|
||||
@ -2950,7 +2950,7 @@ var UICollectionMarksActions = actions.Actions({
|
||||
})
|
||||
})],
|
||||
addMarkedToCollection: ['Collections|Mark/Add marked to $collection...',
|
||||
{browseMode: function(){
|
||||
{mode: function(){
|
||||
return this.marked.length == 0 && 'disabled' }},
|
||||
mixedModeCollectionAction(function(title){ this.collectMarked(title) })],
|
||||
})
|
||||
|
||||
@ -112,7 +112,7 @@ var FileSystemCommentsActions = actions.Actions({
|
||||
// XXX this will not save comments for merged indexes...
|
||||
saveComments: ['- File/',
|
||||
function(path, date, logger){
|
||||
if(this.location.method != 'loadIndex'
|
||||
if(this.location.load != 'loadIndex'
|
||||
|| this.location.loaded.length > 1){
|
||||
return
|
||||
}
|
||||
@ -135,7 +135,7 @@ var FileSystemCommentsActions = actions.Actions({
|
||||
//*/
|
||||
loadComments: ['- File/',
|
||||
function(path, date, logger){
|
||||
if(this.location.method != 'loadIndex'){
|
||||
if(this.location.load != 'loadIndex'){
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@ -485,6 +485,58 @@ var IntrospectionActions = actions.Actions({
|
||||
isEvent:
|
||||
actions.doWithRootAction(function(action){
|
||||
return !!action.__event__ }),
|
||||
|
||||
// XXX revise...
|
||||
getActionMode: ['- Interface/',
|
||||
doc`Get action browse mode...
|
||||
|
||||
Get and action's .mode(..) method and return its result.
|
||||
|
||||
Expected values:
|
||||
'disabled' - actions is disabled
|
||||
'hidden' - actions is hidden
|
||||
|
||||
NOTE: other values are ignored.
|
||||
`,
|
||||
function(action, mode_cache){
|
||||
var m = action
|
||||
var visited = [m]
|
||||
var last
|
||||
|
||||
// check cache...
|
||||
if(m in (mode_cache || {})){
|
||||
return mode_cache[m] }
|
||||
|
||||
// handle aliases...
|
||||
do {
|
||||
last = m
|
||||
m = this.getActionAttr(m, 'mode')
|
||||
|
||||
// check cache...
|
||||
if(m in (mode_cache || {})){
|
||||
return mode_cache[m] }
|
||||
|
||||
// check for loops...
|
||||
if(m && visited[m] != null){
|
||||
m = null
|
||||
break
|
||||
}
|
||||
visited.push(m)
|
||||
} while(typeof(m) == typeof('str'))
|
||||
|
||||
//return m ? m.call(this) : undefined
|
||||
return m ?
|
||||
// no cache...
|
||||
(mode_cache == null ?
|
||||
m.call(this)
|
||||
// cache hit...
|
||||
: last in mode_cache ?
|
||||
mode_cache[last]
|
||||
// call check and populate cache...
|
||||
: (mode_cache[action] =
|
||||
mode_cache[last] =
|
||||
m.call(this)))
|
||||
: actions.UNDEFINED }],
|
||||
})
|
||||
|
||||
|
||||
@ -1799,7 +1851,7 @@ var JournalActions = actions.Actions({
|
||||
.journal up until and including the undone action.
|
||||
NOTE: only the undone action is pushed to .rjournal
|
||||
`,
|
||||
{browseMode: function(){
|
||||
{mode: function(){
|
||||
return (this.journal && this.journal.length > 0) || 'disabled' }},
|
||||
function(){
|
||||
var journal = this.journal.slice() || []
|
||||
@ -1854,7 +1906,7 @@ var JournalActions = actions.Actions({
|
||||
|
||||
Essentially this will remove and re-run the last action in .rjournal
|
||||
`,
|
||||
{browseMode: function(){
|
||||
{mode: function(){
|
||||
return (this.rjournal && this.rjournal.length > 0) || 'disabled' }},
|
||||
function(){
|
||||
if(!this.rjournal || this.rjournal.length == 0){
|
||||
|
||||
@ -206,7 +206,7 @@ var ExampleActions = actions.Actions({
|
||||
// target...
|
||||
// XXX more docs!
|
||||
null,
|
||||
// state accessor...
|
||||
// state getter...
|
||||
function(_, state){
|
||||
// get the state...
|
||||
if(state == null){
|
||||
@ -324,7 +324,7 @@ var ExampleUIActions = actions.Actions({
|
||||
}],
|
||||
|
||||
exampleActionDisabled: ['Test/$Disabled example action',
|
||||
{browseMode: function(){ return 'disabled' }},
|
||||
{mode: function(){ return 'disabled' }},
|
||||
function(){
|
||||
console.log('Disabled action called:', [...arguments]) }],
|
||||
|
||||
|
||||
@ -382,13 +382,15 @@ var FileSystemLoaderActions = actions.Actions({
|
||||
!index
|
||||
&& console.error('Failed to load index from:', paths)
|
||||
|
||||
|
||||
// prepare the location data...
|
||||
index.location = {
|
||||
index.location =
|
||||
Object.assign(
|
||||
index.location || {},
|
||||
{
|
||||
path: path,
|
||||
loaded: loaded,
|
||||
method: 'loadIndex',
|
||||
}
|
||||
load: 'loadIndex',
|
||||
})
|
||||
if(from_date){
|
||||
index.location.from = from_date
|
||||
}
|
||||
@ -597,7 +599,8 @@ var FileSystemLoaderActions = actions.Actions({
|
||||
|
||||
location: {
|
||||
path: path,
|
||||
method: 'loadImages',
|
||||
load: 'loadImages',
|
||||
sync: 'syncIndexWithDir',
|
||||
}
|
||||
})
|
||||
.then(function(){
|
||||
@ -675,7 +678,7 @@ var FileSystemLoaderActions = actions.Actions({
|
||||
|
||||
// XXX revise logger...
|
||||
// XXX revise alignment...
|
||||
loadNewImages: ['File/Load new images',
|
||||
loadNewImages: ['File/Load new images to index',
|
||||
core.doc`Load new images...
|
||||
|
||||
Load new images from current path...
|
||||
@ -691,6 +694,10 @@ var FileSystemLoaderActions = actions.Actions({
|
||||
|
||||
NOTE: this will not load images that are already loaded.
|
||||
`,
|
||||
{ locationSync: true,
|
||||
mode: function(){
|
||||
return ['loadIndex', 'loadImages'].includes(this.location.load)
|
||||
|| 'disabled' }, },
|
||||
function(path, logger){
|
||||
path = path || this.location.path
|
||||
|
||||
@ -843,6 +850,8 @@ var FileSystemLoaderActions = actions.Actions({
|
||||
NOTE: no actual data is removed.
|
||||
NOTE: this will not remove generated previews from index.
|
||||
`,
|
||||
{ locationSync: true,
|
||||
mode: 'loadNewImages', },
|
||||
function(logger){
|
||||
var that = this
|
||||
logger = logger || this.logger
|
||||
@ -887,7 +896,7 @@ var FileSystemLoaderActions = actions.Actions({
|
||||
|
||||
// XXX EXPERIMENTAL...
|
||||
// shorthand...
|
||||
syncIndexWithDir: ['- File/',
|
||||
syncIndexWithDir: ['File/Synchronize index to path',
|
||||
core.doc`Load new and remove deleted images...
|
||||
|
||||
.syncIndexWithDir()
|
||||
@ -898,6 +907,8 @@ var FileSystemLoaderActions = actions.Actions({
|
||||
.loadNewImages()
|
||||
.removeMissingImages()
|
||||
`,
|
||||
{ locationSync: true,
|
||||
mode: 'loadNewImages', },
|
||||
function(logger){
|
||||
return Promise.all([
|
||||
this.loadNewImages(),
|
||||
@ -1137,7 +1148,7 @@ var FileSystemLoaderUIActions = actions.Actions({
|
||||
var dialog = this
|
||||
var path = that.location.path
|
||||
|
||||
if(that.location.method != 'loadIndex'){
|
||||
if(that.location.load != 'loadIndex'){
|
||||
make('No indexes loaded...', null, true)
|
||||
return
|
||||
}
|
||||
@ -1405,7 +1416,7 @@ var FileSystemSaveHistoryUIActions = actions.Actions({
|
||||
// Unsaved changes will be saved to .unsaved_index when switching
|
||||
// from current to a historic state.
|
||||
//
|
||||
// NOTE: this will show no history if .location.method is not 'loadIndex'..
|
||||
// NOTE: this will show no history if .location.load is not 'loadIndex'..
|
||||
// NOTE: this will set changes to all when loading a historic state
|
||||
// that the latest and to non otherwise....
|
||||
//
|
||||
@ -1441,7 +1452,7 @@ var FileSystemSaveHistoryUIActions = actions.Actions({
|
||||
}
|
||||
|
||||
// only search for history if we have an index loaded...
|
||||
if(that.location.method != 'loadIndex'){
|
||||
if(that.location.load != 'loadIndex'){
|
||||
make('No history...', {disabled: true})
|
||||
|
||||
// select the 'Unsaved' item...
|
||||
@ -1758,7 +1769,7 @@ var FileSystemWriterActions = actions.Actions({
|
||||
}
|
||||
|
||||
// XXX
|
||||
if(path == null && this.location.method != 'loadIndex'){
|
||||
if(path == null && this.location.load != 'loadIndex'){
|
||||
path = this.location.path
|
||||
}
|
||||
|
||||
@ -1798,7 +1809,7 @@ var FileSystemWriterActions = actions.Actions({
|
||||
.spawn('attrib', ['+h', full_path])
|
||||
})
|
||||
.then(function(){
|
||||
location.method = 'loadIndex'
|
||||
location.load = 'loadIndex'
|
||||
location.from = index.date
|
||||
|
||||
//return location
|
||||
|
||||
@ -144,7 +144,7 @@ var URLHistoryActions = actions.Actions({
|
||||
this.url_history = this.url_history || {}
|
||||
var item = !clear ? (this.url_history[url] || {}) : {}
|
||||
|
||||
open = item.open = open || this.location.method
|
||||
open = item.open = open || this.location.load
|
||||
check = item.check = check || this.location.check || 'checkPath'
|
||||
|
||||
// remove the old value...
|
||||
@ -429,7 +429,7 @@ var URLHistoryLocalStorageActions = actions.Actions({
|
||||
})
|
||||
}
|
||||
|
||||
this.openURLFromHistory(l.path, l.method)
|
||||
this.openURLFromHistory(l.path, l.load)
|
||||
|
||||
} else {
|
||||
this.openURLFromHistory(0)
|
||||
@ -514,17 +514,17 @@ module.URLHistoryFSWriter = core.ImageGridFeatures.Feature({
|
||||
res.then(function(l){
|
||||
// push saved to top...
|
||||
if(that.config['url-history-push-to-top-on-save']){
|
||||
that.pushURLToHistory(l.path, l.method)
|
||||
that.pushURLToHistory(l.path, l.load)
|
||||
|
||||
// update...
|
||||
} else {
|
||||
var e = that.url_history[l.path]
|
||||
if(e != null){
|
||||
e.open = l.method
|
||||
e.open = l.load
|
||||
that.storeURLHistory()
|
||||
|
||||
} else {
|
||||
that.pushURLToHistory(l.path, l.method)
|
||||
that.pushURLToHistory(l.path, l.load)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@ -263,9 +263,7 @@ module.GLOBAL_KEYBOARD = {
|
||||
ctrl_F: 'F11',
|
||||
meta_F: 'F11',
|
||||
|
||||
// XXX EXPERIMENTAL: which should it be???
|
||||
//ctrl_R: 'loadNewImages!',
|
||||
ctrl_R: 'syncIndexWithDir!',
|
||||
ctrl_R: 'sync!',
|
||||
ctrl_alt_R: 'reload!',
|
||||
ctrl_shift_R: 'F5',
|
||||
|
||||
@ -616,7 +614,7 @@ var KeyboardActions = actions.Actions({
|
||||
|
||||
// check if we have no doc...
|
||||
if(doc == null || doc == ''){
|
||||
console.warn('Action has no doc: "'
|
||||
console.warn('Action has no short doc: "'
|
||||
+ a.action +'" at: "'+ code +'"')
|
||||
// XXX ???
|
||||
return
|
||||
|
||||
@ -12,6 +12,7 @@ var object = require('lib/object')
|
||||
|
||||
var actions = require('lib/actions')
|
||||
var features = require('lib/features')
|
||||
var toggler = require('lib/toggler')
|
||||
|
||||
var core = require('features/core')
|
||||
|
||||
@ -22,26 +23,17 @@ var core = require('features/core')
|
||||
|
||||
// XXX need the other .location stuff to be visible/accessible...
|
||||
// ...now this only shows path...
|
||||
var LocationProto = {
|
||||
var Location =
|
||||
//module.Location =
|
||||
object.Constructor('Location', {
|
||||
get path(){
|
||||
return this.__actions.__location.path
|
||||
},
|
||||
return this.__actions.__location.path },
|
||||
set path(value){
|
||||
this.__actions.location = value
|
||||
},
|
||||
|
||||
this.__actions.location = value },
|
||||
|
||||
__init__: function(actions){
|
||||
this.__actions = actions
|
||||
|
||||
// XXX this does not work...
|
||||
// ...the oother way around seems best:
|
||||
// actions.__location.__proto__ = this
|
||||
//this.__proto__ = actions.__location
|
||||
},
|
||||
}
|
||||
|
||||
var Location = object.Constructor('Location', LocationProto)
|
||||
this.__actions = actions },
|
||||
})
|
||||
|
||||
|
||||
|
||||
@ -59,13 +51,18 @@ var Location = object.Constructor('Location', LocationProto)
|
||||
var LocationActions = actions.Actions({
|
||||
config: {
|
||||
'default-load-method': null,
|
||||
|
||||
'location-stored-attrs': [
|
||||
'sync',
|
||||
],
|
||||
},
|
||||
|
||||
// Format:
|
||||
// {
|
||||
// path: <base-path>,
|
||||
// method: <load-method>,
|
||||
// current: <current-gid>,
|
||||
// load: <load-method>,
|
||||
// sync: <sync-method>,
|
||||
// path: <base-path>,
|
||||
// ...
|
||||
// }
|
||||
//
|
||||
@ -100,10 +97,6 @@ var LocationActions = actions.Actions({
|
||||
this.loadLocation(value) },
|
||||
|
||||
|
||||
clearLoaction: ['File/Clear location',
|
||||
function(){ delete this.__location }],
|
||||
|
||||
|
||||
// Load location...
|
||||
//
|
||||
// Reload current location...
|
||||
@ -131,7 +124,7 @@ var LocationActions = actions.Actions({
|
||||
if(typeof(location) == typeof('str')){
|
||||
location = {
|
||||
path: path,
|
||||
method: (this.__location && this.__location.method)
|
||||
load: (this.__location && this.__location.load)
|
||||
|| this.config['default-load-method'],
|
||||
current: this.current,
|
||||
}
|
||||
@ -142,8 +135,8 @@ var LocationActions = actions.Actions({
|
||||
location = JSON.parse(JSON.stringify(location))
|
||||
}
|
||||
|
||||
var method = location.method
|
||||
|| this.location.method
|
||||
var load = location.load
|
||||
|| this.location.load
|
||||
|| this.config['default-load-method']
|
||||
var cur = location.current
|
||||
var path = location.path
|
||||
@ -158,7 +151,7 @@ var LocationActions = actions.Actions({
|
||||
//this.__location = location
|
||||
|
||||
// NOTE: the method should set the proper location if it uses .clear()...
|
||||
var res = method && this[method](path)
|
||||
var res = load && this[load](path)
|
||||
|
||||
|
||||
// load current...
|
||||
@ -177,6 +170,10 @@ var LocationActions = actions.Actions({
|
||||
return res
|
||||
}],
|
||||
|
||||
clearLoaction: ['File/Clear location',
|
||||
function(){ delete this.__location }],
|
||||
|
||||
|
||||
// XXX
|
||||
// XXX should these have the same effect as .dispatch('location:*:load', location)???
|
||||
// ...another way to put it is should we call this from dispatch?
|
||||
@ -186,20 +183,19 @@ var LocationActions = actions.Actions({
|
||||
_loadLocation: ['- File/Save location',
|
||||
{protocol: 'location:*:load'},
|
||||
function(location){
|
||||
this.location.method = this.locationMethod(location)
|
||||
this.location.load = this.locationMethod(location)
|
||||
this.dispatch('location:*:load', location)
|
||||
}],
|
||||
_saveLocation: ['- File/Save location',
|
||||
{protocol: 'location:*:save'},
|
||||
function(location){
|
||||
this.location.method = this.locationMethod(location)
|
||||
this.location.load = this.locationMethod(location)
|
||||
this.dispatch('location:*:save', location) }],
|
||||
_locationMethod: ['- File/',
|
||||
{protocol: 'location:?'},
|
||||
function(location){
|
||||
return (location || this.location).method || null }],
|
||||
|
||||
|
||||
return (location || this.location).load || null }],
|
||||
//
|
||||
// format:
|
||||
// {
|
||||
// 'protocol:method': 'actionName',
|
||||
@ -212,8 +208,7 @@ var LocationActions = actions.Actions({
|
||||
get protocols(){
|
||||
var cache = this.__location_protocol_cache = this.__location_protocol_cache
|
||||
|| this.cacheProtocols()
|
||||
return cache
|
||||
},
|
||||
return cache },
|
||||
cacheProtocols: ['- File/',
|
||||
function(){
|
||||
var that = this
|
||||
@ -221,11 +216,8 @@ var LocationActions = actions.Actions({
|
||||
this.actions.forEach(function(n){
|
||||
var proto = that.getActionAttr(n, 'protocol')
|
||||
if(proto){
|
||||
res[proto] = n
|
||||
}
|
||||
})
|
||||
return res
|
||||
}],
|
||||
res[proto] = n } })
|
||||
return res }],
|
||||
// XXX how do we call the dispatched actions and all the matching
|
||||
// pattern actions???
|
||||
// One way to go would be:
|
||||
@ -316,6 +308,91 @@ var LocationActions = actions.Actions({
|
||||
}
|
||||
}],
|
||||
|
||||
|
||||
// sync API...
|
||||
//
|
||||
get location_sync_methods(){
|
||||
var that = this
|
||||
return (this.__location_sync_methods_cache =
|
||||
this.__location_sync_methods_cache
|
||||
|| this.actions
|
||||
.filter(function(n){
|
||||
return that.getActionAttr(n, 'locationSync') })
|
||||
.reduce(function(res, n){
|
||||
res[n] =
|
||||
(that.getActionAttr(n, 'doc') || '')
|
||||
.split(/[\\\/]/)
|
||||
.pop()
|
||||
res[n] = res[n] == '' ?
|
||||
n
|
||||
: res[n]
|
||||
return res
|
||||
}, {})) },
|
||||
|
||||
sync: ['- System/Synchronize index',
|
||||
core.doc`Synchronize index...
|
||||
|
||||
.sync()
|
||||
-> promise
|
||||
|
||||
|
||||
NOTE: it is up to the client to detect and implement the actual
|
||||
sync mechanics.
|
||||
NOTE: this expects the return value of the sync handler to be
|
||||
a promise...
|
||||
`,
|
||||
function(){
|
||||
var method = this.location.sync
|
||||
return method in this ?
|
||||
// NOTE: this should return a promise...
|
||||
this[method](...arguments)
|
||||
: Promise.resolve() }],
|
||||
|
||||
toggleSyncMethod: ['File/Index synchronization method',
|
||||
core.doc`Toggle index synchronization method
|
||||
|
||||
NOTE: this will not show disabled methods.`,
|
||||
toggler.Toggler(null,
|
||||
function(_, state){
|
||||
var dict = this.location_sync_methods
|
||||
|
||||
// get...
|
||||
if(state == null){
|
||||
return dict[this.location.sync]
|
||||
|| this.location.sync
|
||||
|| 'none' }
|
||||
|
||||
// clear...
|
||||
if(state == 'none'){
|
||||
delete this.location.sync
|
||||
|
||||
// set...
|
||||
} else {
|
||||
// reverse dict...
|
||||
var rdict = new Map(
|
||||
Object.entries(dict)
|
||||
.map(function([k, v]){
|
||||
return [v, k] }))
|
||||
// normalize state to action name...
|
||||
state = state in dict ?
|
||||
state
|
||||
: (rdict.get(state) || state)
|
||||
this.location.sync = state
|
||||
}
|
||||
|
||||
this.markChanged
|
||||
&& this.markChanged('config')
|
||||
},
|
||||
function(){
|
||||
var that = this
|
||||
return ['none',
|
||||
...Object.entries(this.location_sync_methods)
|
||||
.filter(function([n, d]){
|
||||
// do not list disabled methods...
|
||||
return that.getActionMode(n) != 'disabled' })
|
||||
.map(function([n, d]){ return d }) ] })],
|
||||
|
||||
|
||||
// 1) store .location
|
||||
// 2) cleanup .images[..].base_path
|
||||
//
|
||||
@ -335,9 +412,7 @@ var LocationActions = actions.Actions({
|
||||
if(l.path == img.base_path){
|
||||
delete img.base_path
|
||||
}
|
||||
})
|
||||
}
|
||||
}}],
|
||||
}) } }}],
|
||||
load: [function(){
|
||||
return function(_, data){
|
||||
var that = this
|
||||
@ -356,14 +431,12 @@ var LocationActions = actions.Actions({
|
||||
if(img.base_path == null){
|
||||
img.base_path = l.path
|
||||
}
|
||||
})
|
||||
}}],
|
||||
}) }}],
|
||||
clone: [function(){
|
||||
return function(res){
|
||||
if(this.location){
|
||||
res.__location = JSON.parse(JSON.stringify(this.__location))
|
||||
}
|
||||
}}],
|
||||
} }}],
|
||||
clear: [function(){
|
||||
this.clearLoaction() }],
|
||||
})
|
||||
@ -380,6 +453,36 @@ module.Location = core.ImageGridFeatures.Feature({
|
||||
actions: LocationActions,
|
||||
|
||||
handlers: [
|
||||
// handle:
|
||||
// - local configuration...
|
||||
// .location <-> .config
|
||||
// XXX should this handle image .base_path ???
|
||||
['prepareIndexForWrite',
|
||||
function(res){
|
||||
if(res.changes === true || res.changes.config){
|
||||
var data = {}
|
||||
;(this.config['location-stored-attrs'] || [])
|
||||
.forEach(function(attr){
|
||||
attr in res.raw.location
|
||||
&& (data[attr] = res.raw.location[attr]) })
|
||||
Object.keys(data).length > 0
|
||||
&& (res.index.config =
|
||||
Object.assign(
|
||||
res.index.config || {},
|
||||
data)) } }],
|
||||
['prepareIndexForLoad',
|
||||
function(res, json, base_path){
|
||||
if(json.config){
|
||||
var data = {}
|
||||
;(this.config['location-stored-attrs'] || [])
|
||||
.forEach(function(attr){
|
||||
attr in json.config
|
||||
&& (data[attr] = json.config[attr]) })
|
||||
Object.keys(data).length > 0
|
||||
&& (res.location =
|
||||
Object.assign(
|
||||
res.location || {},
|
||||
data)) } }],
|
||||
],
|
||||
})
|
||||
|
||||
|
||||
@ -257,37 +257,37 @@ var ImageMarkActions = actions.Actions({
|
||||
}],
|
||||
|
||||
prevMarked: ['Mark|Navigate/Previous marked image',
|
||||
{browseMode: function(target){
|
||||
{mode: function(target){
|
||||
return this.data.getImage('current', 'before', this.marked) == null && 'disabled' }},
|
||||
function(mode){ this.prevTagged('marked', mode) }],
|
||||
nextMarked: ['Mark|Navigate/Next marked image',
|
||||
{browseMode: function(target){
|
||||
{mode: function(target){
|
||||
return this.data.getImage('current', 'after', this.marked) == null && 'disabled' }},
|
||||
function(mode){ this.nextTagged('marked', mode) }],
|
||||
|
||||
cropMarked: ['Mark|Crop/Crop $marked images',
|
||||
{browseMode: function(target){
|
||||
{mode: function(target){
|
||||
return this.marked.length == 0 && 'disabled' }},
|
||||
'crop: "marked" ...'],
|
||||
//function(flatten){ this.cropTagged('marked', flatten) }],
|
||||
//function(flatten){ this.cropTagged('marked', 'any', flatten) }],
|
||||
|
||||
removeMarkedFromCrop: ['Mark|Crop/Remove marked from crop',
|
||||
{browseMode: function(target){
|
||||
{mode: function(target){
|
||||
return (this.marked.length == 0 || !this.cropped) && 'disabled' }},
|
||||
'removeFromCrop: marked'],
|
||||
|
||||
rotateMarkedCW: ['Mark/Rotate marked clockwise',
|
||||
{browseMode: 'cropMarked'},
|
||||
{mode: 'cropMarked'},
|
||||
'rotateCW: marked'],
|
||||
rotateMarkedCCW: ['Mark/Rotate marked counterclockwise',
|
||||
{browseMode: 'cropMarked'},
|
||||
{mode: 'cropMarked'},
|
||||
'rotateCCW: marked'],
|
||||
flipMarkedVertical: ['Mark/Flip marked vertically',
|
||||
{browseMode: 'cropMarked'},
|
||||
{mode: 'cropMarked'},
|
||||
'flipVertical: marked'],
|
||||
flipMarkedHorizontal: ['Mark/Flip marked horizontally',
|
||||
{browseMode: 'cropMarked'},
|
||||
{mode: 'cropMarked'},
|
||||
'flipHorizontal: marked'],
|
||||
})
|
||||
|
||||
@ -396,29 +396,29 @@ var ImageMarkEditActions = actions.Actions({
|
||||
|
||||
shiftMarkedUp: ['Mark/Shift marked u$p',
|
||||
{undo: undoShift('shiftMarkedDown'),
|
||||
browseMode: 'cropMarked'},
|
||||
mode: 'cropMarked'},
|
||||
shiftMarked('up')],
|
||||
shiftMarkedDown: ['Mark/Shift marked $down',
|
||||
{undo: undoShift('shiftMarkedUp'),
|
||||
browseMode: 'cropMarked'},
|
||||
mode: 'cropMarked'},
|
||||
shiftMarked('down')],
|
||||
|
||||
// XXX undo...
|
||||
shiftMarkedAfter: ['Mark|Image/Shift marked $after',
|
||||
{browseMode: 'cropMarked'},
|
||||
{mode: 'cropMarked'},
|
||||
function(target){
|
||||
this.shiftImageTo(this.marked, target || 'current', 'after') }],
|
||||
// XXX undo...
|
||||
shiftMarkedBefore: ['Mark|Image/Shift marked $b$efore',
|
||||
{browseMode: 'cropMarked'},
|
||||
{mode: 'cropMarked'},
|
||||
function(target){
|
||||
this.shiftImageTo(this.marked, target || 'current', 'before') }],
|
||||
|
||||
unmarkRibbon: ['Mark/Unmark ribbon',
|
||||
{browseMode: 'cropMarked'},
|
||||
{mode: 'cropMarked'},
|
||||
'toggleMark: "ribbon" "off"'],
|
||||
unmarkLoaded: ['Mark/$Unmark all',
|
||||
{browseMode: 'cropMarked'},
|
||||
{mode: 'cropMarked'},
|
||||
'toggleMark: "loaded" "off"'],
|
||||
})
|
||||
|
||||
@ -445,7 +445,7 @@ var ImageMarkGroupActions = actions.Actions({
|
||||
// NOTE: this will only group loaded images...
|
||||
groupMarked: ['Group|Mark/-70:Group loaded marked images',
|
||||
{journal: true,
|
||||
browseMode: 'cropMarked'},
|
||||
mode: 'cropMarked'},
|
||||
function(){
|
||||
this.group(this.data.getImages(this.marked)) }],
|
||||
})
|
||||
@ -524,16 +524,16 @@ var ImageBookmarkActions = actions.Actions({
|
||||
: this.data.sortViaOrder(this.data.tagQuery('bookmark')) },
|
||||
|
||||
prevBookmarked: ['Bookmark|Navigate/Previous bookmarked image',
|
||||
{browseMode: function(target){
|
||||
{mode: function(target){
|
||||
return this.data.getImage('current', 'before', this.bookmarked) == null && 'disabled' }},
|
||||
function(mode){ this.prevTagged('bookmark', mode) }],
|
||||
nextBookmarked: ['Bookmark|Navigate/Next bookmarked image',
|
||||
{browseMode: function(target){
|
||||
{mode: function(target){
|
||||
return this.data.getImage('current', 'after', this.bookmarked) == null && 'disabled' }},
|
||||
function(mode){ this.nextTagged('bookmark', mode) }],
|
||||
|
||||
cropBookmarked: ['Bookmark|Crop/Crop $bookmarked images',
|
||||
{browseMode: function(target){
|
||||
{mode: function(target){
|
||||
return this.bookmarked.length == 0 && 'disabled' }},
|
||||
'crop: "bookmarked" ...'],
|
||||
//function(flatten){ this.cropTagged('bookmark', 'any', flatten) }],
|
||||
@ -572,7 +572,7 @@ var ImageBookmarkEditActions = actions.Actions({
|
||||
// 'off' - toggle all off
|
||||
// 'next' - toggle each image to next state
|
||||
toggleBookmarkOnMarked: ['Bookmark|Mark/-70:Toggle bookmark on maked images',
|
||||
{browseMode: 'cropMarked'},
|
||||
{mode: 'cropMarked'},
|
||||
function(action){
|
||||
return this.toggleBookmark(this.marked, action) }],
|
||||
})
|
||||
|
||||
@ -352,7 +352,7 @@ var MetadataUIActions = actions.Actions({
|
||||
function(){ return this.config['metadata-auto-select-modes'] })],
|
||||
|
||||
toggleMetadataGraph: ['Interface/Metadata graph display',
|
||||
{ browseMode: function(){
|
||||
{ mode: function(){
|
||||
return (!graph || this.config['browse-advanced-mode'] != 'on') && 'hidden' }},
|
||||
core.makeConfigToggler('metadata-graph', ['on', 'off'])],
|
||||
|
||||
|
||||
@ -172,7 +172,7 @@ module.TagsEditActions = actions.Actions({
|
||||
// NOTE: setting source to 'both' and mode to 'reset' is the same as
|
||||
// 'images' and 'reset' as all .data tags will be lost on first
|
||||
// pass...
|
||||
syncTags: ['Tag/-10:Synchoronize tags between data and images',
|
||||
syncTags: ['Tag/-10:Synchronize tags between data and images',
|
||||
{journal: true},
|
||||
function(source, mode){
|
||||
// can't do anything if either .data or .images are not
|
||||
|
||||
@ -28,7 +28,7 @@ var PreCacheActions = actions.Actions({
|
||||
// NOTE: this will not work from chrome when loading from a local fs...
|
||||
// XXX experimental...
|
||||
startCacheWorker: ['Interface/',
|
||||
{browseMode: 'toggleBrowseActionKeys'},
|
||||
{mode: 'toggleBrowseActionKeys'},
|
||||
function(){
|
||||
// a worker is started already...
|
||||
if(this.cacheWorker != null){
|
||||
@ -63,7 +63,7 @@ var PreCacheActions = actions.Actions({
|
||||
this.cacheWorker.url = url
|
||||
}],
|
||||
stopCacheWorker: ['Interface/',
|
||||
{browseMode: 'toggleBrowseActionKeys'},
|
||||
{mode: 'toggleBrowseActionKeys'},
|
||||
function(){
|
||||
if(this.cacheWorker){
|
||||
this.cacheWorker.terminate()
|
||||
|
||||
@ -101,7 +101,7 @@ var RangeActions = actions.Actions({
|
||||
}],
|
||||
clearRange: ['Range/Clear range',
|
||||
// XXX not sure if this is the right way to go...
|
||||
{browseMode: function(){ return !this.data.__range && 'disabled' }},
|
||||
{mode: function(){ return !this.data.__range && 'disabled' }},
|
||||
function(image){
|
||||
var r = this.dom.find('.ribbon')
|
||||
|
||||
@ -162,7 +162,7 @@ var RangeActions = actions.Actions({
|
||||
|
||||
cropRange: ['Range|Crop/Crop range',
|
||||
// XXX not sure if this is the right way to go...
|
||||
{browseMode: function(){ return !this.data.__range && 'disabled' }},
|
||||
{mode: function(){ return !this.data.__range && 'disabled' }},
|
||||
function(){
|
||||
var range = this.data.__range
|
||||
var order = this.data.order
|
||||
@ -175,7 +175,7 @@ var RangeActions = actions.Actions({
|
||||
}],
|
||||
cropRangeOut: ['Range|Crop/Crop out range',
|
||||
// XXX not sure if this is the right way to go...
|
||||
{browseMode: function(){ return !this.data.__range && 'disabled' }},
|
||||
{mode: function(){ return !this.data.__range && 'disabled' }},
|
||||
function(){
|
||||
var range = this.data.__range
|
||||
var order = this.data.order
|
||||
|
||||
@ -224,7 +224,7 @@ var SlideshowActions = actions.Actions({
|
||||
|
||||
NOTE: this will have no effect if the slideshow is not running...
|
||||
`,
|
||||
{browseMode: function(){ return this.toggleSlideshow('?') == 'off' && 'disabled' }},
|
||||
{mode: function(){ return this.toggleSlideshow('?') == 'off' && 'disabled' }},
|
||||
toggler.Toggler(null,
|
||||
function(_, state){
|
||||
if(state == null){
|
||||
|
||||
@ -852,12 +852,12 @@ var StatusLogActions = actions.Actions({
|
||||
|
||||
// XXX should this be here or in a separate feature???
|
||||
statusLog: ['Interface/Show status log',
|
||||
{browseMode: 'toggleBrowseActionKeys'},
|
||||
{mode: 'toggleBrowseActionKeys'},
|
||||
function(){
|
||||
// XXX use list
|
||||
}],
|
||||
clearStatusLog: ['Interface/Clear status log',
|
||||
{browseMode: 'toggleBrowseActionKeys'},
|
||||
{mode: 'toggleBrowseActionKeys'},
|
||||
function(){
|
||||
delete this.__status_log
|
||||
}],
|
||||
|
||||
@ -904,7 +904,7 @@ var DialogsActions = actions.Actions({
|
||||
|
||||
|
||||
listDialogs: ['Interface|System/Dialog/Dialog list...',
|
||||
{browseMode: 'toggleBrowseActionKeys'},
|
||||
{mode: 'toggleBrowseActionKeys'},
|
||||
makeUIDialog(function(){
|
||||
var actions = this
|
||||
|
||||
@ -931,7 +931,7 @@ var DialogsActions = actions.Actions({
|
||||
})],
|
||||
|
||||
toggleOverlayBlur: ['Interface/Dialog overlay blur',
|
||||
{browseMode: 'toggleBrowseActionKeys'},
|
||||
{mode: 'toggleBrowseActionKeys'},
|
||||
toggler.CSSClassToggler(
|
||||
function(){ return this.dom },
|
||||
'overlay-blur-enabled',
|
||||
@ -2587,7 +2587,7 @@ var BrowseActionsActions = actions.Actions({
|
||||
// ...this would help with the (global) search -- switch to
|
||||
// flat if searching in root mode...
|
||||
browseActions: ['Interface/Dialog/Actions...',
|
||||
{browseMode: 'toggleBrowseActionKeys'},
|
||||
{mode: 'toggleBrowseActionKeys'},
|
||||
core.doc`Browse actions dialog...
|
||||
|
||||
This uses action definition to build and present an action tree.
|
||||
@ -2615,33 +2615,33 @@ var BrowseActionsActions = actions.Actions({
|
||||
|
||||
|
||||
Action mode (disabled/hidden) and also be controlled dynamically:
|
||||
- .browseMode() action method is called with actions as base.
|
||||
- .mode() action method is called with actions as base.
|
||||
Example:
|
||||
someAction: ['Path/To/Some action',
|
||||
{browseMode: function(){ ... }},
|
||||
{mode: function(){ ... }},
|
||||
function(){
|
||||
...
|
||||
}],
|
||||
someOtherAction: ['Path/To/Some action',
|
||||
// alias
|
||||
{browseMode: 'someAction'},
|
||||
{mode: 'someAction'},
|
||||
function(){
|
||||
...
|
||||
}],
|
||||
|
||||
.browseMode can be:
|
||||
.mode can be:
|
||||
<function> - action method.
|
||||
<action-name> - alias, name of action to get the
|
||||
method from.
|
||||
|
||||
.browseMode() can return:
|
||||
.mode() can return:
|
||||
'disabled' - item will be disabled.
|
||||
'hidden' - item will be both hidden and disabled.
|
||||
|
||||
NOTE: disabling in path has priority over .browseMode(), thus
|
||||
NOTE: disabling in path has priority over .mode(), thus
|
||||
it is possible to hide/disable an enabled item but not
|
||||
possible to enable a disabled by default path.
|
||||
NOTE: .browseMode() can be defined in any action in chain,
|
||||
NOTE: .mode() can be defined in any action in chain,
|
||||
though only the last one is called...
|
||||
|
||||
|
||||
@ -2704,52 +2704,12 @@ var BrowseActionsActions = actions.Actions({
|
||||
|
||||
// Get action browse mode (disabled or hidden)...
|
||||
//
|
||||
// NOTE: this will cache and reuse action's browseMode, this
|
||||
// NOTE: this will cache and reuse action's mode, this
|
||||
// will make things faster when lots of actions use the
|
||||
// same mode test (alias)...
|
||||
var mode_cache = {}
|
||||
var getMode = function(action){
|
||||
var m = action
|
||||
var visited = [m]
|
||||
var last
|
||||
|
||||
// check cache...
|
||||
if(m in (mode_cache || {})){
|
||||
return mode_cache[m]
|
||||
}
|
||||
|
||||
// handle aliases...
|
||||
do {
|
||||
last = m
|
||||
m = actions.getActionAttr(m, 'browseMode')
|
||||
|
||||
// check cache...
|
||||
if(m in (mode_cache || {})){
|
||||
return mode_cache[m]
|
||||
}
|
||||
|
||||
// check for loops...
|
||||
if(m && visited[m] != null){
|
||||
m = null
|
||||
break
|
||||
}
|
||||
visited.push(m)
|
||||
} while(typeof(m) == typeof('str'))
|
||||
|
||||
//return m ? m.call(actions) : undefined
|
||||
return m ?
|
||||
// no cache...
|
||||
(mode_cache == null ?
|
||||
m.call(actions)
|
||||
// cache hit...
|
||||
: last in mode_cache ?
|
||||
mode_cache[last]
|
||||
// call check and populate cache...
|
||||
: (mode_cache[action] =
|
||||
mode_cache[last] =
|
||||
m.call(actions)))
|
||||
: undefined
|
||||
}
|
||||
return actions.getActionMode(action, mode_cache) }
|
||||
|
||||
// Wait for dialog...
|
||||
var waitFor = function(dialog, child){
|
||||
@ -2944,7 +2904,7 @@ var BrowseActionsActions = actions.Actions({
|
||||
false
|
||||
// hide dirs containing only (statically)
|
||||
// hidden items...
|
||||
// NOTE: we are not checking browseMode
|
||||
// NOTE: we are not checking mode
|
||||
// of other items actively here at
|
||||
// this point to avoid side-effects...
|
||||
: Object.keys(cur[key])
|
||||
@ -2988,7 +2948,7 @@ var BrowseActionsActions = actions.Actions({
|
||||
['off', 'on'])],
|
||||
|
||||
toggleBrowseActionKeys: ['Interface/Show keys in menu',
|
||||
{browseMode: function(){
|
||||
{mode: function(){
|
||||
return this.config['browse-advanced-mode'] != 'on' && 'hidden' }},
|
||||
core.makeConfigToggler(
|
||||
'browse-actions-keys',
|
||||
@ -3034,7 +2994,7 @@ module.ContextActionMenu = core.ImageGridFeatures.Feature({
|
||||
|
||||
actions: actions.Actions({
|
||||
showContextMenu: ['Interface/Show context menu...',
|
||||
{browseMode: 'toggleBrowseActionKeys'},
|
||||
{mode: 'toggleBrowseActionKeys'},
|
||||
uiDialog(function(){
|
||||
return this.current ?
|
||||
this.browseActions('/Image/')
|
||||
@ -3193,13 +3153,13 @@ var ButtonsActions = actions.Actions({
|
||||
},
|
||||
|
||||
toggleMainButtons: ['Interface/Main buttons',
|
||||
{browseMode: 'toggleBrowseActionKeys'},
|
||||
{mode: 'toggleBrowseActionKeys'},
|
||||
makeButtonControlsToggler('main-buttons')],
|
||||
toggleSecondaryButtons: ['Interface/Secondary buttons',
|
||||
{browseMode: 'toggleBrowseActionKeys'},
|
||||
{mode: 'toggleBrowseActionKeys'},
|
||||
makeButtonControlsToggler('secondary-buttons')],
|
||||
toggleAppButtons: ['Interface/App buttons',
|
||||
{browseMode: 'toggleBrowseActionKeys'},
|
||||
{mode: 'toggleBrowseActionKeys'},
|
||||
makeButtonControlsToggler('app-buttons')],
|
||||
|
||||
toggleSideButtons: ['Interface/70: Touch buttons',
|
||||
@ -3216,7 +3176,7 @@ var ButtonsActions = actions.Actions({
|
||||
})()],
|
||||
|
||||
toggleButtonHighlightColor: ['Interface/Theme/Button highlight color',
|
||||
{browseMode: 'toggleBrowseActionKeys'},
|
||||
{mode: 'toggleBrowseActionKeys'},
|
||||
core.makeConfigToggler(
|
||||
'button-highlight-color',
|
||||
function(){ return this.config['button-highlight-colors'] },
|
||||
|
||||
@ -361,7 +361,7 @@ module.ViewerActions = actions.Actions({
|
||||
.centerImage(target)
|
||||
.centerRibbon(target) }],
|
||||
alignRibbons: ['Interface/Align ribbons',
|
||||
{browseMode: 'toggleBrowseActionKeys'},
|
||||
{mode: 'toggleBrowseActionKeys'},
|
||||
function(target, scale, now){
|
||||
if(target == 'now'){
|
||||
now = true
|
||||
@ -392,7 +392,7 @@ module.ViewerActions = actions.Actions({
|
||||
// XXX skip off-screen ribbons (???)
|
||||
// XXX should the timeout be configurable???
|
||||
alignByOrder: ['Interface/Align ribbons by image order',
|
||||
{browseMode: 'toggleBrowseActionKeys'},
|
||||
{mode: 'toggleBrowseActionKeys'},
|
||||
function(target, scale, now){
|
||||
if(target == 'now'){
|
||||
now = true
|
||||
@ -457,7 +457,7 @@ module.ViewerActions = actions.Actions({
|
||||
}
|
||||
}],
|
||||
alignByFirst: ['Interface/Align ribbons except current to first image',
|
||||
{browseMode: 'toggleBrowseActionKeys'},
|
||||
{mode: 'toggleBrowseActionKeys'},
|
||||
function(target){
|
||||
target = target == 'now' ? null : target
|
||||
|
||||
@ -591,7 +591,7 @@ module.ViewerActions = actions.Actions({
|
||||
// Zooming is done by multiplying the current scale by .config['zoom-step']
|
||||
// and rounding to nearest discrete number of images to fit on screen.
|
||||
zoomIn: ['Zoom/Zoom in',
|
||||
{browseMode: function(){
|
||||
{mode: function(){
|
||||
return Math.min(this.screenwidth, this.screenheight) <= 1 && 'disabled' }},
|
||||
function(){
|
||||
var d = (this.config['zoom-step'] || 1.2)
|
||||
@ -606,7 +606,7 @@ module.ViewerActions = actions.Actions({
|
||||
}
|
||||
}],
|
||||
zoomOut: ['Zoom/Zoom out',
|
||||
{browseMode: function(){
|
||||
{mode: function(){
|
||||
return this.screenwidth >= this.config['max-screen-images'] && 'disabled' }},
|
||||
function(){
|
||||
var max = this.config['max-screen-images']
|
||||
@ -647,7 +647,7 @@ module.ViewerActions = actions.Actions({
|
||||
this.ribbonRotation('-='+ (a || this.config['ribbon-rotation-step'] || 10)) }],
|
||||
|
||||
resetRibbonRotation: ['Interface|Ribbon/Reset ribbon rotation',
|
||||
{browseMode: function(){
|
||||
{mode: function(){
|
||||
return this.ribbonRotation() == 0 && 'disabled' }},
|
||||
function(){ this.ribbonRotation(0) }],
|
||||
|
||||
@ -656,7 +656,7 @@ module.ViewerActions = actions.Actions({
|
||||
// XXX make this play nice with crops...
|
||||
// ...should this be a crop???
|
||||
toggleRibbonList: ['Interface|Ribbon/Ribbons as images view',
|
||||
{browseMode: 'toggleBrowseActionKeys'},
|
||||
{mode: 'toggleBrowseActionKeys'},
|
||||
function(){
|
||||
if(this._full_data == null){
|
||||
// XXX do a better name here...
|
||||
@ -697,7 +697,7 @@ module.ViewerActions = actions.Actions({
|
||||
function(angle){ }],
|
||||
|
||||
toggleImageRendering: ['Interface/Image rendering',
|
||||
{browseMode: 'toggleBrowseActionKeys'},
|
||||
{mode: 'toggleBrowseActionKeys'},
|
||||
toggler.CSSClassToggler(
|
||||
function(){ return this.dom },
|
||||
['crisp-resize', 'default-resize'],
|
||||
@ -936,7 +936,7 @@ module.Cursor = core.ImageGridFeatures.Feature({
|
||||
|
||||
actions: actions.Actions({
|
||||
toggleHiddenCursor: ['Interface/Cursor hidden',
|
||||
{browseMode: 'toggleBrowseActionKeys'},
|
||||
{mode: 'toggleBrowseActionKeys'},
|
||||
toggler.CSSClassToggler(
|
||||
function(){ return this.dom },
|
||||
'cursor-hidden',
|
||||
@ -1011,7 +1011,7 @@ module.Cursor = core.ImageGridFeatures.Feature({
|
||||
// .config['cursor-show-threshold']
|
||||
//
|
||||
toggleAutoHideCursor: ['Interface/Cursor auto-hide',
|
||||
{browseMode: 'toggleBrowseActionKeys'},
|
||||
{mode: 'toggleBrowseActionKeys'},
|
||||
toggler.CSSClassToggler(
|
||||
function(){ return this.dom },
|
||||
'cursor-autohide',
|
||||
@ -1127,13 +1127,13 @@ module.Cursor = core.ImageGridFeatures.Feature({
|
||||
})],
|
||||
|
||||
toggleAutoHideCursorTimeout: ['Interface/Hide cursor on timeout',
|
||||
{browseMode: 'toggleBrowseActionKeys'},
|
||||
{mode: 'toggleBrowseActionKeys'},
|
||||
core.makeConfigToggler('cursor-autohide-on-timeout',
|
||||
['on', 'off'],
|
||||
function(){
|
||||
this.toggleAutoHideCursor('!') })],
|
||||
toggleAutoHideCursorKeyboard: ['Interface/Hide cursor on keyboard',
|
||||
{browseMode: 'toggleBrowseActionKeys'},
|
||||
{mode: 'toggleBrowseActionKeys'},
|
||||
core.makeConfigToggler('cursor-autohide-on-keyboard',
|
||||
['on', 'off'],
|
||||
function(){
|
||||
@ -1224,7 +1224,7 @@ var ControlActions = actions.Actions({
|
||||
from reaching the viewer.
|
||||
|
||||
NOTE: this defines the focus/blur handlers on the window object.`,
|
||||
{browseMode: 'toggleBrowseActionKeys'},
|
||||
{mode: 'toggleBrowseActionKeys'},
|
||||
core.makeConfigToggler('lock-unfocused',
|
||||
['off', 'on'],
|
||||
function(state){
|
||||
@ -1378,7 +1378,7 @@ var ControlActions = actions.Actions({
|
||||
// XXX depends on .ribbons...
|
||||
// XXX uses: .focusImage(..)
|
||||
toggleImageClickHandling: ['Interface/Image click handling',
|
||||
{browseMode: 'toggleBrowseActionKeys'},
|
||||
{mode: 'toggleBrowseActionKeys'},
|
||||
toggler.Toggler(null,
|
||||
function(_, new_state){
|
||||
return new_state ?
|
||||
@ -1631,7 +1631,7 @@ var ControlActions = actions.Actions({
|
||||
// XXX depends on .ribbons...
|
||||
// XXX uses: .focusImage(..)
|
||||
toggleRibbonPanHandling: ['Interface/Ribbon pan handling',
|
||||
{browseMode: 'toggleBrowseActionKeys'},
|
||||
{mode: 'toggleBrowseActionKeys'},
|
||||
toggler.Toggler(null,
|
||||
function(_, new_state){
|
||||
return new_state ?
|
||||
@ -1871,7 +1871,7 @@ var ControlActions = actions.Actions({
|
||||
// XXX depends on .ribbons...
|
||||
// XXX uses: .focusImage(..)
|
||||
toggleMouseWheelHandling: ['Interface/Mouse wheel handling',
|
||||
{browseMode: 'toggleBrowseActionKeys'},
|
||||
{mode: 'toggleBrowseActionKeys'},
|
||||
toggler.Toggler(null,
|
||||
function(_, new_state){
|
||||
return new_state ?
|
||||
@ -1976,7 +1976,7 @@ var ControlActions = actions.Actions({
|
||||
})],
|
||||
|
||||
togglePinchHandling: ['Interface/Pinch zoom handling',
|
||||
{browseMode: 'toggleBrowseActionKeys'},
|
||||
{mode: 'toggleBrowseActionKeys'},
|
||||
function(){
|
||||
// XXX
|
||||
}],
|
||||
@ -1986,7 +1986,7 @@ var ControlActions = actions.Actions({
|
||||
// ...allow ui features to control this...
|
||||
// XXX depends on .ribbons...
|
||||
toggleSwipeHandling: ['Interface/Swipe handling',
|
||||
//{browseMode: 'toggleBrowseActionKeys'},
|
||||
//{mode: 'toggleBrowseActionKeys'},
|
||||
toggler.Toggler(null,
|
||||
function(_, state){
|
||||
return state ?
|
||||
|
||||
@ -98,7 +98,7 @@ var VirtualBlocksActions = actions.Actions({
|
||||
...
|
||||
}
|
||||
`,
|
||||
{ browseMode: function(){ return !this.collection && 'disabled' }, },
|
||||
{ mode: function(){ return !this.collection && 'disabled' }, },
|
||||
function(ref, offset, img){
|
||||
ref = ref || 'current'
|
||||
offset = offset || 'after'
|
||||
@ -153,11 +153,11 @@ var VirtualBlocksActions = actions.Actions({
|
||||
core.doc`Add block before...
|
||||
|
||||
This is the same as .makeVirtualBlock(.., 'before', ..)`,
|
||||
{ browseMode: 'makeVirtualBlock', },
|
||||
{ mode: 'makeVirtualBlock', },
|
||||
'makeVirtualBlock: $0 "before" ...'],
|
||||
|
||||
cloneVirtualBlock: ['Virtual block/$Clone block',
|
||||
{ browseMode: function(){
|
||||
{ mode: function(){
|
||||
return (this.image || {}).type != 'virtual' && 'disabled' }, },
|
||||
function(ref, offset, img){
|
||||
var img = Object.assign({},
|
||||
@ -169,15 +169,15 @@ var VirtualBlocksActions = actions.Actions({
|
||||
// crop...
|
||||
// XXX would be nice to avoid these and just register a list and context...
|
||||
cropVirtualBlocks: ['Virtual block|Crop/$Crop $virtual $blocks',
|
||||
{ browseMode: 'makeVirtualBlock' },
|
||||
{ mode: 'makeVirtualBlock' },
|
||||
'crop: "virtual" ...'],
|
||||
cropVirtualBlocksOut: ['Virtual block|Crop/Crop virtual blocks out',
|
||||
{ browseMode: 'cropVirtualBlocks' },
|
||||
{ mode: 'cropVirtualBlocks' },
|
||||
'crop: "!virtual" ...'],
|
||||
|
||||
// marks...
|
||||
toggleMarkVirtualBlocks: ['Virtual block|Mark/Toggle $mark on $virtual blocks',
|
||||
{ browseMode: 'makeVirtualBlock' },
|
||||
{ mode: 'makeVirtualBlock' },
|
||||
'toggleMark: "virtual"'],
|
||||
|
||||
// remove...
|
||||
@ -358,7 +358,7 @@ var VirtualBlocksEditUIActions = actions.Actions({
|
||||
// XXX should we also add a preview (preview constructor from features/metadata.js)???
|
||||
// XXX should we do a sanity check for image type???
|
||||
editVirtualBlock: ['Virtual block/$Edit...',
|
||||
{ browseMode: 'cloneVirtualBlock' },
|
||||
{ mode: 'cloneVirtualBlock' },
|
||||
widgets.makeUIDialog(function(gid){
|
||||
var that = this
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user