Skip to content

Instantly share code, notes, and snippets.

@kaneel
Last active January 11, 2019 10:37
Show Gist options
  • Save kaneel/1d3486f4bb17dedcda88924485141c7a to your computer and use it in GitHub Desktop.
Save kaneel/1d3486f4bb17dedcda88924485141c7a to your computer and use it in GitHub Desktop.
building block components for Rolex mini user-guides
define('views/mini-user-guides/rlx-mini-user-guides-commons', [
'utils'
, 'lib/Model'
, 'lib/ZenView'
, 'lib/UID'
, 'modules/rlx-stylesheet'
], function(_, Model, ZView, UID, Stylesheet) { 'use strict'
var exports = {}
var Seq = exports.Seq = _.classMaker(function(Super, statics) {
/*
Static reset
Will reset the step using before handlers
*/
statics.reset = function(self) {
self.init()
}
/*
PROTOTYPE
addHandler: adds eventName handler to this.handlers[eventName]
start: throw all start handlers
pause: throw all pause handlers
stop: throw all stop handlers
rewind: throw all rewind handlers
*/
return {
constructor: function() {
this.hasStarted = false
this.handlers = {
init: []
, start: []
, pause: []
, stop: []
, rewind: []
, show: []
, hide: []
}
}
, addHandler: function(name, handler, args) {
args = Array.prototype.slice.call(arguments)
if (typeof args[0] == 'object')
return function(self, obj){
for (var k in obj) if (obj.hasOwnProperty(k))
self.addHandler(k, obj[k])
}(this, args[0])
if (typeof name != 'string')
throw new Error('Seq.prototype.addHandler: name must be string')
if (typeof handler != 'function')
throw new Error('Seq.prototype.addHandler: handler must be function')
if (typeof name != 'string' || typeof this.handlers[name] != 'object')
throw new Error('Seq.prototype.addHandler: handler name isn\'t recognised')
// everything is going to be fine
this.handlers[name].push(handler)
}
, removeHandlers: function() {
// remove all handlers
for (var k in this.handlers) if (this.handlers.hasOwnProperty(k))
this.handlers[k] = []
}
, init: function() {
if (this.handlers.init.length)
this.handlers.init.forEach(function(handler) {
handler.call(this)
}.bind(this))
}
, start: function() {
if (this.handlers.start.length)
this.handlers.start.forEach(function(handler) {
handler.call(this)
}.bind(this))
}
, pause: function() {
if (this.handlers.pause.length)
this.handlers.pause.forEach(function(handler) {
handler.call(this)
}.bind(this))
}
, stop: function() {
if (this.handlers.stop.length)
this.handlers.stop.forEach(function(handler) {
handler.call(this)
}.bind(this))
this.rewind()
}
, rewind: function() {
if (this.handlers.rewind.length)
this.handlers.rewind.forEach(function(handler) {
handler.call(this)
}.bind(this))
this.hasStarted = false
}
}
})
var MiniUGModel = exports.MiniUGModel = Model.extend(function(Super, statics) {
return {
constructor: function() {
Model.apply(this, arguments)
void function(self) {
// hook limiting the currentTab
self.hook('currentTab', function(v, tabs) {
tabs = self.get('tabs')
self.set('currentStep', 0)
if (v < -1)
return -1
else if (v > tabs.length - 1)
return tabs.length - 1
return v
})
}(this)
}
, _defaults: {
active: 0, // if active or not, clickable or not etcetc
currentStep: -1, // just a cursor number
currentTab: -1, // just a cursor number
tabs: [] // list of all tabs view as UID
}
}
})
var MiniUGTabModel = exports.MiniUGTabModel = Model.extend(function(Super, statics) {
return {
constructor: function() {
Model.apply(this, arguments)
void function(self) {
// hook limiting currentStep
self.hook('currentStep', function(v, steps) {
steps = self.get('steps')
if (v < -1)
return -1
if (v > steps.length - 1)
return steps.length - 1
return v
})
}(this)
}
, _defaults: {
active: 0, // if active or not, clickable or not etcetc
visible: 0, // if visible or not?
currentStep: -1, // just a cursor number
steps: [], // list of all steps view as UID
listitems: [] // list of all list item view as UID
}
}
})
var MiniUGStepModel = exports.MiniUGStepModel = Model.extend(function(Super, statics) {
return {
constructor: function() {
Model.apply(this, arguments)
void function(self) {
// hook limiting currentStep
self.hook('currentStep', function(v, currentTab) {
currentTab = MiniUGTabZView.getByUid(self._model.get('currentTab'))
if (v < 0)
return 0
if (v > currentTab.steps.length - 1)
return currentTab.steps.length - 1
return v
})
}(this)
}
, _defaults: {
visible: 0,
hasStarted: 0
}
}
})
var MiniUGControlButtonZView = exports.MiniUGControlButtonZView = ZView.extend(function(Super, statics) {
var instances = {}
statics.CLICK_EVT = 'mini-ug/tab-list/anchor'
statics.BUTTON_ENABLED = 'rlx-mini-user-guide__button--enabled'
statics.getByUid = function(uid) {
return instances[uid] && instances[uid].instance
}
return {
constructor: function(dict) {
dict = dict||{}
dict.active = dict.active||false
dict.uid = UID.uid()
Super.call(this, dict)
void function(self) {
instances[dict.uid] = { instance: self }
self._model.on('change', function(k, v) {
switch (k) {
case 'active':
if (v)
setActive()
else
setInactive()
break
}
})
self.root().addEventListener('click', self._onclick = function() {
if (self._model.get('active'))
self.trigger(MiniUGControlButtonZView.CLICK_EVT)
})
if (dict.active)
setActive()
else
setInactive()
function setActive() {
var fullclassname = ' '+MiniUGControlButtonZView.BUTTON_ENABLED+' '+self._model.get('className')+'--enabled'
self.root().removeAttribute("disabled");
self.root().className += fullclassname
}
function setInactive() {
var fullclassname = ' '+MiniUGControlButtonZView.BUTTON_ENABLED+' '+self._model.get('className')+'--enabled'
self.root().disabled = true
self.root().className = self.root().className.replace(fullclassname, '')
}
}(this)
}
, _template: 'button.rlx-mini-user-guide__button$className#$uid>span{$text}'
, enable: function() {
this._model.set('active', 1)
}
, disable: function() {
this._model.set('active', 0)
}
, kill: function() {
this.recover()
this.root().removeEventListener('click', this._onclick)
}
}
})
var MiniUGTabAnchorZView = exports.MiniUGTabAnchorZView = ZView.extend(function(Super, statics) {
var instances = {}
statics.CLICK_EVT = 'mini-ug/tab-list/anchor'
statics.ACTIVE_EVT = 'mini-ug/tab-list/active'
statics.ACTIVE_CLASS = 'rlx-mini-user-guide__tabs-header--active'
statics.getByUid = function(uid) {
return instances[uid] && instances[uid].instance
}
statics.forEach = function(cb) {
for (var k in instances) if (instances.hasOwnProperty(k) && typeof cb == 'function')
cb(instances[k].instance, k)
}
return {
constructor: function(dict) {
dict = dict||{}
dict.uid = UID.uid()
Super.call(this, dict)
void function(self) {
instances[dict.uid] = { instance: self }
self._model.on('change', function(k, v) {
switch(k) {
case 'active':
if (v)
activate()
else
deactivate()
break
}
})
self.root().addEventListener('click', self._onclick = function() {
self.trigger(MiniUGTabAnchorZView.CLICK_EVT)
})
function activate() {
self.trigger(MiniUGTabAnchorZView.ACTIVE_EVT)
self.root().className += ' '+MiniUGTabAnchorZView.ACTIVE_CLASS
}
function deactivate() {
self.root().className = self.root().className.replace(' '+MiniUGTabAnchorZView.ACTIVE_CLASS, '')
}
}(this)
}
, _template: 'li.rlx-mini-user-guide__tabs-header#$uid{$header}'
, activate: function() {
this._model.set('active', 1)
}
, deactivate: function() {
this._model.set('active', 0)
}
, kill: function() {
this.root().removeEventListener('click', this._onclick)
this.recover()
}
}
})
var MiniUGPopinMobile = exports.MiniUGPopinMobile = ZView.extend(function(Super, statics) {
statics.SHOW_EVT = 'mini-ug/popin/show'
statics.HIDE_EVT = 'mini-ug/popin/hide'
statics.SHOWN_EVT = 'mini-ug/popin/shown'
statics.HIDDEN_EVT = 'mini-ug/popin/hidden'
return {
constructor: function(dict) {
dict = dict||{}
dict.className = dict.className||''
dict.uid = UID.uid()
Super.call(this, dict)
void function(self) {
self.isReady = new jQuery.Deferred
Stylesheet.getCompat(function(compatTable) {
Stylesheet.stylesheet.rule(
'#'+ self.uid() +'{}'
, onstylesheetready
)
function onstylesheetready(maincss) {
maincss.property(Stylesheet.CSS_TRANSITION_PROPERTY, Stylesheet.std_transition('opacity .5s', compatTable.transform))
maincss.property('transform', 'translateY(-100%)')
self.on(MiniUGTabZView.SHOW_EVT, onshow)
self.on(MiniUGTabZView.HIDE_EVT, onhide)
self.trigger(MiniUGTabZView.SHOW_EVT)
self.query('button').addEventListener('click', onclose)
function onshow() {
maincss.property(compatTable.transform,'translateY(0)')
_.requestAnimationFrame(function() {
maincss.property('opacity', '1')
setTimeout(isShown, 500)
})
}
function onhide() {
_.requestAnimationFrame(function() {
maincss.property('opacity', '0')
setTimeout(function() {
maincss.property('transform', 'translateY(-100%)')
isHidden()
}, 500)
})
}
function isShown() {
self.trigger(MiniUGPosterZView.SHOWN_EVT)
self.trigger(MiniUGPosterZView.STOP_EVT)
}
function isHidden() {
self.trigger(MiniUGPosterZView.HIDDEN_EVT)
self.trigger(MiniUGPosterZView.START_EVT)
}
function onclose(e) {
e.preventDefault()
self.trigger(MiniUGTabZView.HIDE_EVT)
}
}
})
}(this)
}
, _template: '.rlx-mini-user-guide__popin$className#$uid>.rlx-mini-user-guide__popin-content+button.rlx-mini-user-guide__popin-close@close'
, uid: function() {
return this._model.get('uid')||(this._model.set('uid', UID.uid()),this._model.get('uid'))
}
, kill: function() {
this.recover()
this.isReady.reject()
// boom
this.innerHTML = ''
}
}
})
var MiniUGZView = exports.MiniUGZView = ZView.extend(function(Super, statics) {
var instances = {}
statics.OFF_CLASS = ' rlx-mini-user-guide--off'
statics.END_CLASS = ' rlx-mini-user-guide--end'
statics.SHOW_LAYER = 'mini-ug/layer/show'
statics.HIDE_LAYER = 'mini-ug/layer/hide'
statics.START_EVT = 'mini-ug/start'
statics.STOP_EVT = 'mini-ug/stop'
statics.END_EVT = 'mini-ug/end'
statics.KILL_EVT = 'mini-ug/kill'
statics.getByUid = function(uid) {
return instances[uid] && instances[uid].instance
}
statics.getCurrentTab = function(self) {
return MiniUGTabZView.getByUid(self._model.get('tabs')[self._model.get('currentTab')])
}
statics.isLastTab = function(self) {
return self._model.get('tabs').length - 1 == self._model.get('currentTab')
}
statics.isLastStep = function(self) {
var currentTab = statics.getCurrentTab(self)
if (currentTab)
return currentTab._model.get('steps').length - 1 == self._model.get('currentStep')
return false
}
/*
Static gather
Will return an array of tabs containing the header node for the tab as well as the list of step nodes
*/
statics.gather = function(ugnode, structure) {
structure = (structure||ugnode) ? Array.prototype.map.call(ugnode.querySelectorAll('.rlx-mini-user-guide__tab'), function(item) {
return {
tab: item.querySelectorAll('.rlx-mini-user-guide__tab')
, steps: Array.prototype.map.call(item.querySelectorAll('.rlx-mini-user-guide__step'), function(item) {
return item
})
}
}) : null
if (!structure)
throw new Error('MiniUGModel.gather: node or structure not found')
return structure
}
/*
Statics make
Makes an ugview with a node, execute a callback cb when everything is ready
Returns the ugview
*/
statics.make = function(node, handlers, clockhandlers, cb, UGZView, TabZView, StepZView, ClockZView, PosterStartZView, PosterEndZView, clockview, ugview, proms) {
UGZView = UGZView||(RLX.GLOBALS.features.isMobile ? MiniUGMobileZView : MiniUGDesktopZView)
TabZView = TabZView||(RLX.GLOBALS.features.isMobile ? MiniUGTabMobileZView : MiniUGTabDesktopZView)
StepZView = StepZView||MiniUGStepZView
ClockZView = ClockZView||MiniUGClockZView
PosterStartZView = PosterStartZView||MiniUGPosterStartZView
PosterEndZView = PosterEndZView||MiniUGPosterEndZView
proms = []
if (!(RLX.GLOBALS.features.isMobile ? MiniUGMobileZView : MiniUGDesktopZView).isImplementedBy(UGZView))
throw new Error('MiniUGZView.make: UGZView does not implement MiniUGZView')
if (!(RLX.GLOBALS.features.isMobile ? MiniUGTabMobileZView : MiniUGTabDesktopZView).isImplementedBy(TabZView))
throw new Error('MiniUGZView.make: TabZView does not implement MiniUGTabZView')
if (!MiniUGStepZView.isImplementedBy(StepZView))
throw new Error('MiniUGZView.make: StepZView does not implement MiniUGStepZView')
if (!MiniUGClockZView.isImplementedBy(ClockZView))
throw new Error('MiniUGZView.make: ClockZView does not implement MiniUGClockZView')
if (!MiniUGPosterStartZView.isImplementedBy(PosterStartZView))
throw new Error('MiniUGZView.make: PosterZView does not implement MiniUGPosterStartZView')
if (!MiniUGPosterEndZView.isImplementedBy(PosterEndZView))
throw new Error('MiniUGZView.make: PosterZView does not implement MiniUGPosterEndZView')
if (!node)
throw new Error('MiniUGZView.make: node is null')
ugview = new UGZView
void function(tablist, header) {
if (header && RLX.GLOBALS.features.isMobile)
tablist.parentNode.parentNode.insertBefore(header,tablist.parentNode)
else if (header)
tablist.parentNode.insertBefore(header,tablist)
}(ugview.query('tabs-list'), node.querySelector('.rlx-mini-user-guide__header'))
// add the clock to ugview
if (typeof clockhandlers == 'object' && clockhandlers.length)
clockview = function(clockview) {
proms.push(clockview.isReady)
ugview.addClock(clockview, clockhandlers),
clockview.appendContent(node.querySelector('.rlx-mini-user-guide__clock'))
return clockview
ugview.on(MiniUGZView.KILL_EVT, function() {
clockview.kill()
})
}(new ClockZView)
else clockview = null
// push ugview ready prom
proms.push(ugview.isReady)
// iterate on tabs
Array.prototype.forEach.call(node.querySelectorAll('.rlx-mini-user-guide__tab'), function(node, i, tabview) {
tabview = MiniUGTabZView.make(node, TabZView)
// push tab ready prom
proms.push(tabview.isReady)
// iterate on steps
Array.prototype.forEach.call(node.querySelectorAll('.rlx-mini-user-guide__step'), function(node, j, stepview, hndlrs) {
// set hndlrs as null
hndlrs = null
// create view
stepview = MiniUGStepZView.make(node, StepZView)
// push step ready prom
proms.push(stepview.isReady)
// check handlers and get the right ones
if (typeof handlers == 'object' && typeof handlers[i] == 'object' && typeof handlers[i][j] == 'object')
hndlrs = handlers[i][j]||null
// if there are handlers, add them
if (hndlrs)
stepview.addHandler(hndlrs)
// add stepview to tabview
tabview.add(stepview)
ugview.on(MiniUGZView.KILL_EVT, function() {
stepview.kill()
})
})
// add tabview to ugview
ugview.add(tabview)
ugview.on(MiniUGZView.KILL_EVT, function() {
tabview.kill()
})
})
jQuery.when.apply(jQuery, proms).then(function() {
if (clockview)
exports.initcjs(ugview, start)
else if (typeof cb == 'function')
cb(ugview, function() {
ugview.swap(0)
})
})
// add poster START
void function(rootnode, posternode, poster) {
if (!posternode) return null;
poster = new PosterStartZView({visible:1})
poster.appendContent(posternode)
poster.on(MiniUGPosterZView.HIDDEN_EVT, function() {
ugview.swap(0)
ugview.start()
})
ugview.on(MiniUGZView.STOP_EVT, function() {
ugview.swap(-1)
poster.show()
})
ugview.on(MiniUGZView.KILL_EVT, function() {
poster.kill()
})
rootnode.appendChild(poster.borrow())
}(ugview.root(), node.querySelector('.rlx-mini-user-guide__poster-start'))
node = document.getElementById('ug-test').querySelector('.rlx-mini-user-guide')
node.parentNode.replaceChild(ugview.borrow(), node)
return ugview
function start(stage) {
ugview.swap(0)
if (typeof cb == 'function')
cb(ugview, stage)
}
}
/*
Static addTabs
Will add tabs to to an ugview
You can pass an extension of MiniUGTabZView as well
*/
statics.addTabs = function(ugview, nodes, TabZView) {
TabZView = TabZView||MiniUGTabZView
Array.prototype.map.call(nodes, function(node) {
return MiniUGTabZView.make(node, TabZView)
}).forEach(function(view) {
ugview.add(view)
})
}
/*
Prototype
uid: the usual UID method
add: Add a tab passing a tab view and optionally an extension of MiniUGTabAnchorZView
disable: disabling the UG, mainly displaying a layer on top of it
enable: enabling the UG, mainly removing the layer
start: start the experience
next: next step in current tab
previous: previous step in current tab
swap: swap tabs
replaceContent: replace content in document with the view content
*/
return {
constructor: function(dict) {
dict = dict||{}
dict.className = dict.className||''
dict.uid = UID.uid()
Super.call(this, dict)
void function(self) {
instances[self.uid()] = { instance: self }
self.isReady = new jQuery.Deferred
self.on(MiniUGZView.STOP_EVT, function() {
self._model.set('currentStep', -1)
})
Stylesheet.getCompat(function(compatTable) {
Stylesheet.stylesheet.rule(
'#'+ self.uid() +'{position: relative;}'
, '#'+ self.uid() +' .rlx-mini-user-guide__layer{position:absolute;top:0; height:0;width:100%;height:100%;}'
, onstylesheetready
)
function onstylesheetready(maincss, layercss) {
self.on(MiniUGZView.SHOW_LAYER, showLayer)
self.on(MiniUGZView.HIDE_LAYER, hideLayer)
self._model.on('change', function(k, v, old) {
switch(k) {
case 'state':
if (v < 0)
self.$root().addClass(MiniUGZView.END_CLASS)
if (v <= 0)
self.$root().addClass(MiniUGZView.OFF_CLASS)
if (v > -1)
self.$root().removeClass(MiniUGZView.END_CLASS)
if (v > 0)
self.$root().removeClass(MiniUGZView.OFF_CLASS)
break
case 'active':
if (v)
self.trigger(MiniUGZView.HIDE_LAYER)
else
self.trigger(MiniUGZView.SHOW_LAYER)
break
case 'currentStep':
if (MiniUGZView.isLastStep(self) && !MiniUGZView.isLastTab(self)) {
self.disable()
} else if (MiniUGZView.isLastStep(self) && MiniUGZView.isLastTab(self)) {
self.disable(true) // full disable
} else {
self.enable()
}
break
case 'currentTab':
var from = MiniUGTabZView.getByUid(self._model.get('tabs')[old])
var to = MiniUGZView.getCurrentTab(self)
// on hidden
if (!from && to)
willshow()
else
void function() {
from.on(MiniUGTabZView.HIDDEN_EVT, function onhidden() {
// remove event
from.off(MiniUGTabZView.HIDDEN_EVT, onhidden)
// prepare on shown
if (to)
willshow()
})
// disable UG
self.deactivate()
// hide tab
from.hide()
}()
function willshow() {
to.on(MiniUGTabZView.SHOWN_EVT, function onshown() {
to.off(MiniUGTabZView.SHOWN_EVT, onshown)
// enable UG
self.activate()
})
// show tab
to._model.set('currentStep', 0)
to.show()
}
break
}
})
self.isReady.resolve()
function showLayer() {
layercss.property('display', 'block')
}
function hideLayer() {
layercss.property('display', 'none')
}
}
})
}(this)
}
, _Model: MiniUGModel
, uid: function() {
return this._model.get('uid')||(this._model.set('uid', UID.uid()),this._model.get('uid'))
}
, add: function(tabview, TabAnchorZView, tabanchorview, tabs) {
TabAnchorZView = TabAnchorZView||MiniUGTabAnchorZView
if (!MiniUGTabAnchorZView.isImplementedBy(TabAnchorZView))
throw new Error('MiniUGTabAnchorZView.prototype.add: TabAnchorZView isn\'t implementing MiniUGTabAnchorZView')
// get tabs
tabs = this._model.get('tabs')
// push new tab in tabs list
tabs.push(tabview.uid())
// set tabs in model
this._model.set('tabs', tabs)
// create a new list element
tabanchorview = new TabAnchorZView({
header: tabview._model.get('header')
, link: tabview.uid()
})
// listen to tab changes
this._model.on('change', function(k, v) {
switch(k) {
case 'currentTab':
MiniUGTabAnchorZView.forEach(function(tabanchor, uid) {
// this == _model, btw
tabanchor.deactivate()
if(tabanchor._model.get('link') == this.get('tabs')[v])
tabanchor.activate()
}.bind(this))
break
}
})
tabview._model.on('change', function(k, v) {
switch(k) {
case 'currentStep':
// update currentStep in main ug view
this._model.set('currentStep', v)
break
}
}.bind(this))
// listen to tab anchor click event
tabanchorview.on(MiniUGTabAnchorZView.CLICK_EVT, function() {
this.swap(this._model.get('tabs').indexOf(tabanchorview._model.get('link')))
}.bind(this))
// append tab header in tabs list
this.query('tabs-list').appendChild(tabanchorview.borrow())
// append tab in tabs container
this.query('tabs').appendChild(tabview.borrow())
// kill tab anchor on ug kill
this.on(MiniUGZView.KILL_EVT, function() {
tabanchorview.kill()
})
}
, addClock: function(clock, handlers) {
clock._model.on('change', function(k, v) {
if (k != 'progress') return;
var currentTab = MiniUGZView.getCurrentTab(this)
currentTab._model.set('progress', v)
}.bind(this))
this._model.on('change', function(k, v) {
switch(k) {
case 'currentTab':
clock._model.set('currentTab', v)
break
case 'currentStep':
clock._model.set('currentStep', v)
break
}
})
this._model.on('change', function(k, v) {
switch(k) {
case 'currentTab':
// on a tab change, remove previous handlers and add new ones
clock.stop()
clock.removeHandlers()
if (v>= 0
&& typeof this._model.get('currentStep') == 'number'
&& typeof handlers[v] == 'object'
&& typeof handlers[v][this._model.get('currentStep')] == 'object') {
clock.addHandler(handlers[v][this._model.get('currentStep')])
clock.init()
clock.start()
}
break
case 'currentStep':
// on a tab change, remove previous handlers and add new ones
clock.stop()
clock.removeHandlers()
if (v>= 0
&& typeof this._model.get('currentTab') == 'number'
&& typeof handlers[this._model.get('currentTab')] == 'object'
&& typeof handlers[this._model.get('currentTab')][v] == 'object') {
clock.addHandler(handlers[this._model.get('currentTab')][v])
clock.init()
clock.start()
}
break
}
}.bind(this))
this._model.set('clock', clock.uid())
this.$root().append(clock.borrow())
}
, disable: function(bool) {
if (!!bool)
this._model.set('state', -1)
else
this._model.set('state', 0)
}
, enable: function() {
this._model.set('state', 1)
}
, activate: function(bool) {
this._model.set('active', 1)
}
, deactivate: function() {
this._model.set('active', 0)
}
, start: function(cursor, startTab) {
// get current tab (aka tab 0 or cursor)
startTab = MiniUGZView.getCurrentTab(this)
// throw error if no tab
if (!startTab)
throw new Error('MiniUGZView.prototype.start: start tab not found')
// start
startTab.start()
this.trigger(MiniUGZView.START_EVT)
}
, stop: function(currentTab) {
currentTab = MiniUGZView.getCurrentTab(this)
// throw error if no tab
if (currentTab)
currentTab.stop()
this.trigger(MiniUGZView.STOP_EVT)
}
, replay: function() {
this.swap(0)
}
, next: function() {
this._model.set('currentTab', this._model.get('currentTab') + 1)
}
, previous: function() {
this._model.set('currentTab', this._model.get('currentTab') - 1)
}
, swap: function(idx) {
this._model.set('currentTab', idx)
}
, replaceContent: function(node) {
node.parentNode.replaceChild(this.borrow(), node)
}
, kill: function() {
this.recover()
this.isReady.reject()
this.trigger(MiniUGZView.KILL_EVT)
}
}
})
var MiniUGDesktopZView = exports.MiniUGDesktopZView = MiniUGZView.extend(function(Super, statics) {
return {
constructor: function(dict) {
Super.call(this, dict)
void function(self) {
self.nextbutton = new MiniUGControlButtonZView({className:' rlx-mini-user-guide__next', text: 'Next Tab'})
self.nextbutton.on(MiniUGControlButtonZView.CLICK_EVT, function() {
self.next()
})
self._model.on('change', function(k, v, old) {
switch(k) {
case 'currentStep':
if (v < 0)
self.nextbutton.disable()
if (MiniUGZView.isLastStep(self) && !MiniUGZView.isLastTab(self)) {
self.disable()
} else if (MiniUGZView.isLastStep(self) && MiniUGZView.isLastTab(self)) {
self.nextbutton.disable()
} else {
self.nextbutton.disable()
}
break
}
})
self.root().appendChild(self.nextbutton.borrow())
}(this)
}
, _template: 'article.rlx-mini-user-guide$className#$uid>\
ol.rlx-mini-user-guide__tabs-list@tabs-list\
+ .rlx-mini-user-guide__tabs@tabs\
+ .rlx-mini-user-guide__layer@layer'
, kill: function() {
Super.prototype.kill.call(this)
this.nextbutton.kill()
}
}
})
var MiniUGMobileZView = exports.MiniUGMobileZView = MiniUGZView.extend(function(Super, statics) {
return {
constructor: function(dict) {
Super.call(this, dict)
void function(self) {
// GO GO GADGETO ROLOLO
self.touchme = false
self.startX = -1
self.nowX = 0
self.oldX = 0
self.root().addEventListener('touchstart', ontouchstart)
self.root().addEventListener('touchend', ontouchend)
function ontouchstart(e) {
self.touchme = true
self.startX = e.touches[0].clientX
self.nowX = 0
self.oldX = 0
registermove()
}
function ontouchend(e) {
self.touchme = false
self.startX = -1
unregistermove()
}
function onmove(e) {
self.nowX = e.touches[0].clientX - self.startX
}
function registermove() {
document.addEventListener('touchmove', onmove)
self.oldTime = +(new Date)
self.timer = setTimeout(function timer() {
var nowTime = +(new Date)
var direction = self.nowX == self.oldX ? 0 : self.nowX > self.oldX ? 1 : -1
var delta = (direction == 0) ? 0 : direction > 0 ? self.nowX - self.oldX : self.oldX - self.nowX
var acc = delta/(nowTime - self.oldTime)
var distance = direction == 0 ? 0 : direction > 0 ? self.nowX - self.startX : self.startX - self.nowX
if (acc > 5 && distance > 50)
move(direction)
else if (acc < 5 && distance > 200)
move(direction)
self.oldTime = nowTime
self.oldX = self.nowX
self.timer = setTimeout(timer, 16)
}, 16)
}
function unregistermove() {
clearTimeout(self.timer)
document.removeEventListener('touchmove', onmove)
}
function move(direction) {
unregistermove()
if (direction > 0)
previous(MiniUGZView.getCurrentTab(self))
else if (direction < 0)
next(MiniUGZView.getCurrentTab(self))
}
function next(currTab) {
if (currTab)
currTab.next()
}
function previous(currTab) {
if (currTab)
currTab.previous()
}
}(this)
}
, _template: 'article.rlx-mini-user-guide$className#$uid>\
(.rlx-mini-user-guide__tabs-list-container>ol.rlx-mini-user-guide__tabs-list@tabs-list)\
+ .rlx-mini-user-guide__tabs@tabs\
+ .rlx-mini-user-guide__layer@layer'
, kill: function() {
Super.prototype.kill.call(this)
this.nextbutton.kill()
}
}
})
var MiniUGStepListItemZView = exports.MiniUGStepListItemZView = ZView.extend(function(Super, statics) {
var instances = {}
statics.ACTIVE_CLASS = 'rlx-mini-user-guide__step-item--active'
statics.CLICK_EVT = 'mini-ug/step-list-item/click'
statics.getByUid = function(uid) {
return instances[uid] && instances[uid].instance
}
return {
constructor: function(dict) {
dict = dict||{}
dict.width = dict.width||40
dict.stroke = dict.stroke||2
dict.uid = UID.uid()
Super.call(this, dict)
void function(self) {
instances[self.uid()] = { instance: self }
self.canvas = self.query('timeline')
self.canvas.width = dict.width
self.canvas.height = dict.width
window.changedraw = function(v) {
self._model.set('percent', v)
}
self.context = self.canvas.getContext('2d')
self._model.on('change', function(k, v) {
switch(k) {
case 'active':
self.progress(0)
if(v)
self.root().className += ' '+MiniUGStepListItemZView.ACTIVE_CLASS
else
self.root().className = self.root().className.replace(' '+MiniUGStepListItemZView.ACTIVE_CLASS, '')
break
case 'percent':
var angle = 360 * (v/100), radians = 0
if (v == 0)
angle = -Math.PI/2
else if (angle <= 90)
angle = 360-angle
else if (angle > 90 && angle <= 180)
angle = 360 - angle
else if (angle > 180 && angle <= 270)
angle = -angle
else if (angle > 270 && angle < 360)
angle = 360 - angle
else if (angle == 360)
angle = 360
radians = angle * (Math.PI / 180)
self.context.clearRect(0, 0, dict.width, dict.width)
self.context.beginPath()
self.context.arc(dict.width/2, dict.width/2, (dict.width/2) - dict.stroke*2, -Math.PI/2, -Math.PI/2+radians , true)
self.context.lineWidth = dict.stroke;
self.context.stroke()
break
}
})
self.root().addEventListener('click', self._onclick = function() {
if (!self._model.get('active'))
self.trigger(MiniUGStepListItemZView.CLICK_EVT, [self._model.get('index')])
})
}(this)
}
, _template: 'li.rlx-mini-user-guide__step-item>canvas.rlx-mini-user-guide__step-timeline@timeline+span.rlx-mini-user-guide__step-index{$index}'
, uid: function() {
return this._model.get('uid')||(this._model.set('uid', UID.uid()),this._model.get('uid'))
}
, progress: function(v) {
this._model.set('percent', v)
}
, kill: function() {
this.recover()
this.root().removeEventListener('click', this._onclick)
}
}
})
var MiniUGPosterZView = exports.MiniUGPosterZView = ZView.extend(function(Super, statics) {
var instances = {}
statics.SHOW_EVT = 'mini-ug/tab/show'
statics.HIDE_EVT = 'mini-ug/tab/hide'
statics.SHOWN_EVT = 'mini-ug/tab/shown'
statics.HIDDEN_EVT = 'mini-ug/tab/hidden'
statics.START_EVT = 'mini-ug/tab/start'
statics.getByUid = function(uid) {
return instances[uid] && instances[uid].instance
}
return {
constructor: function(dict) {
dict = dict||{}
dict.uid = UID.uid()
Super.call(this, dict)
void function(self) {
instances[self.uid()] = { instance: self }
self.isReady = new jQuery.Deferred
Stylesheet.getCompat(function(compatTable) {
Stylesheet.stylesheet.rule(
'#'+ self.uid() +'{ opacity: 0; }'
, onstylesheetready
)
function onstylesheetready(maincss) {
maincss.property(Stylesheet.CSS_TRANSITION_PROPERTY, Stylesheet.std_transition('opacity .5s', compatTable.transform))
maincss.property('transform', 'translateY(-100%)')
self.on(MiniUGTabZView.SHOW_EVT, onshow)
self.on(MiniUGTabZView.HIDE_EVT, onhide)
self._model.on('change', onmodelchange)
self.isReady.resolve()
if (dict.visible)
self.trigger(MiniUGPosterZView.SHOW_EVT)
else
self.trigger(MiniUGPosterZView.HIDE_EVT)
function onmodelchange(k, v, old) {
switch(k) {
case 'visible':
if(v)
self.trigger(MiniUGPosterZView.SHOW_EVT)
else
self.trigger(MiniUGPosterZView.HIDE_EVT)
break
}
}
function onshow() {
maincss.property(compatTable.transform,'translateY(0)')
_.requestAnimationFrame(function() {
maincss.property('opacity', '1')
setTimeout(isShown, 500)
})
}
function onhide() {
_.requestAnimationFrame(function() {
maincss.property('opacity', '0')
setTimeout(function() {
maincss.property('transform', 'translateY(-100%)')
isHidden()
}, 500)
})
}
function isShown() {
self.trigger(MiniUGPosterZView.SHOWN_EVT)
self.trigger(MiniUGPosterZView.STOP_EVT)
}
function isHidden() {
self.trigger(MiniUGPosterZView.HIDDEN_EVT)
self.trigger(MiniUGPosterZView.START_EVT)
}
}
})
}(this)
}
, _template: '.rlx-mini-user-guide__poster$className#$uid'
, uid: function() {
return this._model.get('uid')||(this._model.set('uid', UID.uid()),this._model.get('uid'))
}
, appendContent: function(node) {
// using jQuery to get rid of potential script and such
this.$root().append(jQuery(node.cloneNode(true)).html())
}
, show: function() {
this._model.set('visible', 1)
}
, hide: function() {
this._model.set('visible', 0)
}
, kill: function() {
this.recover()
this.isReady.reject()
}
}
})
var MiniUGPosterStartZView = exports.MiniUGPosterStartZView = MiniUGPosterZView.extend(function(Super, statics) {
statics.START_EVT = 'mini-ug/tab/start'
return {
constructor: function(dict) {
Super.call(this, dict)
void function(self) {
self.startbutton = new MiniUGControlButtonZView({
className: ' rlx-mini-user-guide__start'
, text: 'Start the user guide'
, active: 1
})
self.startbutton.on(MiniUGControlButtonZView.CLICK_EVT, onstart)
self.root().appendChild(self.startbutton.borrow())
function onstart(e) {
self.hide()
}
}(this)
}
, _template: '.rlx-mini-user-guide__poster.rlx-mini-user-guide__poster-start#$uid'
, kill: function() {
this.startbutton.kill()
this.recover()
}
}
})
var MiniUGPosterEndZView = exports.MiniUGPosterEndZView = MiniUGPosterZView.extend(function(Super, statics) {
statics.START_EVT = 'mini-ug/tab/start'
return {
constructor: function(dict) {
Super.call(this, dict)
void function(self) {
self.replaybutton = new MiniUGControlButtonZView({
className: ' rlx-mini-user-guide__replay'
, text: 'Replay '
, active: 1
})
self.replaybutton.on(MiniUGControlButtonZView.CLICK_EVT, onreplay)
self.root().appendChild(self.replaybutton.borrow())
function onreplay(e) {
self.hide()
}
}(this)
}
, _template: '.rlx-mini-user-guide__poster.rlx-mini-user-guide__poster-start#$uid'
, kill: function() {
this.replaybutton.kill()
this.recover()
}
}
})
var MiniUGTabZView = exports.MiniUGTabZView = ZView.extend(function(Super, statics) {
var instances = {}
statics.SHOW_EVT = 'mini-ug/tab/show'
statics.HIDE_EVT = 'mini-ug/tab/hide'
statics.SHOWN_EVT = 'mini-ug/tab/shown'
statics.HIDDEN_EVT = 'mini-ug/tab/hidden'
statics.CONTROLS_DISABLED = 'rlx-mini-user-guide__tab-controls--disabled'
statics.PREV_DISABLED = 'rlx-mini-user-guide__tab-controls-previous--disabled'
statics.NEXT_DISABLED = 'rlx-mini-user-guide__tab-controls-next--disabled'
statics.getByUid = function(uid) {
return instances[uid] && instances[uid].instance
}
statics.hideAllSteps = function(self, steps) {
steps = steps||self._model.get('steps')
steps.map(function(uid) {
return MiniUGStepZView.getByUid(uid)
}).forEach(function(item) {
item.hide()
})
}
statics.hideAllListItems = function(self, items) {
items = items||self._model.get('listitems')
items.map(function(uid) {
return MiniUGStepListItemZView.getByUid(uid)
}).forEach(function(item) {
item._model.set('active', 0)
})
}
/*
Static getCurrentStep
Takes an instance or extension of MiniUGTabZView
Return the current active step of the tab
*/
statics.getCurrentStep = function(self) {
return MiniUGStepZView.getByUid(self._model.get('steps')[self._model.get('currentStep')])
}
statics.make = function(node, TabZView, header) {
TabZView = TabZView||MiniUGTabZView
header = node.querySelector('.rlx-mini-user-guide__tab-header')
if (!header)
throw new Error('MiniUGTabZView.make: no header node')
return new TabZView({header: header.textContent||header.innerText})
}
/*
Static addSteps
Takes a tabview, nodes and optional StepZView (must be an extension of MiniUGStepZView)
Makes a
*/
statics.addSteps = function(tabview, nodes, StepZView) {
StepZView = StepZView||MiniUGStepZView
if (!MiniUGStepZView.isImplementedBy(StepZView))
throw new Error('MiniUGTabZView.addSteps: Given StepZView isn\'t implementing of MiniUGStepZView')
return Array.prototype.map.call(nodes, function(node) {
return new StepZView(node)
}).forEach(function(view) {
tabview.add(view)
})
}
statics.makeProtoTabs = function(nodes, handlers, cb) {
Array.prototype.map.call(nodes, function(item, i, tabview, proms) {
tabview = new MiniUGTabZView
proms = []
proms.push(tabview.isReady)
// step test
Array.prototype.map.call(item.querySelectorAll('.rlx-mini-user-guide__step'), function(item, i, view) {
view = MiniUGStepZView.makePrototype(item, handlers[i])
tabview.add(view)
proms.push(view.isReady)
return view
})
jQuery.when.apply(jQuery, proms)
.then(function() {
// now replace item with tabview on the document
item.parentNode.replaceChild(tabview.borrow(), item)
tabview._model.set('currentStep', 0)
tabview.show()
if (typeof cb == 'function')
cb(tabview)
})
})
}
return {
constructor: function(dict) {
dict = dict||{}
dict.uid = UID.uid()
Super.call(this, dict)
void function(self) {
instances[self.uid()] = { instance: self }
self.replaybutton = new MiniUGControlButtonZView({
className: ' rlx-mini-user-guide__tab-controls-replay'
, text: 'Replay animation' // NEED I18N I THINK
})
self.replaybutton.on(MiniUGControlButtonZView.CLICK_EVT, onreplay)
self.query('controls').appendChild(self.replaybutton.borrow())
self.isReady = new jQuery.Deferred
self._timer = null
Stylesheet.getCompat(function(compatTable) {
Stylesheet.stylesheet.rule(
'#'+ self.uid() +'{ position: absolute; width: 100%; top: 0; left: 0; opacity: 0; }'
, '#'+ self.uid() +' .rlx-mini-user-guide__tab-content{ position: relative; overflow: hidden; }'
, onstylesheetready
)
function onstylesheetready(maincss) {
maincss.property(Stylesheet.CSS_TRANSITION_PROPERTY, Stylesheet.std_transition('opacity .5s', compatTable.transform))
maincss.property('transform', 'translateY(-100%)')
self.on(MiniUGTabZView.SHOW_EVT, onshow)
self.on(MiniUGTabZView.HIDE_EVT, onhide)
self._model.on('change', onmodelchange)
self.isReady.resolve()
function onmodelchange(k, v, old) {
switch(k) {
case 'progress':
var items = self._model.get('listitems')
var item = MiniUGStepListItemZView.getByUid(items[self._model.get('currentStep')])
if (item)
item.progress(v*100)
break
case 'currentStep':
var from, to, olditem, toitem;
var items = self._model.get('listitems')
MiniUGTabZView.hideAllSteps(self)
MiniUGTabZView.hideAllListItems(self, items)
to = MiniUGTabZView.getCurrentStep(self)
toitem = MiniUGStepListItemZView.getByUid(items[v])
if (toitem)
toitem._model.set('active', true)
if (v == self._model.get('steps').length-1)
isLast()
else
isNotLast()
to.show()
break
case 'visible':
if(v)
self.trigger(MiniUGTabZView.SHOW_EVT)
else
self.trigger(MiniUGTabZView.HIDE_EVT)
break
}
}
function isLast() {
self.$query('controls').addClass(MiniUGTabZView.CONTROLS_DISABLED)
}
function isNotLast() {
self.$query('controls').removeClass(MiniUGTabZView.CONTROLS_DISABLED)
}
function onshow() {
clearTimeout(self._timer)
maincss.property(compatTable.transform,'translateY(0)')
_.requestAnimationFrame(function() {
maincss.property('opacity', '1')
self._timer = setTimeout(isShown, 500)
})
}
function onhide() {
clearTimeout(self._timer)
_.requestAnimationFrame(function() {
maincss.property('opacity', '0')
self._timer = setTimeout(function() {
maincss.property('transform', 'translateY(-100%)')
isHidden()
}, 500)
})
}
function isShown() {
MiniUGTabZView.getCurrentStep(self).show()
self.trigger(MiniUGTabZView.SHOWN_EVT)
}
function isHidden() {
MiniUGTabZView.getCurrentStep(self).hide()
self.trigger(MiniUGTabZView.HIDDEN_EVT)
}
}
})
function onnext(e) {
self.next()
}
function onprevious(e) {
self.previous()
}
function onreplay(e) {
self.goTo(0)
}
}(this)
}
, _Model: MiniUGTabModel
, _template: '.rlx-mini-user-guide__tab#$uid>\
.rlx-mini-user-guide__tab-content@content\
+ ul.rlx-mini-user-guide__steps-list@list\
+ .rlx-mini-user-guide__tab-controls@controls'
, uid: function() {
return this._model.get('uid')||(this._model.set('uid', UID.uid()),this._model.get('uid'))
}
, add: function(stepview, steps, listitems, listitem, idx) {
// get current step and item lists
steps = this._model.get('steps')
listitems = this._model.get('listitems')
// push a new uid in steps and save index
idx = steps.push(stepview.uid()) - 1
// set back
this._model.set('steps', steps)
// add to content
this.query('content').appendChild(stepview.borrow())
// create a list item
listitem = new MiniUGStepListItemZView({link: stepview.uid(), index: idx})
// push in list
listitems.push(listitem.uid())
// set back
this._model.set('listitems', listitems)
// add a click event
listitem.on(MiniUGStepListItemZView.CLICK_EVT, function(idx) {
// on click, go to idx
this.goTo(idx)
}.bind(this))
// append
this.query('list').appendChild(listitem.borrow())
}
, start: function(currentStep) {
currentStep = MiniUGTabZView.getCurrentStep(this)
if (!currentStep)
throw new Error('MiniUGTabZView.prototype.start: currentStep not found')
currentStep.start()
}
, pause: function(currentStep) {
currentStep = MiniUGTabZView.getCurrentStep(this)
if (!currentStep)
throw new Error('MiniUGTabZView.prototype.pause: currentStep not found')
currentStep.pause()
}
, stop: function(currentStep) {
currentStep = MiniUGTabZView.getCurrentStep(this)
if (!currentStep)
throw new Error('MiniUGTabZView.prototype.stop: currentStep not found')
currentStep.stop()
this.hide()
}
, show: function() {
this._model.set('visible', 1)
}
, hide: function() {
this._model.set('visible', 0)
}
, goTo: function(i) {
if (typeof i != 'number')
throw new Error('MiniUGTabZView.prototype.goTo: Index not a number')
if (!this._model.get('steps')[i])
throw new Error('MiniUGTabZView.prototype.goTo: Wrong index, step does not exist')
this._model.set('currentStep', i)
}
, next: function() {
this._model.set('currentStep', this._model.get('currentStep') + 1)
}
, previous: function() {
this._model.set('currentStep', this._model.get('currentStep') - 1)
}
, kill: function() {
this.recover()
clearTimeout(this._timer)
this.replaybutton.kill()
}
}
})
var MiniUGTabDesktopZView = exports.MiniUGTabDesktopZView = MiniUGTabZView.extend(function(Super, statics) {
return {
constructor: function(dict) {
dict = dict||{}
Super.call(this, dict)
void function(self) {
self.prevbutton = new MiniUGControlButtonZView({
className: ' rlx-mini-user-guide__tab-controls-previous'
, text: 'PREVIOUS STEP'
})
self.nextbutton = new MiniUGControlButtonZView({
className: ' rlx-mini-user-guide__tab-controls-next'
, text: 'NEXT STEP'
})
self.prevbutton.on(MiniUGControlButtonZView.CLICK_EVT, onprevious)
self.nextbutton.on(MiniUGControlButtonZView.CLICK_EVT, onnext)
self.query('controls').appendChild(self.prevbutton.borrow())
self.query('controls').appendChild(self.nextbutton.borrow())
self.isReady = new jQuery.Deferred
self._model.on('change', onmodelchange)
function onmodelchange(k, v, old) {
switch(k) {
case 'currentStep':
var from, to, olditem, toitem;
var items = self._model.get('listitems')
if (v == 0)
self.prevbutton.disable()
else
self.prevbutton.enable()
if (v == self._model.get('steps').length-1)
isLast()
else
isNotLast()
break
}
}
function isLast() {
self.nextbutton.disable()
self.replaybutton.enable()
}
function isNotLast() {
self.nextbutton.enable()
self.replaybutton.disable()
}
function onnext(e) {
self.next()
}
function onprevious(e) {
self.previous()
}
}(this)
}
, _template: '.rlx-mini-user-guide__tab#$uid>\
.rlx-mini-user-guide__tab-content@content\
+ ul.rlx-mini-user-guide__steps-list@list\
+ .rlx-mini-user-guide__tab-controls@controls'
, kill: function() {
Super.prototype.kill.call(this)
this.prevbutton.kill()
this.nextbutton.kill()
}
}
})
var MiniUGTabMobileZView = exports.MiniUGTabMobileZView = MiniUGTabZView.extend(function(Super, statics) {
return {
constructor: function(dict) {
dict = dict||{}
Super.call(this, dict)
void function(self) {
}(this)
}
, _template: '.rlx-mini-user-guide__tab#$uid>\
.rlx-mini-user-guide__tab-content@content\
+ ul.rlx-mini-user-guide__steps-list@list\
+ .rlx-mini-user-guide__tab-controls@controls'
, kill: function() {
Super.prototype.kill.call(this)
}
}
})
var MiniUGClockZView = exports.MiniUGClockZView = _.classMaker(ZView, Seq, function(Super, statics) {
var instances = {}
statics.getByUid = function(uid) {
return instances[uid] && instances[uid].instance
}
statics.makeProtoClock = function(nodes, handlers, cb) {
nodes.forEach(function(node, i, clock, div, play, prev, next, cursor, selfhandler) {
clock = new MiniUGClockZView
selfhandler = handlers[i]
cursor = 0
clock.appendContent(node)
// create control wrapper
div = document.createElement('div')
div.className = 'rlx-mini-user-guide__clock-controls'
// create play button
play = document.createElement('button')
play.textContent = 'START'
play.addEventListener('click', onplay)
// create previous button
prev = document.createElement('button')
prev.textContent = 'PREVIOUS'
prev.addEventListener('click', onprev)
// create next button
next = document.createElement('button')
next.textContent = 'NEXT'
next.addEventListener('click', onnext)
// wrap
div.appendChild(prev)
div.appendChild(play)
div.appendChild(next)
// add to content
clock.root().appendChild(div)
// once it's ready
clock.isReady.then(function() {
// replace in document
node.parentNode.replaceChild(clock.borrow(), node)
// add handlers to cursor 0
clock.addHandler(selfhandler[cursor])
// init
clock.init()
// exec callback
if (typeof cb == 'function')
cb(clock)
})
function onplay() {
clock.start()
}
function onnext() {
clock.removeHandlers()
cursor+=1
if (cursor >= handlers.length - 1)
cursor = handlers.length - 1
clock.addHandler(selfhandler[cursor])
clock.init()
clock.start()
}
function onprev() {
clock.removeHandlers()
cursor-= 1
if(cursor < 0)
cursor=0
clock.addHandler(selfhandler[cursor])
clock.init()
clock.start()
}
})
}
/*
PROTOTYPE
uid: classic uid
appendContent: append and init
*/
return {
constructor: function(dict) {
dict = dict||{}
dict.uid = UID.uid()
ZView.call(this, dict)
Seq.call(this)
void function(self) {
instances[self.uid()] = { instance: self }
self.isReady = new jQuery.Deferred
Stylesheet.getCompat(function(compatTable) {
Stylesheet.stylesheet.rule(
'#'+ self.uid() +'{ opacity: 1; }'
, onstylesheetready
)
function onstylesheetready(maincss) {
self.isReady.resolve()
}
})
}(this)
}
, _template: '.rlx-mini-user-guide__clock#$uid>.rlx-mini-user-guide__clock-content@content'
, uid: function() {
return this._model.get('uid')||(this._model.set('uid', UID.uid()),this._model.get('uid'))
}
, update: function(progress) {
this._model.set('progress', progress)
}
, appendContent: function(node) {
// using jQuery to get rid of potential script and such
this.$query('content').append(jQuery(node.cloneNode(true)).html())
this.init()
}
, kill: function() {
this.recover()
}
}
})
var MiniUGStepZView = exports.MiniUGStepZView = _.classMaker(ZView, Seq, function(Super, statics) {
var instances = {}
statics.SHOW_EVT = 'mini-ug/step/show'
statics.HIDE_EVT = 'mini-ug/step/hide'
statics.SHOWN_EVT = 'mini-ug/step/shown'
statics.HIDDEN_EVT = 'mini-ug/step/hidden'
statics.getByUid = function(uid) {
return instances[uid] && instances[uid].instance
}
/*
Static make
Takes a node, an optional StepZView and optional handlers
Make an instance of StepZView, add the node to the view and return the instance
*/
statics.make = function(node, handlers, StepZView, ugstep) {
StepZView = StepZView||MiniUGStepZView
ugstep = new StepZView()
// check and add handlers
if (typeof handlers == 'object')
ugstep.addHandler(handlers)
// add node to view
ugstep.appendContent(node)
// returns the new view
return ugstep
}
/*
Static makePrototype
Takes a node, an optional StepZView and optional handlers
Like Static make but adds a start button to throw start handlers.
Only used by prototype pages.
*/
statics.makePrototype = function(node, handlers, StepZView, ugstep, buttonStart, buttonReset, wrap) {
ugstep = MiniUGStepZView.make(node, handlers, StepZView)
// create a button wrapper
wrap = document.createElement('div')
wrap.className = ' rlx-mini-user-guide__step-controls'
// add a start button to the layer to start the animation
buttonStart = document.createElement('button')
buttonStart.classList.add('rlx-mini-user-guide__start-button')
buttonStart.textContent = 'Play'
wrap.appendChild(buttonStart)
buttonStart.addEventListener('click', function() {
ugstep.init()
ugstep.start()
})
ugstep.root().appendChild(wrap)
// return the view
return ugstep
}
/*
Static makeSteps
Takes node, loop, create with statics.makePrototype
*/
statics.makeProtoSteps = function(nodes, handlers, cb) {
Array.prototype.map.call(nodes, function(item, i, view) {
// create view with given handlers
view = MiniUGStepZView.makePrototype(item, handlers[i])
// replace the node on the document
item.parentNode.replaceChild(view.borrow(), item)
return view
}).forEach(function(item, i) {
// when steps is ready
item.isReady.then(function() {
// show it baby
item.show()
if (typeof cb == 'function')
cb(item)
})
})
}
/*
PROTOTYPE
uid: Usual suspect.
appendContent: adds a node to @content
show: show following show handlers or basic opacity animation
hide: hide following show handlers or basic opacity animation
*/
return {
constructor: function(dict) {
dict = dict||{}
dict.uid = UID.uid()
ZView.call(this, dict)
Seq.call(this)
void function(self) {
instances[self.uid()] = { instance: self }
self.isReady = new jQuery.Deferred
Stylesheet.getCompat(function(compatTable) {
Stylesheet.stylesheet.rule(
'#'+ self.uid() +'{ position: absolute; width: 100%; top: 0; left: 0; opacity: 0; }'
, onstylesheetready
)
function onstylesheetready(maincss) {
maincss.property(Stylesheet.CSS_TRANSITION_PROPERTY, Stylesheet.std_transition('opacity .5s', compatTable.transform))
maincss.property('transform', 'translateY(-100%)')
self.on(MiniUGStepZView.SHOW_EVT, onshow)
self.on(MiniUGStepZView.HIDE_EVT, onhide)
self.on(MiniUGStepZView.SHOWN_EVT, onshown)
self._model.on('change', onmodelchange)
self.isReady.resolve()
self.timer = null
function onshow() {
self.init()
if (self.handlers.show.length)
self.handlers.show.forEach(function(handler) {
handler(isShown)
})
else
show()
}
function onshown() {
self.start()
}
function onhide() {
if (self.handlers.hide.length)
self.handlers.show.forEach(function(handler) {
handler(isHidden)
})
else
hide()
}
function show() {
clearTimeout(self.timer)
maincss.property(compatTable.transform,'translateY(0)')
maincss.property('opacity', '1')
self.timer = setTimeout(function() {
isShown()
}, 500)
}
function hide() {
clearTimeout(self.timer)
maincss.property('opacity', '0')
self.timer = setTimeout(function() {
maincss.property('transform', 'translateY(-100%)')
isHidden()
}, 500)
}
function isShown() {
self.trigger(MiniUGStepZView.SHOWN_EVT)
}
function isHidden() {
self.trigger(MiniUGStepZView.HIDDEN_EVT)
}
function onmodelchange(k, v, old) {
switch(k) {
case 'visible':
if(v)
self.trigger(MiniUGStepZView.SHOW_EVT)
else
self.trigger(MiniUGStepZView.HIDE_EVT)
break
}
}
}
})
}(this)
}
, _template: '.rlx-mini-user-guide__step#$uid>.rlx-mini-user-guide__step-content@content'
, _Model: MiniUGStepModel
, uid: function() {
return this._model.get('uid')||(this._model.set('uid', UID.uid()),this._model.get('uid'))
}
, appendContent: function(node) {
// using jQuery to remove potential script and stuff
this.$query('content').append(jQuery(node.cloneNode(true)).html())
this.init()
}
, show: function() {
this._model.set('visible', 1)
}
, hide: function() {
this._model.set('visible', 0)
}
, kill: function() {
clearTimeout(this.timer)
this.recover()
}
}
})
exports.initcjs = function initSequence(ug, cb) {
if (!ug)
return null
var clock = MiniUGClockZView.getByUid(ug._model.get('clock'))
, canvas, anim_container, dom_overlay_container, images, ss, loader, stage, fnStartAnimation
canvas = clock.$root().find('canvas').get(0)
anim_container = ug.$root().find('.animation_container').get(0)
dom_overlay_container = ug.$root().find('.dom_overlay_container').get(0)
images = window.images||{}
ss = window.ss||{}
loader = new window.createjs.LoadQueue(false)
loader.addEventListener("fileload", handleFileLoad)
loader.addEventListener("complete", handleComplete)
loader.loadManifest(lib.properties.manifest)
function handleFileLoad(evt) {
if (evt.item.type == "image")
images[evt.item.id] = evt.result;
}
function handleComplete(evt) {
//This function is always called, irrespective of the content. You can use the variable "stage" after it is created in token create_stage.
var queue = evt.target, ssMetadata = lib.ssMetadata
ssMetadata.forEach(function(item) {
ss[item.name] = new window.createjs.SpriteSheet( {"images": [queue.getResult(item.name)], "frames": item.frames} )
})
stage = new window.createjs.Stage(canvas)
if (typeof cb == 'function')
cb(stage)
//Code to support hidpi screens and responsive scaling.
function makeResponsive(isResp, respDim, isScale, scaleType) {
var lastW, lastH, lastS=1
window.addEventListener('resize', resizeCanvas)
resizeCanvas()
function resizeCanvas() {
var w = lib.properties.width, h = lib.properties.height
var iw = window.innerWidth, ih=window.innerHeight
var pRatio = window.devicePixelRatio || 1, xRatio=iw/w, yRatio=ih/h, sRatio=1
if(isResp) {
if((respDim=='width'&&lastW==iw) || (respDim=='height'&&lastH==ih)) {
sRatio = lastS;
}
else if(!isScale) {
if(iw<w || ih<h)
sRatio = Math.min(xRatio, yRatio);
}
else if(scaleType==1) {
sRatio = Math.min(xRatio, yRatio);
}
else if(scaleType==2) {
sRatio = Math.max(xRatio, yRatio);
}
}
canvas.width = w*pRatio*sRatio
canvas.height = h*pRatio*sRatio
// canvas.style.width = dom_overlay_container.style.width = anim_container.style.width = w*sRatio+'px';
// canvas.style.height = anim_container.style.height = dom_overlay_container.style.height = h*sRatio+'px';
stage.scaleX = pRatio*sRatio
stage.scaleY = pRatio*sRatio
lastW = iw;
lastH = ih
lastS = sRatio
}
}
makeResponsive(false,'both',false,1)
// Registers the "tick" event listener.
createjs.Ticker.setFPS(lib.properties.fps)
createjs.Ticker.addEventListener("tick", stage)
}
}
return exports
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment