VulpesOS/gaia/apps/calendar/js/router.js
OverSu b38dea0597 Publish Vulpes Preview 15 sources for desktop, Android and Tundra
Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
2026-09-30 23:13:03 +02:00

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();
});