Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
190 lines
4.6 KiB
JavaScript
Executable file
190 lines
4.6 KiB
JavaScript
Executable file
/* jshint loopfunc: true */
|
|
define(function(require, exports, module) {
|
|
'use strict';
|
|
|
|
var COPY_METHODS = ['start', 'stop', 'show'];
|
|
|
|
var core = require('core');
|
|
var page = require('ext/page');
|
|
|
|
function Router() {
|
|
var i = 0;
|
|
var len = COPY_METHODS.length;
|
|
|
|
this.page = page;
|
|
this.page({ hashbang: true });
|
|
this._activeObjects = [];
|
|
|
|
for (; i < len; i++) {
|
|
this[COPY_METHODS[i]] = page[COPY_METHODS[i]].bind(page);
|
|
}
|
|
|
|
this._lastState = this._lastState.bind(this);
|
|
}
|
|
|
|
Router.prototype = {
|
|
|
|
go: function(path, context) {
|
|
this.show(path, context);
|
|
},
|
|
|
|
/**
|
|
* Tells router to manage the object.
|
|
* This will call the 'onactive'
|
|
* method if present on the object.
|
|
*
|
|
* When the route is changed all 'manged'
|
|
* object will be cleared and 'oninactive'
|
|
* will be fired.
|
|
*/
|
|
mangeObject: function() {
|
|
var args = Array.prototype.slice.call(arguments);
|
|
var object = args.shift();
|
|
|
|
this._activeObjects.push(object);
|
|
// intentionally using 'in'
|
|
if ('onactive' in object) {
|
|
object.onactive.apply(object, args);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Clears active objects, calls oninactive
|
|
* on object if available.
|
|
*/
|
|
clearObjects: function() {
|
|
var item;
|
|
while ((item = this._activeObjects.pop())) {
|
|
// intentionally using 'in'
|
|
if ('oninactive' in item) {
|
|
item.oninactive();
|
|
}
|
|
}
|
|
},
|
|
|
|
/**
|
|
* This method serves two purposes.
|
|
*
|
|
* 1. to safely end the loop by _not_ calling next.
|
|
* 2. to store the last location.
|
|
*
|
|
* This function is added to the end of every rule.
|
|
*/
|
|
_lastState: function(ctx) {
|
|
this.last = ctx;
|
|
},
|
|
|
|
resetState: function() {
|
|
if (!this.currentPath) {
|
|
this.currentPath = '/month/';
|
|
}
|
|
|
|
this.show(this.currentPath);
|
|
},
|
|
|
|
/**
|
|
* Adds a route that represents a state of the page.
|
|
* The theory is that a page can only enter
|
|
* one state at a time (basically yield control to some
|
|
* view or other object).
|
|
*
|
|
* Modifiers can be used to alter the behaviour
|
|
* of a given state (without exiting it)
|
|
*
|
|
* @param {String} path path as defined by page.js.
|
|
* @param {String|Array} one or multiple view identifiers.
|
|
* @param {Object} options (clear, path).
|
|
*/
|
|
state: function(path, views, options) {
|
|
|
|
options = options || {};
|
|
if (!Array.isArray(views)) {
|
|
views = [views];
|
|
}
|
|
|
|
var self = this;
|
|
var viewObjs = [];
|
|
|
|
function loadAllViews(ctx, next) {
|
|
var len = views.length;
|
|
var numViews = len;
|
|
var i;
|
|
|
|
// Reset our views
|
|
viewObjs = [];
|
|
|
|
/*jshint loopfunc: true */
|
|
for (i = 0; i < numViews; i++) {
|
|
core.viewFactory.get(views[i], function(view) {
|
|
viewObjs.push(view);
|
|
len--;
|
|
|
|
if (!len) {
|
|
next();
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
function setPath(ctx, next) {
|
|
// set the theme color based on the view
|
|
var meta = document.querySelector('meta[name="theme-color"]');
|
|
meta.setAttribute('content', meta.dataset[options.color || 'default']);
|
|
|
|
// Only set the dataset path after the view has loaded
|
|
// its resources. Otherwise, there is some flash and
|
|
// jank while styles start to apply and the view is only
|
|
// partially loaded.
|
|
if (options.path !== false) {
|
|
document.body.dataset.path = ctx.canonicalPath;
|
|
// Need to trigger the DOM to accept the new style
|
|
// right away. Otherwise, once manageObject is called,
|
|
// any styles/animations it triggers may be delayed
|
|
// or dropped as the browser coalesces style changes
|
|
// into one visible change. Example is the settings
|
|
// drawer animation getting chopped so it is not smooth.
|
|
document.body.clientWidth;
|
|
}
|
|
next();
|
|
}
|
|
|
|
function handleViews(ctx, next) {
|
|
|
|
// Clear views if needed
|
|
if (options.clear !== false) {
|
|
self.clearObjects();
|
|
}
|
|
|
|
// Activate objects
|
|
for (var i = 0; i < viewObjs.length; i++) {
|
|
self.mangeObject(viewObjs[i], ctx);
|
|
}
|
|
|
|
// Set the current path
|
|
if (options.appPath !== false) {
|
|
self.currentPath = ctx.canonicalPath;
|
|
}
|
|
|
|
next();
|
|
}
|
|
|
|
this.page(path, loadAllViews, setPath, handleViews, this._lastState);
|
|
},
|
|
|
|
/**
|
|
* Adds a modifier route
|
|
* Modifiers are essentially views, without the currentPath updating
|
|
*/
|
|
modifier: function(path, view, options) {
|
|
options = options || {};
|
|
options.appPath = false;
|
|
options.clear = false;
|
|
this.state(path, view, options);
|
|
}
|
|
};
|
|
|
|
// router is singleton to simplify dependency graph, specially since it's
|
|
// needed by notifications and it could get into weird race conditions
|
|
module.exports = new Router();
|
|
|
|
});
|