Last active
January 11, 2019 10:37
-
-
Save kaneel/1d3486f4bb17dedcda88924485141c7a to your computer and use it in GitHub Desktop.
building block components for Rolex mini user-guides
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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