Alex A. Naanou 27aef3db40 dos linefeeds converted to unix...
Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
2024-10-27 11:01:12 +03:00

308 lines
6.8 KiB
JavaScript
Executable File

/**********************************************************************
*
*
*
**********************************************************************/
((typeof define)[0]=='u'?function(f){module.exports=f(require)}:define)
(function(require){ var module={} // make module AMD/node compatible...
/*********************************************************************/
var keyboard = require('../keyboard')
var object = require('../object')
/*********************************************************************/
// helpers...
// NOTE: this may produce a leak in cases where lots of events are bound
// and on a long running widget... (XXX)
var proxyToDom =
module.proxyToDom =
function(name){
return function(...args){
// XXX this feels hacky, investigate a better solution...
// ...one way to go is to either handle events internally
// or remove .bind(..)...
// XXX this is a potential leak (see note above)...
var d = this.__proxy_to_dom_dict =
this.__proxy_to_dom_dict || new Map()
// bind functions to this...
args = args
.map(function(a){
name != 'off'
&& a instanceof Function
&& ( d.has(a)
|| d.set(a, a.bind(this)) )
var res = a instanceof Function ?
d.get(a) || a.bind(this)
: a
// NOTE: this will delete cached handlers but it can't
// get all of them in a generic way...
name == 'off'
&& a instanceof Function
&& d.delete(a)
return res
}.bind(this))
// call method or trigger event...
name in this.dom ?
this.dom[name](...args)
: this.dom.trigger(name, args)
return this } }
var eventToDom =
module.eventToDom =
function(name, defaults){
return function(){
// register...
if(arguments[0] instanceof Function){
this.dom.trigger(name, [...arguments])
// trigger...
} else {
var args = (arguments.length == 0 && defaults) ?
defaults.call(this)
: [...arguments]
args = args instanceof Array ? args : [args]
this.dom.trigger(name, args)
}
return this } }
// XXX triggering events from here and from jQuery/dom has a
// different effect...
var triggerEventWithSource =
module.triggerEventWithSource =
function(){
var args = [...arguments]
var evt = args.shift()
if(typeof(evt) == typeof('str')){
evt = $.Event(evt)
}
evt.source = this
args.splice(0, 0, evt)
this.dom.trigger.apply(this.dom, args)
return this
}
/*********************************************************************/
var WidgetClassPrototype = {
make: function(obj, client, options){
throw new Error('Widget must define a .make method.') },
}
var WidgetPrototype = {
// NOTE: this must have .data('widget-controller', this) set...
dom: null,
client: null,
options: {
keyboardRepeatPause: 100,
nonPropagatedEvents: [
'start',
'click',
'keydown',
'close',
],
},
keybindings: null,
keyboard: null,
// XXX triggering events from here and from jQuery/dom has a
// different effect...
trigger: triggerEventWithSource,
// proxy event api...
on: proxyToDom('on'),
one: proxyToDom('one'),
off: proxyToDom('off'),
bind: proxyToDom('bind'),
unbind: proxyToDom('unbind'),
deligate: proxyToDom('deligate'),
undeligate: proxyToDom('undeligate'),
// custom events...
//
start: function(handler){
handler ?
this.on('start', handler)
:this.trigger('start')
return this
},
// NOTE: this can be passed a string that can be used as a reason
// for closing...
close: function(a){
// trigger...
if(a == null || typeof(a) == typeof('str')){
a = a || 'accept'
this.parent.close
&& this.parent.close(a)
this.trigger('close', a)
// register new handler...
} else {
this.on('close', a)
}
return this
},
// XXX this will not:
// - attach dom to parent... (???)
// - handle focus... (???)
// XXX same as ContainerPrototype.__init__ but skips client...
__init__: function(parent, options){
var that = this
parent = this.parent = $(parent || 'body')
this.keybindings = JSON.parse(JSON.stringify(this.keybindings))
// merge options...
options = this.options = Object.assign(
Object.create(this.options),
options || {})
// build the dom...
if(this.constructor.make){
this.dom = this.constructor.make(this, options)
this.dom.data('widget-controller', this)
}
// XXX do we do this here???
/*
if(parent && this.dom){
parent.append(this.dom)
}
*/
// add keyboard handler...
if(this.keybindings && this.dom){
this.keyboard =
this.keyboard || keyboard.KeyboardWithCSSModes(
function(){ return that.keybindings },
function(){ return that.dom })
this.dom
.keydown(
keyboard.makePausableKeyboardHandler(
this.keyboard,
options.logKeys,
this,
function(){ return this.options.keyboardRepeatPause })) }
this.options.nonPropagatedEvents != null
&& this.on(this.options.nonPropagatedEvents.join(' '),
function(evt){ evt.stopPropagation() })
return this
},
}
var Widget =
module.Widget =
object.Constructor('Widget',
WidgetClassPrototype,
WidgetPrototype)
/*********************************************************************/
var ContainerClassPrototype = {
}
var ContainerPrototype = {
// NOTE: this must have .data('widget-controller', this) set...
dom: null,
focus: function(handler){
if(handler != null){
this.on('focus', handler)
} else {
this.dom.focus()
this.client
&& this.client.focus
&& this.client.focus()
}
return this
},
// XXX this is the same as WidgetPrototype.__init__ but also handles
// the client...
__init__: function(parent, client, options){
var that = this
parent = this.parent = $(parent || 'body')
this.keybindings = JSON.parse(JSON.stringify(this.keybindings))
this.client = client
client.parent = this
// merge options...
options = this.options = Object.assign(
Object.create(this.options),
options || {})
// build the dom...
if(this.constructor.make){
this.dom = this.constructor
.make(this, client.dom || client, options)
this.dom.data('widget-controller', this)
}
// add keyboard handler...
if(this.keybindings && this.dom){
this.keyboard =
this.keyboard || keyboard.KeyboardWithCSSModes(
function(){ return that.keybindings },
function(){ return that.dom })
this.dom
.keydown(
keyboard.makePausableKeyboardHandler(
this.keyboard,
options.logKeys,
this,
function(){ return this.options.keyboardRepeatPause }))
}
if(this.options.nonPropagatedEvents != null){
this.on(this.options.nonPropagatedEvents.join(' '),
function(evt){ evt.stopPropagation() })
}
return this
},
}
var Container =
module.Container =
object.Constructor('Container',
Widget,
ContainerClassPrototype,
ContainerPrototype)
/**********************************************************************
* vim:set ts=4 sw=4 : */ return module })