UNPKG

toloframework

Version:

Javascript/HTML/CSS compiler for Firefox OS or nodewebkit apps using modules in the nodejs style.

397 lines (330 loc) 9.7 kB
"use strict"; /** @module dom.fx */ require('dom.fx', function (require, module, exports) { var _ = function () { var D = { "en": {} }, X = require("$").intl; function _() { return X(D, arguments); } _.all = D; return _; }(); "use strict"; require("polyfill.promise"); var $ = require("dom"), DB = require("tfw.data-binding"); var EMPTY_FUNC = function EMPTY_FUNC() {// Do nothing. }; // Unique ID. var ID = 1; var delay = window.setTimeout; /** * @class Fx * @param {string} name - Optional name of this animation chain. */ function Fx(name) { // Because it is possible to call `end()` before the sequence is // over, we need to know in what session we are. This way, if a // callback is called after the end, it can know that it must not // execute the next task. this._session = {}; // A name can be defined for debug purpose, but it is not used right now. Object.defineProperty(Fx.prototype, 'name', { value: name, writable: false, configurable: true, enumerable: true }); this._name = name; // Sequence of tasks. A task is a function with two parameters: // the `next()` function and a boolean `async` which tells you if // your are allowed to call async functions. // When a task is called in the `end()` function, it is not // allowed to call async functions. this._tasks = []; // Index of the current task. this._index = 0; this._started = false; this._startTime = 0; this._onEnd = EMPTY_FUNC; } /** * @param {function} _onEnd - Function to call as soon as the animation ends. * @returns {undefined} */ Fx.prototype.start = function (_onEnd) { var onEnd = typeof _onEnd !== "undefined" ? _onEnd : EMPTY_FUNC; if (this._started) this.end(); this._onEnd = onEnd; this._started = true; this._index = 0; this._session = { $id: ID++ }; this._startTime = Date.now(); next.call(this, this._session); }; /** * Next step of the animation. * @this Fx * @param {object} session - ``{ $id } * @returns {undefined} */ function next(session) { if (session !== this._session) return; if (this._index >= this._tasks.length) { this._index = 0; this._started = false; delete this._session; this._onEnd(this); return; } var that = this, tsk = this._tasks[this._index++]; if (this._debug) { console.info("[dom.fx] tsk[".concat(this._index - 1, "]: "), tsk.label, "(".concat(Date.now() - this._startTime, " ms)"), tsk.args, session); } tsk(function () { delay(next.bind(that, session)); }, true); } Fx.prototype.end = function () { if (!this._started) return this; var that = this; this._started = false; delete this._session; while (this._index < this._tasks.length) { var tsk = this._tasks[this._index++]; if (that._debug) { console.info("[dom.fx.end] tsk[" + (this._index - 1) + "]: ", tsk.label, tsk.args); } tsk(EMPTY_FUNC, false); } this._onEnd(this); return this; }; /** * @member Fx.debug * @param value */ Fx.prototype.debug = function (value) { this.addTask(function (next) { this._debug = value ? true : false; next(); }); return this; }; /** * @return void */ Fx.prototype.addTask = function (task, label, args) { task.label = label; task.args = args; this._tasks.push(task); return this; }; /** * @return void */ Fx.prototype.log = function (msg) { this.addTask(function (next) { console.log("[dom.fx]", msg); next(); }, 'log'); return this; }; /** * Stop execution of the animation until end() has been called. */ Fx.prototype.pause = function () { this.addTask(EMPTY_FUNC, 'pause'); return this; }; Fx.prototype.exec = function () { var args = Array.prototype.slice.call(arguments); this.addTask(function (next, session) { args.forEach(function (arg) { try { if (typeof arg === 'function') arg(session);else console.log('[dom.fx]', arg); } catch (ex) { console.error(ex); } }); next(); }, 'exec', args); return this; }; ['css', 'addClass', 'removeClass', 'toggleClass', 'detach', 'saveStyle', 'restoreStyle', 'add', 'removeAtt', 'replace'].forEach(function (methodname) { var slot = $[methodname]; Fx.prototype[methodname] = function () { var args = Array.prototype.slice.call(arguments); this.addTask(function (next) { slot.apply($, args); next(); }, methodname, args); return this; }; }); /** * @member Fx.vanish * @param elem, ms */ Fx.prototype.vanish = function (elem, ms) { ms = parseInt(ms); if (isNaN(ms)) ms = 300; return this.css(elem, { transition: "none" }).css(elem, { transition: "opacity " + ms + "ms", opacity: 0 }).wait(ms); }; Fx.prototype.vanishAndDetach = function (elem, ms) { ms = parseInt(ms); if (isNaN(ms)) ms = 300; return this.vanish(elem, ms).detach(elem); }; /** * @member Fx.wait * @param ms */ Fx.prototype.wait = function (msOrElem) { var that = this; var args = Array.prototype.slice.call(arguments); if (typeof msOrElem === 'undefined') msOrElem = 0; if (typeof msOrElem === 'number') { this.addTask(function (next, async) { if (async) { delay(next, msOrElem); } }, 'wait', args); } else { this.addTask(function (next, async) { if (async) { var e = $(msOrElem); var slot = function slot(evt) { ['transitionend', 'oTransitionEnd', 'webkitTransitionEnd'].forEach(function (itm) { e.removeEventListener(itm, slot); }); next(); }; ['transitionend', 'oTransitionEnd', 'webkitTransitionEnd'].forEach(function (itm) { e.addEventListener(itm, slot); }); } }, 'wait', args); } return this; }; module.exports = function (name) { return new Fx(name); }; /** * @module dom.fx * * @description * All the animation effects you can do on DOM elements. * * @example * var mod = require('dom.fx'); */ /** * @export {function} fullscreen * @param opts * * __target__: target DOM element. * * @return {object} * * __value__ {boolean}: If `true` the element goes fullscreen. Otherwise, it returns to its initial position. */ module.exports.Fullscreen = function (opts) { if (typeof opts === 'undefined') { throw Error("[dom.fx:fullscreen] Missing argument!"); } if (typeof opts.target === 'undefined') { throw Error("[dom.fx:fullscreen] Missing `opts.target`!"); } if (typeof opts.target.element === 'function') opts.target = opts.target.element(); if (typeof opts.target.element !== 'undefined') opts.target = opts.target.element; var voidFunc = function voidFunc() {}; var tools = { onBeforeReplace: typeof opts.onBeforeReplace === 'function' ? opts.onBeforeReplace : voidFunc, onAfterReplace: typeof opts.onAfterReplace === 'function' ? opts.onAfterReplace : voidFunc }; DB.propBoolean(this, 'value')(function (isFullScreen) { if (isFullScreen) { fullscreenOn(opts.target, tools); } else { fullscreenOff(opts.target, tools); } }); }; function fullscreenOn(target, tools) { if (tools.terminate) tools.terminate(); var rect = target.getBoundingClientRect(); console.info("[dom.fx] rect=...", rect); var substitute = $.div(); $.css(substitute, { display: 'inline-block', width: rect.width + "px", height: rect.height + "px" }); tools.onBeforeReplace(target); $.replace(substitute, target); tools.onAfterReplace(target); tools.substitute = substitute; tools.styles = saveStyles(target); tools.overlay = $.div('dom-fx-fullscreen'); document.body.appendChild(tools.overlay); tools.overlay.appendChild(target); $.css(target, { left: rect.left + 'px', top: rect.top + 'px', width: rect.width + 'px', height: rect.height + 'px' }); $.addClass(target, 'dom-fx-fullscreen-target'); delay(function () { var r2 = tools.overlay.getBoundingClientRect(); $.css(target, { left: '20px', top: '20px', width: r2.width - 40 + 'px', height: r2.height - 40 + 'px' }); }); } function fullscreenOff(target, tools) { var rect = tools.substitute.getBoundingClientRect(); $.css(target, { left: rect.left + 'px', top: rect.top + 'px', width: rect.width + 'px', height: rect.height + 'px' }); tools.terminate = function () { $.detach(tools.overlay); tools.onBeforeReplace(target); $.replace(target, tools.substitute); tools.onAfterReplace(target); loadStyles(target, tools.styles); delete tools.terminate; }; delay(tools.terminate, 200); } function saveStyles(element) { var styles = {}; var key, val; for (key in element.style) { val = element.style[key]; styles[key] = val; } console.info("[dom.fx] styles=...", styles); return styles; } function loadStyles(element, styles) { for (var key in styles) { element.style[key] = styles[key]; } } module.exports._ = _; });