/********************************************************************** * * * **********************************************************************/ ((typeof define)[0]=='u'?function(f){module.exports=f(require)}:define) (function(require){ var module={} // make module AMD/node compatible... /*********************************************************************/ // XXX should we guard against loading in node??? if(typeof(window) == 'undefined'){ return } // NOTE: this needs preact.js to be loaded by index.html if(typeof(preact) == 'undefined'){ console.error('Preact.js required but not present.') } var h = preact.h var object = require('lib/object') var actions = require('lib/actions') var features = require('lib/features') var core = require('features/core') var ribbons = require('imagegrid/ribbons') /*********************************************************************/ // Preact Components... // // XXX at this point this is an experiment... // XXX Questions: // - do we do align in the Preact render or outside? // - do we update preview in Preact render or outside? // class IGRibbonSet extends preact.Component { render(props, state){ var data = props.data var ribbons = data.ribbon_order.map(function(gid){ return h(IGRibbon, { gid: gid, current: data.current, base: data.base, data: data }) }) var s = props.scale || 1 return h('div', { className: 'ribbon-set', style: { transform: 'scale('+ s +', '+ s +')', }, }, [ h('div', {className: 'current-marker'}), h('div', {className: 'ribbon-locator'}, ribbons), ]) } } // render: // - ribbon // - images // - image marks // // XXX needs horizontal align... class IGRibbon extends preact.Component { render(props, state){ var data = props.data var ribbon = props.gid var images = data.ribbons[ribbon] .map(function(gid){ var marks = data.tags.selected.indexOf(gid) >= 0 ? h(IGImageMark, { gid: gid, type: 'selected', data: data, }) : [] return [ h(IGImage, { gid: gid, data: data, })].concat(marks) }) .reduce(function(a, b){ return a.concat(b) }) .filter(function(a){ return !!a }) var base = data.base == ribbon ? ['base'] : [] return h('div', { classList: ['ribbon'].concat(base).join(' '), gid: props.gid, style: { // XXX offset... }, }, images) } } // render: // - image class IGImage extends preact.Component { render(props, state){ var data = props.data || {} var gid = props.gid return h('div', { classList: ['image'] .concat(data.current == gid ? ['current'] : []) .join(' '), gid: gid || '', style: { // XXX background-image... }, // XXX handle clicks??? }) } } // render: // - image mark class IGImageMark extends preact.Component { render(props, state){ var gid = props.gid var type = props.type var data = props.data return h('div', { classList: ['mark'].concat([type]).join(' '), gid: gid, }) } } //--------------------------------------------------------------------- var RibbonsClassPrototype = { // XXX this is almost exclusively needed for determining scale... createImage: function(){ return preact.render(h(IGImage)) }, } RibbonsClassPrototype.__proto__ = ribbons.BaseRibbons.prototype.__proto__ var RibbonsPrototype = { viewer: null, dom: null, createImage: RibbonsClassPrototype.createImage, update: function(data, full){ if(!data){ return } full && this.clear() this.dom = preact.render( h(IGRibbonSet, { data: data, images: this.images || {}, scale: this.scale() || 1, }), this.viewer[0], this.dom) return this }, clear: function(){ if(this.dom){ this.dom.remove() delete this.dom } return this }, } RibbonsPrototype.__proto__ = ribbons.BaseRibbons.prototype var Ribbons = module.Ribbons = object.makeConstructor('Ribbons', RibbonsClassPrototype, RibbonsPrototype) /*********************************************************************/ // Checklist: // - full ribbons: // - build the initial DOM - DONE // - centering - DONE // - scaling - DONE (save/restore does not work) // - ribbon up/down navigation - XXX // XXX BUG: up does not hit limit on top ribbon, // similar thing sometimes happened on down... // XXX see if we need to put .focusRibbon(..) in ui??? // - shifting images - XXX // - preview setting - // - marks - // - partial ribbons: // - XXX // // var RenderActions = actions.Actions({ get dom(){ return this.ribbons ? this.ribbons.viewer : undefined }, load: [ function(data){ return function(){ // XXX setup .ribbons var viewer = data.viewer viewer = viewer == null && this.ribbons != null ? this.dom : viewer if(this.ribbons == null){ this.ribbons = Ribbons(viewer, this.images) // XXX is this correct??? //this.ribbons.__image_updaters = [this.updateImage.bind(this)] } else { //this.ribbons.clear() this.ribbons.images = this.images } this.reload() } }], // XXX do a full reload... reload: [ function(force){ // full reload... if(force == 'full'){ //this.stop() /* killAllWorkers() .done(function(){ reload() }) */ return location.reload() } // XXX is this correct here??? this.ribbons.preventTransitions() if(!this.ribbons){ return } // XXX need to get the data... // XXX this.ribbons.update(this.data, force) this.ribbons.restoreTransitions() }], // XXX refresh the previews... refresh: [ function(){ if(!this.ribbons){ return } // XXX need to get the data... // XXX this.ribbons.update(this.data) }], clear: [ function(){ this.ribbons && this.ribbons.clear() }], resizing: [ core.Event(function(unit, size, overflow){ // This is a resizing protocol root function. // // This will never be used directly, but will wrap protocol user // functions. // // As an example see: .viewScale(..) // XXX stop current animation... // XXX // XXX call .resizingDone(..) when animations done... // XXX })], viewScale: ['- Zoom/', function(scale){ if(!this.ribbons){ return } if(scale == null || scale == '?'){ return this.ribbons.scale() } this.resizing.chainCall(this, function(){ this.ribbons.scale(scale) }, 'scale', scale) }], fitImage: ['Zoom/Fit image', function(count, overflow){ if(!this.ribbons){ return } if(count == '?'){ return this.ribbons.getScreenWidthImages() } this.resizing.chainCall(this, function(){ if(count != null){ overflow = overflow == false ? 0 : overflow var o = overflow != null ? overflow : count % 2 != 1 ? 0 : (this.config['fit-overflow'] || 0) count += o } // set the scale... this.ribbons.fitImage(count) // XXX refresh image previews... }, 'screenwidth', count, overflow) }], fitRibbon: ['Zoom/Fit ribbon vertically', function(count, whole){ if(!this.ribbons){ return } if(count == '?'){ return this.ribbons.getScreenHeightRibbons() } this.resizing.chainCall(this, function(){ this.ribbons.fitRibbon(count, whole) // XXX refresh image previews... }, 'screenheight', count, whole) }], // XXX do we need updateImage here??? centerImage: ['- Interface/Center an image in ribbon horizontally', function(target, align, offset, scale){ this.ribbons && this.ribbons.centerImage(target, align, offset, scale) }], centerRibbon: ['- Interface/Center a ribbon vertically', function(target){ this.ribbons && this.ribbons.centerRibbon(target) }], ribbonRotation: ['- Interface|Ribbon/', function(angle){ // XXX }], // XXX should these be here, in ui or in ribbons??? // XXX these are identical to features/ui-ribbons.js focusRibbon: [ function(target, mode){ mode = mode || this.config['ribbon-focus-mode'] var c = this.data.getRibbonOrder() var i = this.data.getRibbonOrder(target) // NOTE: we are not changing the direction here based on // this.direction as swap will confuse the user... var direction = c < i ? 'before' : 'after' if(mode == 'visual'){ var ribbons = this.ribbons var r = this.data.getRibbon(target) var t = ribbons.getImageByPosition('current', r) if(t.length > 1){ t = t.eq(direction == 'before' ? 0 : 1) } t = ribbons.elemGID(t) this.focusImage(t, r) } }], }) var Render = module.Render = core.ImageGridFeatures.Feature({ title: '', doc: '', tag: 'ui-preact-render', exclusive: ['ui-render'], depends: [ // XXX ], actions: RenderActions, handlers: [ [[ 'shiftImageTo', 'shiftImageUp', 'shiftImageDown', 'shiftImageLeft', 'shiftImageRight', ], function(){ // XXX stub... this.reload() }], ], }) /********************************************************************** * vim:set ts=4 sw=4 : */ return module })