refactoring + work on kb editors...

Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
Alex A. Naanou 2017-01-15 00:49:19 +03:00
parent f78c56a8c5
commit 5050537966
6 changed files with 350 additions and 216 deletions

View File

@ -241,6 +241,26 @@ body {
display: none; display: none;
} }
/* new item... */
.browse-widget .list>.action {
margin-top: 0.2em;
border-top: solid 1px rgba(255,255,255, 0.2);
}
.browse-widget .list>.action .text {
font-style: italic;
}
/* do not show top border if after another action or separator... */
.browse-widget .list>.action+.action,
.browse-widget .list>.separator+.action {
border-top: none;
}
/* do not show top border if after another action or separator... */
.browse-widget .list>.warn {
background-color: yellow !important;
color: red !important;
font-weight: bolder !important;
}
/* Dialog highlight experiment... */ /* Dialog highlight experiment... */

View File

@ -594,21 +594,7 @@ var URLHistoryUIActions = actions.Actions({
o.redraw() o.redraw()
}], }],
// mark for removal... // mark for removal...
['&times;', widgets.makeRemoveItemButton(to_remove)
function(p, cur){
cur.toggleClass('strike-out')
if(cur.hasClass('strike-out')){
to_remove.indexOf(p) < 0
&& to_remove.push(p)
} else {
var i = to_remove.indexOf(p)
if(i >= 0){
to_remove.splice(i, 1)
}
}
}],
], ],
}) })
.open(function(evt, path){ .open(function(evt, path){

View File

@ -147,6 +147,8 @@ module.GLOBAL_KEYBOARD2 = {
'on this page.', 'on this page.',
pattern: '*', pattern: '*',
F1: 'browseActions: "/Help/" -- Help menu...',
alt_X: 'close', alt_X: 'close',
alt_F4: 'close', alt_F4: 'close',
meta_Q: 'close', meta_Q: 'close',
@ -176,7 +178,7 @@ module.GLOBAL_KEYBOARD2 = {
// XXX should this be all here or in respective sections??? // XXX should this be all here or in respective sections???
alt_A: 'browseActions', alt_A: 'browseActions',
//alt_S: 'browseActions: "/Sort/"', //alt_S: 'browseActions: "/Sort/" -- Sort menu...',
alt_shift_A: 'listActions', alt_shift_A: 'listActions',
@ -201,7 +203,7 @@ module.GLOBAL_KEYBOARD2 = {
ctrl_Z: 'undo', ctrl_Z: 'undo',
shift_U: 'redo', shift_U: 'redo',
ctrl_shift_Z: 'redo', ctrl_shift_Z: 'redo',
alt_H: 'browseActions: "/History/" -- Open history menu', alt_H: 'browseActions: "/History/" -- History menu...',
// tilt... // tilt...
@ -293,8 +295,8 @@ module.GLOBAL_KEYBOARD2 = {
// ribbon image stuff... // ribbon image stuff...
alt_I: 'browseActions: "/Image/" -- Show image menu', alt_I: 'browseActions: "/Image/" -- Image menu...',
alt_R: 'browseActions: "/Ribbon/" -- Open ribbon menu', alt_R: 'browseActions: "/Ribbon/" -- Ribbon menu...',
// ranges... // ranges...
@ -332,7 +334,7 @@ module.GLOBAL_KEYBOARD2 = {
shift_F2: 'cropRibbonAndAbove', shift_F2: 'cropRibbonAndAbove',
ctrl_F2: 'cropMarked', ctrl_F2: 'cropMarked',
alt_F2: 'cropBookmarked', alt_F2: 'cropBookmarked',
C: 'browseActions: "/Crop/" -- Show crop menu', C: 'browseActions: "/Crop/" -- Crop menu...',
// metadata... // metadata...
@ -347,14 +349,14 @@ module.GLOBAL_KEYBOARD2 = {
ctrl_I: 'toggleMark!: "ribbon" -- Invert marks in ribbon', ctrl_I: 'toggleMark!: "ribbon" -- Invert marks in ribbon',
',': 'prevMarked', ',': 'prevMarked',
'.': 'nextMarked', '.': 'nextMarked',
alt_M: 'browseActions: "/Mark/" -- Show mark menu', alt_M: 'browseActions: "/Mark/" -- Mark menu...',
// bookmarking... // bookmarking...
B: 'toggleBookmark', B: 'toggleBookmark',
'[': 'prevBookmarked', '[': 'prevBookmarked',
']': 'nextBookmarked', ']': 'nextBookmarked',
alt_B: 'browseActions: "/Bookmark/" -- Show bookmark menu', alt_B: 'browseActions: "/Bookmark/" -- Bookmark menu...',
@ -425,6 +427,9 @@ var KeyboardActions = actions.Actions({
// for changes to take effect. // for changes to take effect.
// XXX EXPERIMENTAL // XXX EXPERIMENTAL
'keyboard-key-pressed-action': 'off', 'keyboard-key-pressed-action': 'off',
// XXX make this generic...
'confirm-delete-timeout': 2000,
}, },
get keybindings(){ get keybindings(){
@ -717,11 +722,27 @@ var KeyboardActions = actions.Actions({
// Interface stuff ------------------------------------------------ // Interface stuff ------------------------------------------------
// options format:
// {
// cls: 'edit',
// show_non_actions: true,
// empty_section_text: false,
//
// mode_buttons:
// mode_actions:
//
// key_buttons:
//
// drop_buttons:
//
// }
//
// XXX BUG sections with doc do not show up in title... // XXX BUG sections with doc do not show up in title...
// XXX sub-group by path (???) // XXX sub-group by path (???)
// XXX place this in /Doc/.. (???)
browseKeyboardBindings: ['Interface|Help/Keyboard bindings...', browseKeyboardBindings: ['Interface|Help/Keyboard bindings...',
widgets.makeUIDialog(function(path, edit, get_text){ widgets.makeUIDialog(function(path, options){
options = options || {}
var actions = this var actions = this
var keybindings = this.keybindings var keybindings = this.keybindings
var kb = this.keyboard var kb = this.keyboard
@ -729,14 +750,13 @@ var KeyboardActions = actions.Actions({
var keys = kb.keys('*') var keys = kb.keys('*')
// get doc... // get doc...
get_text = get_text === undefined && !edit ? var getKeyText = options.get_key_text
function(action){ || function(action){
var doc = action.doc ? action.doc var doc = action.doc ? action.doc
: action.action in this ? this.getDocTitle(action.action) : action.action in this ? this.getDocTitle(action.action)
: action.action : action.action
return doc.length == 0 ? action.action : doc return doc.length == 0 ? action.action : doc
} }
: get_text
var dialog = browse.makeLister(null, var dialog = browse.makeLister(null,
function(path, make){ function(path, make){
@ -744,16 +764,8 @@ var KeyboardActions = actions.Actions({
.forEach(function(mode){ .forEach(function(mode){
var dropped = keybindings[mode].drop || [] var dropped = keybindings[mode].drop || []
var bound_ignored = [] var bound_ignored = []
var buttons = edit ?
[
// XXX up
['&#9206;', function(){}],
// XXX down
['&#9207;', function(){}],
].concat(dialog.options.itemButtons)
: undefined
// section heading... // section heading (mode)...
make(keybindings[mode].doc ? make(keybindings[mode].doc ?
$('<span>') $('<span>')
// NOTE: at this time adding a br // NOTE: at this time adding a br
@ -769,7 +781,7 @@ var KeyboardActions = actions.Actions({
not_filtered_out: true, not_filtered_out: true,
// XXX should sections be searchable??? // XXX should sections be searchable???
not_searchable: true, not_searchable: true,
buttons: buttons, buttons: options.mode_buttons,
}) })
.attr('mode', mode) .attr('mode', mode)
.addClass('mode') .addClass('mode')
@ -780,9 +792,9 @@ var KeyboardActions = actions.Actions({
var o = keyboard.parseActionCall(action) var o = keyboard.parseActionCall(action)
if(get_text){ if(getKeyText){
var doc = '' var doc = ''
var text = get_text.call(actions, o) var text = getKeyText.call(actions, o)
} else { } else {
var doc = o.doc var doc = o.doc
@ -792,9 +804,13 @@ var KeyboardActions = actions.Actions({
(': '+ o.arguments.map(JSON.stringify).join(' ')) (': '+ o.arguments.map(JSON.stringify).join(' '))
: '') : '')
} }
var hidden = !edit
&& !(o.action in actions) var hidden = !options.show_non_actions
&& !(kb.handler(mode, keys[mode][action][0]) // hide all non-actions...
&& !(o.action in actions
// except: functions represented by their doc...
|| keybindings[mode][action] == null
&& kb.handler(mode, keys[mode][action][0])
instanceof Function) instanceof Function)
// NOTE: wee need the button spec to be // NOTE: wee need the button spec to be
@ -820,6 +836,8 @@ var KeyboardActions = actions.Actions({
// hide stuff that is not an action... // hide stuff that is not an action...
hidden: hidden, hidden: hidden,
disabled: hidden, disabled: hidden,
buttons: options.key_buttons,
}) })
.attr({ .attr({
'mode': mode, 'mode': mode,
@ -842,8 +860,8 @@ var KeyboardActions = actions.Actions({
// no keys in view mode... // no keys in view mode...
// XXX is adding info stuff like this a correct // XXX is adding info stuff like this a correct
// thing to do in code? // thing to do in code?
c == 0 && !edit c == 0 && options.empty_section_text !== false
&& make('No bindings...', && make(options.empty_section_text || 'No bindings...',
{ {
disabled: true, disabled: true,
hide_on_search: true, hide_on_search: true,
@ -860,30 +878,18 @@ var KeyboardActions = actions.Actions({
dropped dropped
.filter(function(k){ .filter(function(k){
return bound_ignored.indexOf(k) == -1 }) return bound_ignored.indexOf(k) == -1 })
.join(' / ')]) .join(' / ')],
{
buttons: options.drop_buttons,
})
.addClass('drop-list') .addClass('drop-list')
.attr('mode', mode) .attr('mode', mode)
// controls... // controls...
if(edit){ if(options.mode_actions){
var elem = make('new', { var elem = make('new', {
buttons: [ buttons: options.mode_actions,
// XXX })
['key',
function(){
//elem.before( XXX )
actions.editKeyBinding(mode)
// XXX update when done???
}],
// XXX
['mode',
function(){
//elem.after( XXX )
// XXX need to pass order info...
actions.editKeyboardMode()
// XXX update when done???
}],
]})
.addClass('new') .addClass('new')
} }
}) })
@ -900,40 +906,13 @@ var KeyboardActions = actions.Actions({
hide_on_search: true, hide_on_search: true,
}) })
.addClass('info') .addClass('info')
}, { },
{
cls: [ cls: [
'key-bindings', 'key-bindings',
'no-item-numbers', 'no-item-numbers',
(edit ? 'edit' : 'browse'), options.cls,
].join(' '), ].join(' '),
itemButtons: edit ?
[
// NOTE: ordering within one section is purely
// aesthetic and has no function...
// XXX do wee actually need ordering???
// XXX up
//['&#9206;', function(){}],
// XXX down
//['&#9207;', function(){}],
// XXX edit -- launch the editor...
// ...do we actually need this as a button????
['&ctdot;', function(_, cur){
// key...
if(cur.hasClass('key')){
actions.editKeyBinding(cur.attr('mode'), cur.attr('code'))
// mode...
} else if(cur.hasClass('mode')){
actions.editKeyboardMode(cur.attr('mode'))
}
}],
//*/
//['edit', function(){}],
//['&#128393;', function(){}],
]
: [],
}) })
return dialog return dialog
@ -942,24 +921,77 @@ var KeyboardActions = actions.Actions({
editKeyboardBindings: ['Interface/Keyboard bindings editor...', editKeyboardBindings: ['Interface/Keyboard bindings editor...',
widgets.uiDialog(function(path){ widgets.uiDialog(function(path){
var that = this var that = this
var dialog = this.browseKeyboardBindings(path, true) var dialog = this.browseKeyboardBindings(
path,
{
cls: 'edit',
show_non_actions: true,
empty_section_text: false,
// mode...
mode_buttons: [
// XXX up
['&#9206;', function(_, cur){
}],
// XXX down
['&#9207;', function(_, cur){
}],
['&ctdot;', function(_, cur){
that.editKeyboardMode(cur.attr('mode'))
.close(function(){ dialog.update() }) }],
],
mode_actions: [
['key', function(_, cur){
//elem.before( XXX )
that.editKeyBinding(cur.attr('mode'))
.close(function(){ dialog.update() }) }],
['mode', function(_, cur){
//elem.after( XXX )
// XXX need to pass order info...
that.editKeyboardMode()
.close(function(){ dialog.update() }) }],
],
// keys...
key_buttons: [
['&ctdot;', function(_, cur){
that.editKeyBinding(cur.attr('mode'), cur.attr('code'))
.close(function(){ dialog.update() }) }],
],
// dropped key list...
drop_buttons: [
['&ctdot;', function(_, cur){
that.editKeyboardModeDroppedKeys(cur.attr('mode'))
.close(function(){ dialog.update() }) }],
],
})
// XXX should this be only a button thing (done in .browseKeyboardBindings(..)) // XXX should this be only a button thing (done in .browseKeyboardBindings(..))
// or also the main action??? // or also the main action???
.open(function(){ .open(function(){
var cur = dialog.select('!') var cur = dialog.select('!')
var sub_dialog
// key... // key...
if(cur.hasClass('key')){ if(cur.hasClass('key')){
that.editKeyBinding(cur.attr('mode'), cur.attr('code')) sub_dialog = that
.editKeyBinding(cur.attr('mode'), cur.attr('code'))
// mode... // mode...
// XXX BUG: for some reason modes are unclickable... // XXX BUG: for some reason modes are unclickable...
} else if(cur.hasClass('mode')){ } else if(cur.hasClass('mode')){
that.editKeyboardMode(cur.attr('mode')) sub_dialog = that
.editKeyboardMode(cur.attr('mode'))
// dropped...
} else if(cur.hasClass('drop-list')){ } else if(cur.hasClass('drop-list')){
that.editKeyboardModeDroppedKeys(cur.attr('mode')) sub_dialog = that
.editKeyboardModeDroppedKeys(cur.attr('mode'))
} }
sub_dialog
&& sub_dialog
.close(function(){ dialog.update() })
}) })
return dialog return dialog
})], })],
@ -970,8 +1002,8 @@ var KeyboardActions = actions.Actions({
// --- // ---
// <list of keys> // <list of keys>
// new key // new key
// XXX // XXX need a way to abort edits...
editKeyBinding: ['- Interface/Key binding editor...', editKeyBinding: ['- Interface/Key mapping...',
widgets.makeUIDialog(function(mode, code){ widgets.makeUIDialog(function(mode, code){
var that = this var that = this
// XXX // XXX
@ -990,77 +1022,83 @@ var KeyboardActions = actions.Actions({
keys[mode][code] keys[mode][code]
: [] : []
var to_remove = []
keys keys
.forEach(function(key){ .forEach(function(key){
// XXX make editable... // XXX make editable...
make(key, { buttons: [ make(key, { buttons: [
['&times;', function(){}], widgets.makeRemoveItemButton(to_remove),
], }) ], })
}) })
make('New key') var new_button = make('New key')
// XXX stub... .addClass('action')
.css({ fontStyle: 'italic' }) .on('open', function(){
widgets.editItem(dialog, new_button)
})
make('---') make('---')
make('', { buttons: [ widgets.makeConfirmActionItem(make('Delete'),
['Delete mapping', function(){}], function(){
], }) // XXX
dialog.close()
}, that.config['confirm-delete-timeout'] || 2000)
},
{
cls: 'metadata-view',
}) })
return dialog return dialog
})], })],
// XXX // XXX make fields editable...
editKeyboardMode: ['- Interface/keyboard mode editor...', // XXX need a way to abort edits...
widgets.makeUIDialog(function(mode){ editKeyboardMode: ['- Interface/Mode...',
var that = this
var dialog = browse.makeLister(null,
function(path, make){
make(['Mode:', mode || ''])
make(['Doc:', that.keybindings[mode].doc || ''])
make(['Pattern:', that.keybindings[mode].pattern || mode])
make('---')
make('', { buttons: [
['Delete mode', function(){}],
], })
})
return dialog
})],
// XXX
editKeyboardModeDroppedKeys: ['- Interface/keyboard mode dropped key editor...',
widgets.makeUIDialog(function(mode){ widgets.makeUIDialog(function(mode){
var that = this var that = this
var dialog = browse.makeLister(null, var dialog = browse.makeLister(null,
function(path, make){ function(path, make){
// XXX make these editable....
make(['Mode:', mode || '']) make(['Mode:', mode || ''])
make(['Doc:', (that.keybindings[mode] || {}).doc || ''])
make(['Pattern:', (that.keybindings[mode] || {}).pattern || mode])
make('---') make('---')
var drop = that.keybindings[mode].drop || [] widgets.makeConfirmActionItem(make('Delete'),
drop = drop == '*' ? [drop] : drop function(){
if(mode in that.keybindings){
drop delete that.keybindings[mode]
.forEach(function(key){ }
// XXX make editable... dialog.close()
make(key, { buttons: [ }, that.config['confirm-delete-timeout'] || 2000)
['&times;', function(){}], },
], }) {
cls: 'metadata-view',
}) })
make('New key') return dialog
// XXX stub... })],
.css({ fontStyle: 'italic' }) // XXX need a way to abort edits...
// XXX need a way to set a special '*' key...
editKeyboardModeDroppedKeys: ['- Interface/Dropped keys...',
widgets.makeUIDialog(function(mode){
var that = this
make('---') // XXX need a way to set a special '*' key...
var dialog = widgets.makeListEditor(function(keys){
// get...
if(keys === undefined){
return that.keybindings[mode].drop || []
make('', { buttons: [ // set...
['Clear dropped keys', function(){}], } else {
], }) that.keybindings[mode].drop = keys
}
},
{
unique: true,
}) })
return dialog return dialog

View File

@ -148,15 +148,15 @@ var SlideshowActions = actions.Actions({
o.parent.close() o.parent.close()
}) })
.addClass('selected') .addClass('selected')
},
{
cls: 'metadata-view tail-action',
}) })
.on('close', function(){ .on('close', function(){
// reset the timer if it was not suspended outside... // reset the timer if it was not suspended outside...
suspended_timer || that.resetSlideshowTimer() suspended_timer || that.resetSlideshowTimer()
}) })
o.dom
.addClass('metadata-view tail-action')
return o return o
})], })],

View File

@ -144,12 +144,16 @@ function(cls, cfg, parent){
// XXX make the selector more accurate... // XXX make the selector more accurate...
// ...at this point this will select the first elem with text which // ...at this point this will select the first elem with text which
// can be a different elem... // can be a different elem...
var makeEditableItem = var editItem =
module.makeEditableItem = module.editItem =
function(list, item, elem, callback, options){ function(list, elem, callback, options){
return elem return elem
.makeEditable({ .makeEditable(options
|| {
activate: true, activate: true,
clear_on_edit: true,
blur_on_abort: false,
blur_on_commit: false,
}) })
.on('edit-done', callback || function(){}) .on('edit-done', callback || function(){})
.on('edit-aborted edit-done', function(_, text){ .on('edit-aborted edit-done', function(_, text){
@ -157,11 +161,75 @@ function(list, item, elem, callback, options){
// XXX make the selector more accurate... // XXX make the selector more accurate...
// ...at this point this will select the first elem // ...at this point this will select the first elem
// with text which can be a different elem... // with text which can be a different elem...
.then(function(){ list.select(item.text()) }) .then(function(){ list.select(elem.text()) })
}) })
} }
var makeRemoveItemButton =
module.makeRemoveItemButton =
function makeRemoveItemButton(list, html){
return [html || '&times;',
function(p, e){
e.toggleClass('strike-out')
if(e.hasClass('strike-out')){
list.indexOf(p) < 0
&& list.push(p)
} else {
var i = list.indexOf(p)
if(i >= 0){
list.splice(i, 1)
}
}
}]
}
var makeConfirmActionItem =
module.makeConfirmActionItem =
function makeConfirmActionItem(elem, callback, timeout, confirm_text){
confirm_text = confirm_text ?
confirm_text
: 'Confirm '+ elem.text().toLowerCase() +'?'
var text
return elem
.addClass('action')
.on('open', function(){
var item = $(this)
var elem = item.find('.text')
// ready to delete...
if(elem.text() != confirm_text){
text = elem.text()
elem.text(confirm_text)
item.addClass('warn')
// reset...
setTimeout(function(){
elem.text(text)
item.removeClass('warn')
}, timeout || 2000)
// confirmed...
} else {
callback && callback()
}
})
}
var makeNewEditableItem =
module.makeNewEditableItem =
function makeNewEditableItem(elem){
}
// //
// Options format: // Options format:
// { // {
@ -190,15 +258,15 @@ function(list, item, elem, callback, options){
// } // }
// //
// XXX add sort buttons: up/down/top/bottom... // XXX add sort buttons: up/down/top/bottom...
// XXX make this more generic...
// XXX currently using this also requires the use of makeUIDialog(..), // XXX currently using this also requires the use of makeUIDialog(..),
// can this be simpler??? // can this be simpler???
var makeConfigListEditor = // XXX this is generic, move to browse...
module.makeConfigListEditor = var makeListEditor =
function(actions, list_key, options){ module.makeListEditor =
function(list, options){
options = options || {} options = options || {}
var new_button = options.new_button var new_button = options.new_button || true
new_button = new_button === true ? 'New...' : new_button new_button = new_button === true ? 'New...' : new_button
var _makeEditable = function(elem){ var _makeEditable = function(elem){
@ -210,15 +278,15 @@ function(actions, list_key, options){
blur_on_commit: false, blur_on_commit: false,
}) })
.on('edit-aborted', function(){ .on('edit-aborted', function(){
list.select(null) dialog.select(null)
list.update() dialog.update()
}) })
.on('edit-done', function(evt, text){ .on('edit-done', function(evt, text){
var txt = $(this).text() var txt = $(this).text()
// invalid format... // invalid format...
if(options.check && !options.check(txt)){ if(options.check && !options.check(txt)){
list.update() dialog.update()
return return
} }
@ -226,83 +294,76 @@ function(actions, list_key, options){
if(options.length_limit if(options.length_limit
&& (lst.length >= options.length_limit)){ && (lst.length >= options.length_limit)){
options.callback && options.callback.call(list, txt) options.callback && options.callback.call(dialog, txt)
return return
} }
// prevent editing non-arrays... // prevent editing non-arrays...
if(!(actions.config[list_key] instanceof Array)){ if(!editable || !lst){
return return
} }
// save the new version...
actions.config[list_key] = actions.config[list_key].slice()
// add new value and sort list... // add new value and sort list...
actions.config[list_key] lst.push(txt)
.push(txt)
// unique... // unique...
if(options.unique == null || options.unique === true){ if(options.unique == null || options.unique === true){
actions.config[list_key] = actions.config[list_key] lst = lst.unique()
.unique()
// unique normalized... // unique normalized...
} else if( typeof(options.unique) == typeof(function(){})){ } else if(typeof(options.unique) == typeof(function(){})){
actions.config[list_key] = actions.config[list_key] lst = lst.unique(options.unique)
.unique(options.unique)
} }
// sort... // sort...
if(options.sort){ if(options.sort){
actions.config[list_key] = actions.config[list_key] lst = lst
.sort(typeof(options.sort) == typeof(function(){}) ? .sort(typeof(options.sort) == typeof(function(){}) ?
options.sort options.sort
: undefined) : undefined)
} }
_write(lst)
// update the list data... // update the list data...
list.options.data dialog.options.data = lst.concat(new_button ? [ new_button ] : [])
= actions.config[list_key]
.concat(new_button ? [ new_button ] : [])
// update list and select new value... // update list and select new value...
list.update() dialog.update()
.done(function(){ .done(function(){
list.select('"'+txt+'"') dialog.select('"'+txt+'"')
}) })
}) })
} }
var _write = function(lst){
// write back the list...
return list instanceof Function ?
// call the writer...
list(lst)
// in-place replace list elements...
// NOTE: this is necessary as not everything we do with lst
// is in-place...
: list.splice.apply(list, [0, list.length].concat(lst))
}
var to_remove = [] var to_remove = []
var lst = list_key instanceof Function ? list_key() var lst = list instanceof Function ?
: list_key instanceof Array ? list_key list()
: actions.config[list_key] : list
lst = lst instanceof Array ? lst : Object.keys(lst) var editable = lst instanceof Array
var list = browse.makeList(null, // view objects...
lst = !editable ? Object.keys(lst) : lst.slice()
var dialog = browse.makeList(null,
lst.concat(new_button ? [ new_button ] : []), lst.concat(new_button ? [ new_button ] : []),
{ {
path: options.path, path: options.path,
itemButtons: options.itemButtons || [ itemButtons: options.itemButtons || [
// mark for removal... // mark for removal...
['&times;', makeRemoveItemButton(to_remove)
function(p){
var e = this.filter('"'+p+'"', false)
.toggleClass('strike-out')
if(e.hasClass('strike-out')){
to_remove.indexOf(p) < 0
&& to_remove.push(p)
} else {
var i = to_remove.indexOf(p)
if(i >= 0){
to_remove.splice(i, 1)
}
}
}],
// XXX add shift up/down/top/bottom and other buttons (optional)... // XXX add shift up/down/top/bottom and other buttons (optional)...
] ]
}) })
@ -315,43 +376,71 @@ function(actions, list_key, options){
// restore striked-out items... // restore striked-out items...
.on('update', function(){ .on('update', function(){
to_remove.forEach(function(e){ to_remove.forEach(function(e){
list.filter('"'+ e +'"') dialog.filter('"'+ e +'"')
.toggleClass('strike-out') .toggleClass('strike-out')
}) })
}) })
.open(function(evt, path){ .open(function(evt, path){
// we clicked the 'New' button -- select it... // we clicked the 'New' button -- select it...
if(new_button && (path == new_button || path == '')){ if(new_button && (path == new_button || path == '')){
list.select(new_button) dialog.select(new_button)
} else { } else {
options.callback && options.callback.call(list, path) options.callback && options.callback.call(dialog, path)
} }
}) })
.on('close', function(){ .on('close', function(){
// prevent editing non-arrays... // prevent editing non-arrays...
if(!(actions.config[list_key] instanceof Array)){ if(!editable){
return return
} }
// remove striked items... // remove striked items...
to_remove.forEach(function(e){ to_remove.forEach(function(e){
var lst = actions.config[list_key].slice()
lst.splice(lst.indexOf(e), 1) lst.splice(lst.indexOf(e), 1)
actions.config[list_key] = lst _write(lst)
}) })
// sort... // sort...
if(options.sort){ if(options.sort){
actions.config[list_key] = actions.config[list_key] lst.sort(options.sort !== true ? options.sort : undefined)
.sort(options.sort !== true ? options.sort : undefined)
_write(lst)
} }
}) })
new_button && list.dom.addClass('tail-action') // XXX
new_button && dialog.dom.addClass('tail-action')
return list return dialog
}
//---------------------------------------------------------------------
var makeConfigListEditor =
module.makeConfigListEditor =
function(actions, path, options){
path = path.split('.')
var key = path.pop()
return makeListEditor(function(lst){
var target = actions.config
path.forEach(function(p){
target = target[p] = target[p] || {}
})
// get...
if(lst === undefined){
return target[key]
// set...
} else {
target[key] = lst
}
}, options)
} }
@ -372,7 +461,7 @@ function(actions, list, list_key, value_key, options){
// NOTE: this is called when adding a new value and // NOTE: this is called when adding a new value and
// list maximum length is reached... // list maximum length is reached...
callback: function(value){ callback: function(value){
if(typeof(value_key) == typeof(function(){})){ if(value_key instanceof Function){
value_key(value) value_key(value)
} else { } else {
actions.config[value_key] = value actions.config[value_key] = value
@ -392,7 +481,7 @@ function(actions, list, list_key, value_key, options){
}) })
// select default... // select default...
o.on('update', function(){ o.on('update', function(){
if(typeof(value_key) == typeof(function(){})){ if(value_key instanceof Function){
o.select(value_key()) o.select(value_key())
} else { } else {

View File

@ -1131,6 +1131,7 @@ var BrowserPrototype = {
// custom buttons... // custom buttons...
buttons && buttons buttons && buttons
.slice()
// make the order consistent for the user -- first // make the order consistent for the user -- first
// in list, first in item (from left), and should // in list, first in item (from left), and should
// be added last... // be added last...