mirror of
https://github.com/flynx/ImageGrid.git
synced 2025-10-29 18:30:09 +00:00
split util.js into a generic and DOM modules...
Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
parent
98f1979146
commit
0548f0f9b3
383
ui (gen4)/lib/util-dom.js
Executable file
383
ui (gen4)/lib/util-dom.js
Executable file
@ -0,0 +1,383 @@
|
|||||||
|
/**********************************************************************
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
**********************************************************************/
|
||||||
|
((typeof define)[0]=='u'?function(f){module.exports=f(require)}:define)
|
||||||
|
(function(require){ var module={} // make module AMD/node compatible...
|
||||||
|
/*********************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*********************************************************************/
|
||||||
|
|
||||||
|
var selectElemText =
|
||||||
|
module.selectElemText =
|
||||||
|
function(elem){
|
||||||
|
var range = document.createRange()
|
||||||
|
range.selectNodeContents(elem)
|
||||||
|
var sel = window.getSelection()
|
||||||
|
sel.removeAllRanges()
|
||||||
|
sel.addRange(range)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// XXX make this global...
|
||||||
|
var getCaretOffset =
|
||||||
|
module.getCaretOffset =
|
||||||
|
function(elem){
|
||||||
|
var s = window.getSelection()
|
||||||
|
if(s.rangeCount == 0){
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
var r = s.getRangeAt(0)
|
||||||
|
var pre = r.cloneRange()
|
||||||
|
pre.selectNodeContents(elem)
|
||||||
|
pre.setEnd(r.endContainer, r.endOffset)
|
||||||
|
return pre.toString().length || 0
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var selectionCollapsed =
|
||||||
|
module.selectionCollapsed =
|
||||||
|
function(elem){
|
||||||
|
var s = window.getSelection()
|
||||||
|
if(s.rangeCount == 0){
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return s.getRangeAt(0).cloneRange().collapsed
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------
|
||||||
|
// XXX experiment
|
||||||
|
if(typeof(jQuery) != typeof(undefined)){
|
||||||
|
jQuery.fn._drag = function(){
|
||||||
|
var dragging = false
|
||||||
|
var s,
|
||||||
|
px, py
|
||||||
|
|
||||||
|
var elem = $(this)
|
||||||
|
.on('mousedown touchstart', function(evt){
|
||||||
|
dragging = true
|
||||||
|
px = evt.clientX
|
||||||
|
px = evt.clientY
|
||||||
|
|
||||||
|
s = elem.rscale()
|
||||||
|
})
|
||||||
|
.on('mousemove touchmove', function(evt){
|
||||||
|
if(!dragging){
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var x = evt.clientX
|
||||||
|
var dx = px - x
|
||||||
|
px = x
|
||||||
|
|
||||||
|
var y = evt.clientY
|
||||||
|
var dy = py - y
|
||||||
|
py = y
|
||||||
|
|
||||||
|
elem
|
||||||
|
.velocity('stop')
|
||||||
|
.velocity({
|
||||||
|
translateX: '-=' + (dx / s),
|
||||||
|
translateY: '-=' + (dy / s),
|
||||||
|
}, 0)
|
||||||
|
})
|
||||||
|
.on('mouseup touchend', function(evt){
|
||||||
|
dragging = false
|
||||||
|
elem.velocity('stop')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
jQuery.fn.selectText = function(){
|
||||||
|
var range = document.createRange()
|
||||||
|
|
||||||
|
this.each(function(){
|
||||||
|
range.selectNodeContents(this)
|
||||||
|
})
|
||||||
|
|
||||||
|
var sel = window.getSelection()
|
||||||
|
sel.removeAllRanges()
|
||||||
|
sel.addRange(range)
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
jQuery.fn.caretOffset = function(){ return getCaretOffset(this) }
|
||||||
|
jQuery.fn.selectionCollapsed = function(){ return selectionCollapsed(this) }
|
||||||
|
|
||||||
|
|
||||||
|
var keyboard = require('lib/keyboard')
|
||||||
|
|
||||||
|
// Make element editable...
|
||||||
|
//
|
||||||
|
// Options format:
|
||||||
|
// {
|
||||||
|
// // activate (focus) element...
|
||||||
|
// //
|
||||||
|
// // NOTE: this will also select the element text...
|
||||||
|
// activate: false,
|
||||||
|
//
|
||||||
|
// // set multi-line edit mode...
|
||||||
|
// multiline: false,
|
||||||
|
//
|
||||||
|
// // if true in multi-line mode, accept filed on Enter, while
|
||||||
|
// // ctrl-Enter / meta-Enter insert a new line; otherwise
|
||||||
|
// // ctrl-Enter / meta-Enter will accept the edit.
|
||||||
|
// accept_on_enter: true,
|
||||||
|
//
|
||||||
|
// // clear element value on edit...
|
||||||
|
// clear_on_edit: false,
|
||||||
|
//
|
||||||
|
// // reset value on commit/abort...
|
||||||
|
// // XXX revise default...
|
||||||
|
// reset_on_commit: true,
|
||||||
|
// reset_on_abort: true,
|
||||||
|
//
|
||||||
|
// // blur element on commit/abort...
|
||||||
|
// blur_on_commit: false,
|
||||||
|
// blur_on_abort: false,
|
||||||
|
//
|
||||||
|
// // restore focus before disabling the editor...
|
||||||
|
// keep_focus_on_parent: true,
|
||||||
|
//
|
||||||
|
// // clear selection on commit/abort...
|
||||||
|
// clear_selection_on_commit: true,
|
||||||
|
// clear_selection_on_abort: true,
|
||||||
|
//
|
||||||
|
// // If false unhandled key events will not be propagated to
|
||||||
|
// // parents...
|
||||||
|
// propagate_unhandled_keys: true,
|
||||||
|
//
|
||||||
|
// // If false the element editable state will not be reset to
|
||||||
|
// // the original when edit is done...
|
||||||
|
// reset_on_done: true,
|
||||||
|
//
|
||||||
|
// // Keys that will abort the edit...
|
||||||
|
// abort_keys: [
|
||||||
|
// 'Esc',
|
||||||
|
// ],
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// This listens to these events triggerable by user:
|
||||||
|
// 'edit-commit' - will commit changes, this is passed the
|
||||||
|
// new text just edited.
|
||||||
|
// 'edit-abort' - will reset field, this is passed the
|
||||||
|
// original text before the edit.
|
||||||
|
//
|
||||||
|
// These events get passed the relevant text, but the element is
|
||||||
|
// likely to be already reset to a different state, to get the
|
||||||
|
// element before any state change is started use one of the
|
||||||
|
// following variants:
|
||||||
|
// 'edit-committing' - triggered within 'edit-commit' but before
|
||||||
|
// anything is changed, gets passed the final
|
||||||
|
// text (same as 'edit-commit')
|
||||||
|
// 'edit-aborting' - triggered within 'edit-abort' but before
|
||||||
|
// anything is changed, gets passed the
|
||||||
|
// original text value (same as 'edit-abort')
|
||||||
|
//
|
||||||
|
// This will try and preserve element content DOM when resetting.
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// NOTE: removing tabindex will reset focus, so this will attempt to
|
||||||
|
// focus the first [tabindex] element up the tree...
|
||||||
|
//
|
||||||
|
// XXX add option to select the element on start or just focus it...
|
||||||
|
// .activate: 'select' | true | false
|
||||||
|
// XXX should we just use form elements???
|
||||||
|
// ...it's a trade-off, here we add editing functionality and fight
|
||||||
|
// a bit the original function, in an input we'll need to fight part
|
||||||
|
// of the editing functionality and add our own navigation...
|
||||||
|
// XXX move this to a more generic spot...
|
||||||
|
jQuery.fn.makeEditable = function(options){
|
||||||
|
var that = this
|
||||||
|
|
||||||
|
if(options == false){
|
||||||
|
this
|
||||||
|
.removeAttr('contenteditable')
|
||||||
|
.removeAttr('tabindex')
|
||||||
|
.removeClass('editable-field')
|
||||||
|
|
||||||
|
var events = this.data('editable-field-events')
|
||||||
|
for(var e in events){
|
||||||
|
this.off(e, events[e])
|
||||||
|
}
|
||||||
|
this.removeData('editable-field-events')
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
options = Object.assign({
|
||||||
|
// defaults...
|
||||||
|
activate: false,
|
||||||
|
multiline: false,
|
||||||
|
accept_on_enter: true,
|
||||||
|
clear_on_edit: false,
|
||||||
|
reset_on_commit: true,
|
||||||
|
reset_on_abort: true,
|
||||||
|
blur_on_commit: false,
|
||||||
|
blur_on_abort: false,
|
||||||
|
keep_focus_on_parent: true,
|
||||||
|
clear_selection_on_commit: true,
|
||||||
|
clear_selection_on_abort: true,
|
||||||
|
propagate_unhandled_keys: true,
|
||||||
|
reset_on_done: true,
|
||||||
|
abort_keys: ['Esc'],
|
||||||
|
}, options || {})
|
||||||
|
|
||||||
|
var original_text = this[0].innerText
|
||||||
|
var original_dom = document.createDocumentFragment()
|
||||||
|
this[0].childNodes
|
||||||
|
.forEach(function(node){
|
||||||
|
original_dom.appendChild(node.cloneNode(true)) })
|
||||||
|
var resetOriginal = function(){
|
||||||
|
//that.text(original_text)
|
||||||
|
that[0].innerHTML = ''
|
||||||
|
that[0].appendChild(original_dom.cloneNode(true))
|
||||||
|
}
|
||||||
|
|
||||||
|
this.prop('contenteditable', true)
|
||||||
|
|
||||||
|
options.activate
|
||||||
|
&& options.clear_on_edit
|
||||||
|
// XXX this for some reason breaks on click...
|
||||||
|
&& this.text('')
|
||||||
|
|
||||||
|
// NOTE: this will also focus the element...
|
||||||
|
options.activate
|
||||||
|
&& this.selectText()
|
||||||
|
|
||||||
|
// do not setup handlers more than once...
|
||||||
|
if(!this.hasClass('editable-field')){
|
||||||
|
var events = {}
|
||||||
|
this
|
||||||
|
// make the element focusable and selectable...
|
||||||
|
.attr('tabindex', '0')
|
||||||
|
.addClass('editable-field')
|
||||||
|
.keydown(events.keydown = function(in_evt){
|
||||||
|
var evt = window.event || in_evt
|
||||||
|
if(!that.prop('contenteditable')){
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
evt.stopPropagation()
|
||||||
|
|
||||||
|
var c = getCaretOffset(this)
|
||||||
|
var collapsed = selectionCollapsed(this)
|
||||||
|
var n = keyboard.code2key(evt.keyCode)
|
||||||
|
|
||||||
|
// abort...
|
||||||
|
if((options.abort_keys || []).indexOf(n) >= 0){
|
||||||
|
that.trigger('edit-abort', original_text)
|
||||||
|
|
||||||
|
// done -- single line...
|
||||||
|
} else if(n == 'Enter'
|
||||||
|
&& !options.multiline){
|
||||||
|
evt.preventDefault()
|
||||||
|
that.trigger('edit-commit', that[0].innerText)
|
||||||
|
|
||||||
|
// done -- multi-line...
|
||||||
|
} else if(options.multiline
|
||||||
|
&& n == 'Enter'
|
||||||
|
&& (options.accept_on_enter ?
|
||||||
|
!(evt.ctrlKey || evt.shiftKey || evt.metaKey)
|
||||||
|
: (evt.ctrlKey || evt.shiftKey || evt.metaKey)) ){
|
||||||
|
evt.preventDefault()
|
||||||
|
that.trigger('edit-commit', that[0].innerText)
|
||||||
|
|
||||||
|
// multi-line keep keys...
|
||||||
|
} else if(options.multiline
|
||||||
|
&& options.accept_on_enter ?
|
||||||
|
(n == 'Enter'
|
||||||
|
&& (evt.ctrlKey || evt.shiftKey || evt.metaKey))
|
||||||
|
: n == 'Enter'){
|
||||||
|
return
|
||||||
|
|
||||||
|
// multi-line arrow keys -- keep key iff not at first/last position...
|
||||||
|
} else if(options.multiline
|
||||||
|
&& n == 'Up'
|
||||||
|
&& (c > 0 || !collapsed)){
|
||||||
|
return
|
||||||
|
} else if(options.multiline
|
||||||
|
&& n == 'Down'
|
||||||
|
&& (c < $(this).text().length || !collapsed)){
|
||||||
|
return
|
||||||
|
} else if(n == 'Up' || n == 'Down'){
|
||||||
|
evt.preventDefault()
|
||||||
|
that.trigger('edit-commit', that[0].innerText)
|
||||||
|
|
||||||
|
// continue handling...
|
||||||
|
} else if(options.propagate_unhandled_keys){
|
||||||
|
// NOTE: jQuery can't reuse browser events, this
|
||||||
|
// we need to pass a jq event/proxy here...
|
||||||
|
$(this).parent().trigger(in_evt || evt)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.blur(events.blur = function(){
|
||||||
|
window.getSelection().removeAllRanges()
|
||||||
|
})
|
||||||
|
.on('focus click', events['focus click'] = function(evt){
|
||||||
|
evt.stopPropagation()
|
||||||
|
options.clear_on_edit
|
||||||
|
&& $(this)
|
||||||
|
.text('')
|
||||||
|
.selectText()
|
||||||
|
})
|
||||||
|
// user triggerable events...
|
||||||
|
.on('edit-abort', events['edit-abort'] = function(evt, text){
|
||||||
|
that.trigger('edit-aborting', text)
|
||||||
|
|
||||||
|
options.clear_selection_on_abort
|
||||||
|
&& window.getSelection().removeAllRanges()
|
||||||
|
|
||||||
|
// reset original value...
|
||||||
|
options.reset_on_abort
|
||||||
|
&& resetOriginal()
|
||||||
|
options.blur_on_abort
|
||||||
|
&& this.blur()
|
||||||
|
|
||||||
|
// restore focus on parent...
|
||||||
|
options.keep_focus_on_parent
|
||||||
|
&& that.parents('[tabindex]').first().focus()
|
||||||
|
|
||||||
|
options.reset_on_done
|
||||||
|
&& that.makeEditable(false)
|
||||||
|
})
|
||||||
|
.on('edit-commit', events['edit-commit'] = function(evt, text){
|
||||||
|
that.trigger('edit-committing', text)
|
||||||
|
|
||||||
|
options.clear_selection_on_commit
|
||||||
|
&& window.getSelection().removeAllRanges()
|
||||||
|
|
||||||
|
// reset original value...
|
||||||
|
options.reset_on_commit
|
||||||
|
&& resetOriginal()
|
||||||
|
options.blur_on_commit
|
||||||
|
&& this.blur()
|
||||||
|
|
||||||
|
// restore focus on parent...
|
||||||
|
options.keep_focus_on_parent
|
||||||
|
&& that.parents('[tabindex]').first().focus()
|
||||||
|
|
||||||
|
options.reset_on_done
|
||||||
|
&& that.makeEditable(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
this.data('editable-field-events', events)
|
||||||
|
}
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**********************************************************************
|
||||||
|
* vim:set ts=4 sw=4 : */ return module })
|
||||||
@ -391,375 +391,6 @@ function(path){
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*********************************************************************/
|
|
||||||
// HTML/DOM/jQuery...
|
|
||||||
|
|
||||||
var selectElemText =
|
|
||||||
module.selectElemText =
|
|
||||||
function(elem){
|
|
||||||
var range = document.createRange()
|
|
||||||
range.selectNodeContents(elem)
|
|
||||||
var sel = window.getSelection()
|
|
||||||
sel.removeAllRanges()
|
|
||||||
sel.addRange(range)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// XXX make this global...
|
|
||||||
var getCaretOffset =
|
|
||||||
module.getCaretOffset =
|
|
||||||
function(elem){
|
|
||||||
var s = window.getSelection()
|
|
||||||
if(s.rangeCount == 0){
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
var r = s.getRangeAt(0)
|
|
||||||
var pre = r.cloneRange()
|
|
||||||
pre.selectNodeContents(elem)
|
|
||||||
pre.setEnd(r.endContainer, r.endOffset)
|
|
||||||
return pre.toString().length || 0
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
var selectionCollapsed =
|
|
||||||
module.selectionCollapsed =
|
|
||||||
function(elem){
|
|
||||||
var s = window.getSelection()
|
|
||||||
if(s.rangeCount == 0){
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return s.getRangeAt(0).cloneRange().collapsed
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//---------------------------------------------------------------------
|
|
||||||
// XXX experiment
|
|
||||||
if(typeof(jQuery) != typeof(undefined)){
|
|
||||||
jQuery.fn._drag = function(){
|
|
||||||
var dragging = false
|
|
||||||
var s,
|
|
||||||
px, py
|
|
||||||
|
|
||||||
var elem = $(this)
|
|
||||||
.on('mousedown touchstart', function(evt){
|
|
||||||
dragging = true
|
|
||||||
px = evt.clientX
|
|
||||||
px = evt.clientY
|
|
||||||
|
|
||||||
s = elem.rscale()
|
|
||||||
})
|
|
||||||
.on('mousemove touchmove', function(evt){
|
|
||||||
if(!dragging){
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var x = evt.clientX
|
|
||||||
var dx = px - x
|
|
||||||
px = x
|
|
||||||
|
|
||||||
var y = evt.clientY
|
|
||||||
var dy = py - y
|
|
||||||
py = y
|
|
||||||
|
|
||||||
elem
|
|
||||||
.velocity('stop')
|
|
||||||
.velocity({
|
|
||||||
translateX: '-=' + (dx / s),
|
|
||||||
translateY: '-=' + (dy / s),
|
|
||||||
}, 0)
|
|
||||||
})
|
|
||||||
.on('mouseup touchend', function(evt){
|
|
||||||
dragging = false
|
|
||||||
elem.velocity('stop')
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
jQuery.fn.selectText = function(){
|
|
||||||
var range = document.createRange()
|
|
||||||
|
|
||||||
this.each(function(){
|
|
||||||
range.selectNodeContents(this)
|
|
||||||
})
|
|
||||||
|
|
||||||
var sel = window.getSelection()
|
|
||||||
sel.removeAllRanges()
|
|
||||||
sel.addRange(range)
|
|
||||||
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
jQuery.fn.caretOffset = function(){ return getCaretOffset(this) }
|
|
||||||
jQuery.fn.selectionCollapsed = function(){ return selectionCollapsed(this) }
|
|
||||||
|
|
||||||
|
|
||||||
var keyboard = require('lib/keyboard')
|
|
||||||
|
|
||||||
// Make element editable...
|
|
||||||
//
|
|
||||||
// Options format:
|
|
||||||
// {
|
|
||||||
// // activate (focus) element...
|
|
||||||
// //
|
|
||||||
// // NOTE: this will also select the element text...
|
|
||||||
// activate: false,
|
|
||||||
//
|
|
||||||
// // set multi-line edit mode...
|
|
||||||
// multiline: false,
|
|
||||||
//
|
|
||||||
// // if true in multi-line mode, accept filed on Enter, while
|
|
||||||
// // ctrl-Enter / meta-Enter insert a new line; otherwise
|
|
||||||
// // ctrl-Enter / meta-Enter will accept the edit.
|
|
||||||
// accept_on_enter: true,
|
|
||||||
//
|
|
||||||
// // clear element value on edit...
|
|
||||||
// clear_on_edit: false,
|
|
||||||
//
|
|
||||||
// // reset value on commit/abort...
|
|
||||||
// // XXX revise default...
|
|
||||||
// reset_on_commit: true,
|
|
||||||
// reset_on_abort: true,
|
|
||||||
//
|
|
||||||
// // blur element on commit/abort...
|
|
||||||
// blur_on_commit: false,
|
|
||||||
// blur_on_abort: false,
|
|
||||||
//
|
|
||||||
// // restore focus before disabling the editor...
|
|
||||||
// keep_focus_on_parent: true,
|
|
||||||
//
|
|
||||||
// // clear selection on commit/abort...
|
|
||||||
// clear_selection_on_commit: true,
|
|
||||||
// clear_selection_on_abort: true,
|
|
||||||
//
|
|
||||||
// // If false unhandled key events will not be propagated to
|
|
||||||
// // parents...
|
|
||||||
// propagate_unhandled_keys: true,
|
|
||||||
//
|
|
||||||
// // If false the element editable state will not be reset to
|
|
||||||
// // the original when edit is done...
|
|
||||||
// reset_on_done: true,
|
|
||||||
//
|
|
||||||
// // Keys that will abort the edit...
|
|
||||||
// abort_keys: [
|
|
||||||
// 'Esc',
|
|
||||||
// ],
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// This listens to these events triggerable by user:
|
|
||||||
// 'edit-commit' - will commit changes, this is passed the
|
|
||||||
// new text just edited.
|
|
||||||
// 'edit-abort' - will reset field, this is passed the
|
|
||||||
// original text before the edit.
|
|
||||||
//
|
|
||||||
// These events get passed the relevant text, but the element is
|
|
||||||
// likely to be already reset to a different state, to get the
|
|
||||||
// element before any state change is started use one of the
|
|
||||||
// following variants:
|
|
||||||
// 'edit-committing' - triggered within 'edit-commit' but before
|
|
||||||
// anything is changed, gets passed the final
|
|
||||||
// text (same as 'edit-commit')
|
|
||||||
// 'edit-aborting' - triggered within 'edit-abort' but before
|
|
||||||
// anything is changed, gets passed the
|
|
||||||
// original text value (same as 'edit-abort')
|
|
||||||
//
|
|
||||||
// This will try and preserve element content DOM when resetting.
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// NOTE: removing tabindex will reset focus, so this will attempt to
|
|
||||||
// focus the first [tabindex] element up the tree...
|
|
||||||
//
|
|
||||||
// XXX add option to select the element on start or just focus it...
|
|
||||||
// .activate: 'select' | true | false
|
|
||||||
// XXX should we just use form elements???
|
|
||||||
// ...it's a trade-off, here we add editing functionality and fight
|
|
||||||
// a bit the original function, in an input we'll need to fight part
|
|
||||||
// of the editing functionality and add our own navigation...
|
|
||||||
// XXX move this to a more generic spot...
|
|
||||||
jQuery.fn.makeEditable = function(options){
|
|
||||||
var that = this
|
|
||||||
|
|
||||||
if(options == false){
|
|
||||||
this
|
|
||||||
.removeAttr('contenteditable')
|
|
||||||
.removeAttr('tabindex')
|
|
||||||
.removeClass('editable-field')
|
|
||||||
|
|
||||||
var events = this.data('editable-field-events')
|
|
||||||
for(var e in events){
|
|
||||||
this.off(e, events[e])
|
|
||||||
}
|
|
||||||
this.removeData('editable-field-events')
|
|
||||||
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
options = Object.assign({
|
|
||||||
// defaults...
|
|
||||||
activate: false,
|
|
||||||
multiline: false,
|
|
||||||
accept_on_enter: true,
|
|
||||||
clear_on_edit: false,
|
|
||||||
reset_on_commit: true,
|
|
||||||
reset_on_abort: true,
|
|
||||||
blur_on_commit: false,
|
|
||||||
blur_on_abort: false,
|
|
||||||
keep_focus_on_parent: true,
|
|
||||||
clear_selection_on_commit: true,
|
|
||||||
clear_selection_on_abort: true,
|
|
||||||
propagate_unhandled_keys: true,
|
|
||||||
reset_on_done: true,
|
|
||||||
abort_keys: ['Esc'],
|
|
||||||
}, options || {})
|
|
||||||
|
|
||||||
var original_text = this[0].innerText
|
|
||||||
var original_dom = document.createDocumentFragment()
|
|
||||||
this[0].childNodes
|
|
||||||
.forEach(function(node){
|
|
||||||
original_dom.appendChild(node.cloneNode(true)) })
|
|
||||||
var resetOriginal = function(){
|
|
||||||
//that.text(original_text)
|
|
||||||
that[0].innerHTML = ''
|
|
||||||
that[0].appendChild(original_dom.cloneNode(true))
|
|
||||||
}
|
|
||||||
|
|
||||||
this.prop('contenteditable', true)
|
|
||||||
|
|
||||||
options.activate
|
|
||||||
&& options.clear_on_edit
|
|
||||||
// XXX this for some reason breaks on click...
|
|
||||||
&& this.text('')
|
|
||||||
|
|
||||||
// NOTE: this will also focus the element...
|
|
||||||
options.activate
|
|
||||||
&& this.selectText()
|
|
||||||
|
|
||||||
// do not setup handlers more than once...
|
|
||||||
if(!this.hasClass('editable-field')){
|
|
||||||
var events = {}
|
|
||||||
this
|
|
||||||
// make the element focusable and selectable...
|
|
||||||
.attr('tabindex', '0')
|
|
||||||
.addClass('editable-field')
|
|
||||||
.keydown(events.keydown = function(in_evt){
|
|
||||||
var evt = window.event || in_evt
|
|
||||||
if(!that.prop('contenteditable')){
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
evt.stopPropagation()
|
|
||||||
|
|
||||||
var c = getCaretOffset(this)
|
|
||||||
var collapsed = selectionCollapsed(this)
|
|
||||||
var n = keyboard.code2key(evt.keyCode)
|
|
||||||
|
|
||||||
// abort...
|
|
||||||
if((options.abort_keys || []).indexOf(n) >= 0){
|
|
||||||
that.trigger('edit-abort', original_text)
|
|
||||||
|
|
||||||
// done -- single line...
|
|
||||||
} else if(n == 'Enter'
|
|
||||||
&& !options.multiline){
|
|
||||||
evt.preventDefault()
|
|
||||||
that.trigger('edit-commit', that[0].innerText)
|
|
||||||
|
|
||||||
// done -- multi-line...
|
|
||||||
} else if(options.multiline
|
|
||||||
&& n == 'Enter'
|
|
||||||
&& (options.accept_on_enter ?
|
|
||||||
!(evt.ctrlKey || evt.shiftKey || evt.metaKey)
|
|
||||||
: (evt.ctrlKey || evt.shiftKey || evt.metaKey)) ){
|
|
||||||
evt.preventDefault()
|
|
||||||
that.trigger('edit-commit', that[0].innerText)
|
|
||||||
|
|
||||||
// multi-line keep keys...
|
|
||||||
} else if(options.multiline
|
|
||||||
&& options.accept_on_enter ?
|
|
||||||
(n == 'Enter'
|
|
||||||
&& (evt.ctrlKey || evt.shiftKey || evt.metaKey))
|
|
||||||
: n == 'Enter'){
|
|
||||||
return
|
|
||||||
|
|
||||||
// multi-line arrow keys -- keep key iff not at first/last position...
|
|
||||||
} else if(options.multiline
|
|
||||||
&& n == 'Up'
|
|
||||||
&& (c > 0 || !collapsed)){
|
|
||||||
return
|
|
||||||
} else if(options.multiline
|
|
||||||
&& n == 'Down'
|
|
||||||
&& (c < $(this).text().length || !collapsed)){
|
|
||||||
return
|
|
||||||
} else if(n == 'Up' || n == 'Down'){
|
|
||||||
evt.preventDefault()
|
|
||||||
that.trigger('edit-commit', that[0].innerText)
|
|
||||||
|
|
||||||
// continue handling...
|
|
||||||
} else if(options.propagate_unhandled_keys){
|
|
||||||
// NOTE: jQuery can't reuse browser events, this
|
|
||||||
// we need to pass a jq event/proxy here...
|
|
||||||
$(this).parent().trigger(in_evt || evt)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.blur(events.blur = function(){
|
|
||||||
window.getSelection().removeAllRanges()
|
|
||||||
})
|
|
||||||
.on('focus click', events['focus click'] = function(evt){
|
|
||||||
evt.stopPropagation()
|
|
||||||
options.clear_on_edit
|
|
||||||
&& $(this)
|
|
||||||
.text('')
|
|
||||||
.selectText()
|
|
||||||
})
|
|
||||||
// user triggerable events...
|
|
||||||
.on('edit-abort', events['edit-abort'] = function(evt, text){
|
|
||||||
that.trigger('edit-aborting', text)
|
|
||||||
|
|
||||||
options.clear_selection_on_abort
|
|
||||||
&& window.getSelection().removeAllRanges()
|
|
||||||
|
|
||||||
// reset original value...
|
|
||||||
options.reset_on_abort
|
|
||||||
&& resetOriginal()
|
|
||||||
options.blur_on_abort
|
|
||||||
&& this.blur()
|
|
||||||
|
|
||||||
// restore focus on parent...
|
|
||||||
options.keep_focus_on_parent
|
|
||||||
&& that.parents('[tabindex]').first().focus()
|
|
||||||
|
|
||||||
options.reset_on_done
|
|
||||||
&& that.makeEditable(false)
|
|
||||||
})
|
|
||||||
.on('edit-commit', events['edit-commit'] = function(evt, text){
|
|
||||||
that.trigger('edit-committing', text)
|
|
||||||
|
|
||||||
options.clear_selection_on_commit
|
|
||||||
&& window.getSelection().removeAllRanges()
|
|
||||||
|
|
||||||
// reset original value...
|
|
||||||
options.reset_on_commit
|
|
||||||
&& resetOriginal()
|
|
||||||
options.blur_on_commit
|
|
||||||
&& this.blur()
|
|
||||||
|
|
||||||
// restore focus on parent...
|
|
||||||
options.keep_focus_on_parent
|
|
||||||
&& that.parents('[tabindex]').first().focus()
|
|
||||||
|
|
||||||
options.reset_on_done
|
|
||||||
&& that.makeEditable(false)
|
|
||||||
})
|
|
||||||
|
|
||||||
this.data('editable-field-events', events)
|
|
||||||
}
|
|
||||||
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**********************************************************************
|
/**********************************************************************
|
||||||
* vim:set ts=4 sw=4 : */ return module })
|
* vim:set ts=4 sw=4 : */ return module })
|
||||||
|
|||||||
@ -45,6 +45,7 @@ if(typeof(process) != 'undefined'){
|
|||||||
function(require){ var module={} // makes module AMD/node compatible...
|
function(require){ var module={} // makes module AMD/node compatible...
|
||||||
/*********************************************************************/
|
/*********************************************************************/
|
||||||
|
|
||||||
|
var utildom = require('lib/util-dom')
|
||||||
var viewer = require('imagegrid/viewer')
|
var viewer = require('imagegrid/viewer')
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user