mirror of
https://github.com/flynx/ImageGrid.git
synced 2025-10-29 10:20:08 +00:00
migrated the html renderer to new arch, still needs testing...
Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
parent
91cd10fada
commit
68fd915a84
@ -780,9 +780,9 @@ module.BaseRenderer = {
|
|||||||
root: null,
|
root: null,
|
||||||
|
|
||||||
// component renderers...
|
// component renderers...
|
||||||
elem: function(elem, index, path, options){
|
elem: function(item, index, path, options){
|
||||||
throw new Error('.elem(..): Not implemented.') },
|
throw new Error('.elem(..): Not implemented.') },
|
||||||
inline: function(elem, lst, index, path, options){
|
inline: function(item, lst, index, path, options){
|
||||||
throw new Error('.inline(..): Not implemented.') },
|
throw new Error('.inline(..): Not implemented.') },
|
||||||
nest: function(header, lst, index, path, options){
|
nest: function(header, lst, index, path, options){
|
||||||
throw new Error('.nest(..): Not implemented.') },
|
throw new Error('.nest(..): Not implemented.') },
|
||||||
@ -807,12 +807,12 @@ var TextRenderer =
|
|||||||
module.TextRenderer = {
|
module.TextRenderer = {
|
||||||
__proto__: BaseRenderer,
|
__proto__: BaseRenderer,
|
||||||
|
|
||||||
elem: function(elem, index, path, options){
|
elem: function(item, index, path, options){
|
||||||
return path
|
return path
|
||||||
.slice(0, -1)
|
.slice(0, -1)
|
||||||
.map(function(e){ return ' '})
|
.map(function(e){ return ' '})
|
||||||
.join('') + elem.id },
|
.join('') + item.id },
|
||||||
inline: function(elem, lst, index, path, options){
|
inline: function(item, lst, index, path, options){
|
||||||
return lst },
|
return lst },
|
||||||
// XXX if header is null then render a headless nested block...
|
// XXX if header is null then render a headless nested block...
|
||||||
nest: function(header, lst, index, path, options){
|
nest: function(header, lst, index, path, options){
|
||||||
@ -839,9 +839,9 @@ module.PathRenderer = {
|
|||||||
// renderers...
|
// renderers...
|
||||||
//
|
//
|
||||||
// render paths...
|
// render paths...
|
||||||
elem: function(elem, index, path, options){
|
elem: function(item, index, path, options){
|
||||||
return path.join('/') },
|
return path.join('/') },
|
||||||
inline: function(elem, lst, index, path, options){
|
inline: function(item, lst, index, path, options){
|
||||||
return lst },
|
return lst },
|
||||||
// XXX if header is null then render a headless nested block...
|
// XXX if header is null then render a headless nested block...
|
||||||
nest: function(header, lst, index, path, options){
|
nest: function(header, lst, index, path, options){
|
||||||
@ -3008,6 +3008,7 @@ var BaseBrowserPrototype = {
|
|||||||
// do not reconstruct the ones already present...
|
// do not reconstruct the ones already present...
|
||||||
// XXX should from/to/around/count be a feature of this or of .walk(..)???
|
// XXX should from/to/around/count be a feature of this or of .walk(..)???
|
||||||
// XXX might be a good idea to use this.root === this instead of context.root === this
|
// XXX might be a good idea to use this.root === this instead of context.root === this
|
||||||
|
//*
|
||||||
render: function(options, renderer, context){
|
render: function(options, renderer, context){
|
||||||
renderer = renderer || this
|
renderer = renderer || this
|
||||||
context = renderer.renderContext(context)
|
context = renderer.renderContext(context)
|
||||||
@ -3176,6 +3177,7 @@ var BaseBrowserPrototype = {
|
|||||||
renderer.renderFinalize(null, items, null, context)
|
renderer.renderFinalize(null, items, null, context)
|
||||||
// nested context -> return item list...
|
// nested context -> return item list...
|
||||||
: items } },
|
: items } },
|
||||||
|
/*/
|
||||||
|
|
||||||
// XXX EXPERIMENTAL....
|
// XXX EXPERIMENTAL....
|
||||||
//
|
//
|
||||||
@ -3186,7 +3188,7 @@ var BaseBrowserPrototype = {
|
|||||||
// - ability to render separate items/sub-trees or lists of items...
|
// - ability to render separate items/sub-trees or lists of items...
|
||||||
// ...pass the list to .walk(..), i.e. .walk(list/query, ...)
|
// ...pass the list to .walk(..), i.e. .walk(list/query, ...)
|
||||||
// XXX doc...
|
// XXX doc...
|
||||||
render2: function(options, renderer){
|
render: function(options, renderer){
|
||||||
var that = this
|
var that = this
|
||||||
|
|
||||||
// XXX args parsing...
|
// XXX args parsing...
|
||||||
@ -3233,7 +3235,7 @@ var BaseBrowserPrototype = {
|
|||||||
|
|
||||||
|
|
||||||
// used as a means to calculate lengths of nested blocks rendered
|
// used as a means to calculate lengths of nested blocks rendered
|
||||||
// via .render2(..)
|
// via .render(..)
|
||||||
var l
|
var l
|
||||||
return ((render.root === this && section instanceof Array) ?
|
return ((render.root === this && section instanceof Array) ?
|
||||||
// render list of sections...
|
// render list of sections...
|
||||||
@ -3243,7 +3245,7 @@ var BaseBrowserPrototype = {
|
|||||||
// is rendered for all nested browsers...
|
// is rendered for all nested browsers...
|
||||||
section
|
section
|
||||||
.reduce(function(res, name){
|
.reduce(function(res, name){
|
||||||
res[name] = that.render2(
|
res[name] = that.render(
|
||||||
Object.assign({},
|
Object.assign({},
|
||||||
options,
|
options,
|
||||||
{
|
{
|
||||||
@ -3278,21 +3280,21 @@ var BaseBrowserPrototype = {
|
|||||||
p = base_path.concat(p)
|
p = base_path.concat(p)
|
||||||
|
|
||||||
// children...
|
// children...
|
||||||
// do not go down child browsers -- use their .render2(..)
|
// do not go down child browsers -- use their .render(..)
|
||||||
// NOTE: doing so will require us to manually handle some
|
// NOTE: doing so will require us to manually handle some
|
||||||
// of the options that would otherwise be handled
|
// of the options that would otherwise be handled
|
||||||
// by .walk(..)...
|
// by .walk(..)...
|
||||||
var inlined = (e instanceof BaseBrowser
|
var inlined = (e instanceof BaseBrowser
|
||||||
|| e.children instanceof BaseBrowser)
|
|| e.children instanceof BaseBrowser)
|
||||||
&& !children(false)
|
&& !children(false)
|
||||||
// get children either via .walk(..) or .render2(..)
|
// get children either via .walk(..) or .render(..)
|
||||||
// depending on item type...
|
// depending on item type...
|
||||||
var getChildren = function(){
|
var getChildren = function(){
|
||||||
return inlined ?
|
return inlined ?
|
||||||
(l = (e.children instanceof BaseBrowser ?
|
(l = (e.children instanceof BaseBrowser ?
|
||||||
e.children
|
e.children
|
||||||
: e)
|
: e)
|
||||||
.render2(options, render, i+1, p))
|
.render(options, render, i+1, p))
|
||||||
: children(true) }
|
: children(true) }
|
||||||
|
|
||||||
// do the actual rendering...
|
// do the actual rendering...
|
||||||
@ -3333,6 +3335,7 @@ var BaseBrowserPrototype = {
|
|||||||
{[section]: this}
|
{[section]: this}
|
||||||
: this, options)
|
: this, options)
|
||||||
: this }) },
|
: this }) },
|
||||||
|
//*/
|
||||||
|
|
||||||
|
|
||||||
// Events...
|
// Events...
|
||||||
@ -4124,25 +4127,515 @@ var updateElemClass = function(action, cls, handler){
|
|||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
// Renderer...
|
// Renderer...
|
||||||
|
|
||||||
// XXX
|
// XXX needs testing...
|
||||||
|
// - structure seems to be fine...
|
||||||
|
// - rename .render(..) -> .render(..) and do a full test...
|
||||||
|
// - problems:
|
||||||
|
// - inlined/nested dialogs do not get button config...
|
||||||
|
// ...options not passed down correctly?
|
||||||
|
// - re-rendering loses focus...
|
||||||
|
// XXX doc...
|
||||||
var HTMLRenderer =
|
var HTMLRenderer =
|
||||||
module.HTMLRenderer = {
|
module.HTMLRenderer = {
|
||||||
__proto__: BaseRenderer,
|
__proto__: BaseRenderer,
|
||||||
|
|
||||||
elem: function(elem, index, path, options){
|
// secondary renderers...
|
||||||
|
//
|
||||||
|
// base dialog structure...
|
||||||
|
dialog: function(sections, options){
|
||||||
|
var that = this
|
||||||
|
var {header, items, footer} = sections
|
||||||
|
|
||||||
|
// dialog (container)...
|
||||||
|
var dialog = document.createElement('div')
|
||||||
|
dialog.classList.add('browse-widget')
|
||||||
|
dialog.setAttribute('tabindex', '0')
|
||||||
|
// HACK?: prevent dialog from grabbing focus from item...
|
||||||
|
dialog.addEventListener('mousedown',
|
||||||
|
function(evt){ evt.stopPropagation() })
|
||||||
|
|
||||||
|
// header...
|
||||||
|
header
|
||||||
|
&& !options.hideListHeader
|
||||||
|
&& dialog.appendChild(this.dialogHeader(header, options))
|
||||||
|
|
||||||
|
// list...
|
||||||
|
var list = document.createElement('div')
|
||||||
|
list.classList.add('list', 'v-block', 'items')
|
||||||
|
// prevent scrollbar from grabbing focus...
|
||||||
|
list.addEventListener('mousedown',
|
||||||
|
function(evt){ evt.stopPropagation() })
|
||||||
|
items
|
||||||
|
.forEach(function(item){
|
||||||
|
list.appendChild(item instanceof Array ?
|
||||||
|
that.renderGroup(item)
|
||||||
|
: item) })
|
||||||
|
dialog.appendChild(list)
|
||||||
|
|
||||||
|
// footer...
|
||||||
|
footer
|
||||||
|
&& !options.hideListFooter
|
||||||
|
&& dialog.appendChild(this.dialogFooter(footer, options))
|
||||||
|
|
||||||
|
return dialog
|
||||||
},
|
},
|
||||||
inline: function(elem, lst, index, path, options){
|
dialogHeader: function(items, options){
|
||||||
|
var elem = this.dialog({items}, options).firstChild
|
||||||
|
elem.classList.replace('items', 'header')
|
||||||
|
return elem },
|
||||||
|
dialogFooter: function(items, options){
|
||||||
|
var elem = this.dialog({items}, options).firstChild
|
||||||
|
elem.classList.replace('items', 'footer')
|
||||||
|
return elem },
|
||||||
|
// custom elements...
|
||||||
|
headerElem: function(item, index, path, options){
|
||||||
|
return this.elem(...arguments)
|
||||||
|
// update dom...
|
||||||
|
.run(function(){
|
||||||
|
this.classList.add('sub-list-header', 'traversable')
|
||||||
|
item.collapsed
|
||||||
|
&& this.classList.add('collapsed') }) },
|
||||||
|
|
||||||
|
|
||||||
|
// base renderers...
|
||||||
|
//
|
||||||
|
elem: function(item, index, path, options){
|
||||||
|
var that = this
|
||||||
|
var browser = this.root
|
||||||
|
if(options.hidden && !options.renderHidden){
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
var section = item.section || options.section
|
||||||
|
|
||||||
|
// helpers...
|
||||||
|
// XXX we need to more carefully test the value to avoid name clashes...
|
||||||
|
var resolveValue = function(value, context, exec_context){
|
||||||
|
var htmlhandler = typeof(value) == typeof('str') ?
|
||||||
|
browser.parseStringHandler(value, exec_context)
|
||||||
|
: null
|
||||||
|
return value instanceof Function ?
|
||||||
|
value.call(browser, item)
|
||||||
|
: htmlhandler
|
||||||
|
&& htmlhandler.action in context
|
||||||
|
&& context[htmlhandler.action] instanceof Function ?
|
||||||
|
context[htmlhandler.action]
|
||||||
|
.call(browser, item, ...htmlhandler.arguments)
|
||||||
|
: value }
|
||||||
|
var setDOMValue = function(target, value){
|
||||||
|
value instanceof HTMLElement ?
|
||||||
|
target.appendChild(value)
|
||||||
|
: (typeof(jQuery) != 'undefined' && value instanceof jQuery) ?
|
||||||
|
value.appendTo(target)
|
||||||
|
: (target.innerHTML = value)
|
||||||
|
return target }
|
||||||
|
var doTextKeys = function(text, doKey){
|
||||||
|
return text.replace(/\$\w/g,
|
||||||
|
function(k){
|
||||||
|
// forget the '$'...
|
||||||
|
k = k[1]
|
||||||
|
return (doKey && doKey(k)) ?
|
||||||
|
`<u class="key-hint">${k}</u>`
|
||||||
|
: k }) }
|
||||||
|
|
||||||
|
// special-case: item.html...
|
||||||
|
if(item.html){
|
||||||
|
// NOTE: this is a bit of a cheat, but it saves us from either
|
||||||
|
// parsing or restricting the format...
|
||||||
|
var tmp = document.createElement('div')
|
||||||
|
tmp.innerHTML = item.html
|
||||||
|
var elem = item.dom = tmp.firstElementChild
|
||||||
|
elem.classList.add(
|
||||||
|
...(item['class'] instanceof Array ?
|
||||||
|
item['class']
|
||||||
|
: item['class'].split(/\s+/g)))
|
||||||
|
return elem
|
||||||
|
}
|
||||||
|
|
||||||
|
// Base DOM...
|
||||||
|
var elem = document.createElement('div')
|
||||||
|
var text = item.text
|
||||||
|
|
||||||
|
// classes...
|
||||||
|
elem.classList.add(...['item']
|
||||||
|
// user classes...
|
||||||
|
.concat((item['class'] || item.cls || [])
|
||||||
|
// parse space-separated class strings...
|
||||||
|
.run(function(){
|
||||||
|
return this instanceof Array ?
|
||||||
|
this
|
||||||
|
: this.split(/\s+/g) }))
|
||||||
|
// special classes...
|
||||||
|
.concat(
|
||||||
|
(options.shorthandItemClasses || {})
|
||||||
|
.filter(function(cls){
|
||||||
|
return !!item[cls] })))
|
||||||
|
|
||||||
|
// attrs...
|
||||||
|
;(item.disabled && !options.focusDisabledItems)
|
||||||
|
|| elem.setAttribute('tabindex', '0')
|
||||||
|
Object.entries(item.attrs || {})
|
||||||
|
// shorthand attrs...
|
||||||
|
.concat((options.shorthandItemAttrs || [])
|
||||||
|
.map(function(key){
|
||||||
|
return [key, item[key]] }))
|
||||||
|
.forEach(function([key, value]){
|
||||||
|
value !== undefined
|
||||||
|
&& elem.setAttribute(key, value) })
|
||||||
|
;(item.value == null
|
||||||
|
|| item.value instanceof Object)
|
||||||
|
|| elem.setAttribute('value', item.text)
|
||||||
|
;(item.value == null
|
||||||
|
|| item.value instanceof Object
|
||||||
|
|| item.alt != item.text)
|
||||||
|
&& elem.setAttribute('alt', item.alt)
|
||||||
|
|
||||||
|
// values...
|
||||||
|
text != null
|
||||||
|
&& (item.value instanceof Array ?
|
||||||
|
item.value
|
||||||
|
: [item.value])
|
||||||
|
// handle $keys and other stuff...
|
||||||
|
// NOTE: the actual key setup is done in .__preRender__(..)
|
||||||
|
// see that for more info...
|
||||||
|
.map(function(v){
|
||||||
|
// handle key-shortcuts $K...
|
||||||
|
v = typeof(v) == typeof('str') ?
|
||||||
|
doTextKeys(v,
|
||||||
|
function(k){
|
||||||
|
return (item.keys || [])
|
||||||
|
.includes(browser.keyboard.normalizeKey(k)) })
|
||||||
|
: v
|
||||||
|
|
||||||
|
var value = document.createElement('span')
|
||||||
|
value.classList.add('text')
|
||||||
|
|
||||||
|
// set the value...
|
||||||
|
setDOMValue(value,
|
||||||
|
resolveValue(v, browser))
|
||||||
|
|
||||||
|
elem.appendChild(value)
|
||||||
|
})
|
||||||
|
|
||||||
|
// system events...
|
||||||
|
elem.addEventListener('click',
|
||||||
|
function(evt){
|
||||||
|
evt.stopPropagation()
|
||||||
|
// NOTE: if an item is disabled we retain its expand/collapse
|
||||||
|
// functionality...
|
||||||
|
// XXX revise...
|
||||||
|
item.disabled ?
|
||||||
|
browser.toggleCollapse(item)
|
||||||
|
: browser.open(item, text, elem) })
|
||||||
|
elem.addEventListener('focus',
|
||||||
|
function(){
|
||||||
|
// NOTE: we do not retrigger focus on an item if it's
|
||||||
|
// already focused...
|
||||||
|
browser.focused !== item
|
||||||
|
// only trigger focus on gettable items...
|
||||||
|
// ...i.e. items in the main section excluding headers
|
||||||
|
// and footers...
|
||||||
|
&& browser.focus(item) })
|
||||||
|
elem.addEventListener('contextmenu',
|
||||||
|
function(evt){
|
||||||
|
evt.preventDefault()
|
||||||
|
browser.menu(item) })
|
||||||
|
// user events...
|
||||||
|
Object.entries(item.events || {})
|
||||||
|
// shorthand DOM events...
|
||||||
|
.concat((options.shorthandItemEvents || [])
|
||||||
|
.map(function(evt){
|
||||||
|
return [evt, item[evt]] }))
|
||||||
|
// setup the handlers...
|
||||||
|
.forEach(function([evt, handler]){
|
||||||
|
handler
|
||||||
|
&& elem.addEventListener(evt, handler.bind(browser)) })
|
||||||
|
|
||||||
|
// buttons...
|
||||||
|
var button_keys = {}
|
||||||
|
// XXX migrate button inheritance...
|
||||||
|
var buttons = (item.buttons
|
||||||
|
|| (section == 'header'
|
||||||
|
&& (options.headerButtons || []))
|
||||||
|
|| (section == 'footer'
|
||||||
|
&& (options.footerButtons || []))
|
||||||
|
|| options.itemButtons
|
||||||
|
|| [])
|
||||||
|
// resolve buttons from library...
|
||||||
|
.map(function(button){
|
||||||
|
return button instanceof Array ?
|
||||||
|
button
|
||||||
|
// XXX reference the actual make(..) and not Items...
|
||||||
|
: Items.buttons[button] instanceof Function ?
|
||||||
|
[Items.buttons[button].call(browser, item)].flat()
|
||||||
|
: Items.buttons[button] || button })
|
||||||
|
// NOTE: keep the order unsurprising -- first defined, first from left...
|
||||||
|
.reverse()
|
||||||
|
var stopPropagation = function(evt){ evt.stopPropagation() }
|
||||||
|
buttons
|
||||||
|
.forEach(function([html, handler, ...rest]){
|
||||||
|
var force = (rest[0] === true
|
||||||
|
|| rest[0] === false
|
||||||
|
|| rest[0] instanceof Function) ?
|
||||||
|
rest.shift()
|
||||||
|
: undefined
|
||||||
|
var metadata = rest.shift() || {}
|
||||||
|
|
||||||
|
// metadata...
|
||||||
|
var cls = metadata.cls || []
|
||||||
|
cls = cls instanceof Function ?
|
||||||
|
cls.call(browser, item)
|
||||||
|
: cls
|
||||||
|
cls = cls instanceof Array ?
|
||||||
|
cls
|
||||||
|
: cls.split(/\s+/g)
|
||||||
|
var alt = metadata.alt
|
||||||
|
alt = alt instanceof Function ?
|
||||||
|
alt.call(browser, item)
|
||||||
|
: alt
|
||||||
|
var keys = metadata.keys
|
||||||
|
|
||||||
|
var button = document.createElement('div')
|
||||||
|
button.classList.add('button', ...cls)
|
||||||
|
alt
|
||||||
|
&& button.setAttribute('alt', alt)
|
||||||
|
|
||||||
|
// button content...
|
||||||
|
var text_keys = []
|
||||||
|
var v = resolveValue(html, Items.buttons, {item})
|
||||||
|
setDOMValue(button,
|
||||||
|
typeof(v) == typeof('str') ?
|
||||||
|
doTextKeys(v,
|
||||||
|
function(k){
|
||||||
|
k = browser.keyboard.normalizeKey(k)
|
||||||
|
return options.disableButtonSortcuts ?
|
||||||
|
false
|
||||||
|
: !text_keys.includes(k)
|
||||||
|
&& text_keys.push(k) })
|
||||||
|
: v)
|
||||||
|
keys = text_keys.length > 0 ?
|
||||||
|
(keys || []).concat(text_keys)
|
||||||
|
: keys
|
||||||
|
|
||||||
|
// non-disabled button...
|
||||||
|
if(force instanceof Function ?
|
||||||
|
force.call(browser, item)
|
||||||
|
: (force || !item.disabled) ){
|
||||||
|
button.setAttribute('tabindex', '0')
|
||||||
|
// events to keep in buttons...
|
||||||
|
;(options.buttonLocalEvents || options.itemLocalEvents || [])
|
||||||
|
.forEach(function(evt){
|
||||||
|
button.addEventListener(evt, stopPropagation) })
|
||||||
|
// button keys...
|
||||||
|
keys && !options.disableButtonSortcuts
|
||||||
|
&& (keys instanceof Array ? keys : [keys])
|
||||||
|
.forEach(function(key){
|
||||||
|
// XXX should we break or warn???
|
||||||
|
if(key in button_keys){
|
||||||
|
throw new Error(`renderItem(..): button key already used: ${key}`) }
|
||||||
|
button_keys[keyboard.joinKey(keyboard.normalizeKey(key))] = button })
|
||||||
|
// keep focus on the item containing the button -- i.e. if
|
||||||
|
// we tab out of the item focus the item we get to...
|
||||||
|
button.addEventListener('focus', function(){
|
||||||
|
item.focused
|
||||||
|
// only focus items in the main section,
|
||||||
|
// outside of headers and footers...
|
||||||
|
|| browser.focus(item)
|
||||||
|
&& button.focus() })
|
||||||
|
// main button action (click/enter)...
|
||||||
|
// XXX should there be a secondary action (i.e. shift-enter)???
|
||||||
|
if(handler){
|
||||||
|
var func = handler instanceof Function ?
|
||||||
|
handler
|
||||||
|
// string handler -> browser.<handler>(item)
|
||||||
|
: function(evt, ...args){
|
||||||
|
var a = browser.parseStringHandler(
|
||||||
|
handler,
|
||||||
|
// button handler arg namespace...
|
||||||
|
{
|
||||||
|
event: evt,
|
||||||
|
item: item,
|
||||||
|
// NOTE: if we are not focusing
|
||||||
|
// on button click this may
|
||||||
|
// be different from item...
|
||||||
|
focused: browser.focused,
|
||||||
|
button: html,
|
||||||
|
})
|
||||||
|
browser[a.action](...a.arguments) }
|
||||||
|
|
||||||
|
// handle clicks and keyboard...
|
||||||
|
button.addEventListener('click', func.bind(browser))
|
||||||
|
// NOTE: we only trigger buttons on Enter and do
|
||||||
|
// not care about other keys...
|
||||||
|
button.addEventListener('keydown',
|
||||||
|
function(evt){
|
||||||
|
var k = keyboard.event2key(evt)
|
||||||
|
if(k.includes('Enter')){
|
||||||
|
event.stopPropagation()
|
||||||
|
func.call(browser, evt, item) } }) }
|
||||||
|
}
|
||||||
|
|
||||||
|
elem.appendChild(button)
|
||||||
|
})
|
||||||
|
|
||||||
|
// button shortcut keys...
|
||||||
|
Object.keys(button_keys).length > 0
|
||||||
|
&& elem.addEventListener('keydown',
|
||||||
|
function(evt){
|
||||||
|
var k = keyboard.joinKey(keyboard.event2key(evt))
|
||||||
|
if(k in button_keys){
|
||||||
|
evt.preventDefault()
|
||||||
|
evt.stopPropagation()
|
||||||
|
button_keys[k].focus()
|
||||||
|
// XXX should this be optional???
|
||||||
|
button_keys[k].click() } })
|
||||||
|
|
||||||
|
item.dom = elem
|
||||||
|
// XXX for some reason this messes up navigation...
|
||||||
|
// to reproduce:
|
||||||
|
// - select element with children
|
||||||
|
// - press right
|
||||||
|
// -> blur current elem
|
||||||
|
// -> next elem not selected...
|
||||||
|
//item.elem = elem
|
||||||
|
|
||||||
|
return elem
|
||||||
},
|
},
|
||||||
|
inline: function(item, lst, index, path, options){
|
||||||
|
var e = document.createElement('div')
|
||||||
|
e.classList.add('group')
|
||||||
|
lst
|
||||||
|
// XXX is this wrong???
|
||||||
|
.flat(Infinity)
|
||||||
|
.forEach(function(item){
|
||||||
|
e.appendChild(item) })
|
||||||
|
return e },
|
||||||
// XXX add support for headless nested blocks...
|
// XXX add support for headless nested blocks...
|
||||||
nest: function(header, lst, index, path, options){
|
nest: function(header, lst, index, path, options){
|
||||||
|
var that = this
|
||||||
|
|
||||||
|
// container...
|
||||||
|
var e = document.createElement('div')
|
||||||
|
e.classList.add('list')
|
||||||
|
|
||||||
|
// localize events...
|
||||||
|
var stopPropagation = function(evt){ evt.stopPropagation() }
|
||||||
|
;(options.itemLocalEvents || [])
|
||||||
|
.forEach(function(evt){
|
||||||
|
e.addEventListener(evt, stopPropagation) })
|
||||||
|
|
||||||
|
// header...
|
||||||
|
// XXX make this optional...
|
||||||
|
e.appendChild(this.headerElem(header, index, path, options))
|
||||||
|
|
||||||
|
// items...
|
||||||
|
lst instanceof Node ?
|
||||||
|
e.appendChild(lst)
|
||||||
|
: lst instanceof Array ?
|
||||||
|
lst
|
||||||
|
.forEach(function(item){
|
||||||
|
e.appendChild(item) })
|
||||||
|
: null
|
||||||
|
|
||||||
|
header.dom = e
|
||||||
|
|
||||||
|
return e
|
||||||
},
|
},
|
||||||
|
|
||||||
//start: function(root, options){
|
|
||||||
//},
|
|
||||||
//finalize: function(sections, options){
|
|
||||||
//},
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// life-cycle...
|
||||||
|
//
|
||||||
|
start: function(root, options){
|
||||||
|
var render = object.parent(HTMLRenderer.start, this).call(this, root, options)
|
||||||
|
|
||||||
|
var browser = render.root
|
||||||
|
|
||||||
|
// prepare for maintaining the scroll position...
|
||||||
|
// XXX need to do this pre any .render*(..) call...
|
||||||
|
// ...something like:
|
||||||
|
// this.getRenderContext(render)
|
||||||
|
// should do the trick...
|
||||||
|
// another way to go might be a render object, but that seems to be
|
||||||
|
// complicating things...
|
||||||
|
var ref = render.scroll_reference =
|
||||||
|
render.scroll_reference
|
||||||
|
|| browser.focused
|
||||||
|
|| browser.pagetop
|
||||||
|
render.scroll_offset =
|
||||||
|
render.scroll_offset
|
||||||
|
|| ((ref && ref.dom && ref.dom.offsetTop) ?
|
||||||
|
ref.dom.offsetTop - ref.dom.offsetParent.scrollTop
|
||||||
|
: null)
|
||||||
|
|
||||||
|
//render.scroll_offset && console.log('renderContext:', render.scroll_offset)
|
||||||
|
|
||||||
|
return render
|
||||||
|
},
|
||||||
|
finalize: function(sections, options){
|
||||||
|
var dialog = this.root
|
||||||
|
|
||||||
|
var d = this.dialog(sections, options)
|
||||||
|
|
||||||
|
// wrap the list (nested list) of nodes in a div...
|
||||||
|
if(d instanceof Array){
|
||||||
|
var c = document.createElement('div')
|
||||||
|
d.classList.add('focusable')
|
||||||
|
d.forEach(function(e){
|
||||||
|
c.appendChild(e) })
|
||||||
|
d = c
|
||||||
|
}
|
||||||
|
d.setAttribute('tabindex', '0')
|
||||||
|
|
||||||
|
// Setup basic event handlers...
|
||||||
|
// keyboard...
|
||||||
|
// NOTE: we are not doing:
|
||||||
|
// d.addEventListener('keydown', this.keyPress.bind(this))
|
||||||
|
// because we are abstracting the user from DOM events and
|
||||||
|
// directly passing them parsed keys...
|
||||||
|
d.addEventListener('keydown', function(evt){
|
||||||
|
dialog.keyPress(dialog.keyboard.event2key(evt)) })
|
||||||
|
// focus...
|
||||||
|
d.addEventListener('click',
|
||||||
|
function(e){
|
||||||
|
e.stopPropagation()
|
||||||
|
d.focus() })
|
||||||
|
/* XXX this messes up the scrollbar...
|
||||||
|
d.addEventListener('focus',
|
||||||
|
function(){
|
||||||
|
dialog.focused
|
||||||
|
&& dialog.focused.elem.focus() })
|
||||||
|
//*/
|
||||||
|
|
||||||
|
|
||||||
|
// XXX should this be done here or in .render(..)???
|
||||||
|
dialog.dom = d
|
||||||
|
|
||||||
|
// set the scroll offset...
|
||||||
|
// XXX does not work correctly for all cases yet...
|
||||||
|
// XXX move this to a seporate method -- need to trigger this
|
||||||
|
// on render that can affect scroll position, e.g. partial
|
||||||
|
// render...
|
||||||
|
// XXX need to trigger the setup for this from .render(..) itself...
|
||||||
|
if(this.scroll_offset){
|
||||||
|
var ref = dialog.focused || dialog.pagetop
|
||||||
|
var scrolled = ref.dom.offsetParent
|
||||||
|
//scrolled.scrollTop =
|
||||||
|
// ref.elem.offsetTop - scrolled.scrollTop - this.scroll_offset
|
||||||
|
scrolled
|
||||||
|
&& (scrolled.scrollTop =
|
||||||
|
ref.elem.offsetTop - scrolled.scrollTop - this.scroll_offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
// keep focus where it is...
|
||||||
|
var focused = this.focused
|
||||||
|
focused
|
||||||
|
&& focused.elem
|
||||||
|
// XXX this will trigger the focus event...
|
||||||
|
// ...can we do this without triggering new events???
|
||||||
|
.focus()
|
||||||
|
|
||||||
|
return dialog.dom
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
@ -4158,6 +4651,7 @@ var HTMLBrowserClassPrototype = {
|
|||||||
var HTMLBrowserPrototype = {
|
var HTMLBrowserPrototype = {
|
||||||
__proto__: BaseBrowser.prototype,
|
__proto__: BaseBrowser.prototype,
|
||||||
__item__: HTMLItem,
|
__item__: HTMLItem,
|
||||||
|
__renderer__: HTMLRenderer,
|
||||||
|
|
||||||
|
|
||||||
options: {
|
options: {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user