From 53371d27ab0b37897a0cab3f7bbabae949557257 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Tue, 15 Mar 2016 14:03:25 -0400 Subject: [PATCH] update components --- .../emby-webcomponents/.bower.json | 8 +- .../emby-webcomponents/page.js/page.js | 975 ++++++++++++++ .../emby-webcomponents/router.js | 2 +- .../emby-webcomponents/viewmanager.js | 25 +- .../bower_components/page.js/.bower.json | 40 - .../bower_components/page.js/.jsbeautifyrc | 5 - .../bower_components/page.js/.jshintrc | 23 - .../bower_components/page.js/bower.json | 23 - .../bower_components/page.js/index.js | 619 --------- dashboard-ui/bower_components/page.js/page.js | 1184 ----------------- dashboard-ui/components/viewcontainer-lite.js | 95 +- 11 files changed, 1061 insertions(+), 1938 deletions(-) create mode 100644 dashboard-ui/bower_components/emby-webcomponents/page.js/page.js delete mode 100644 dashboard-ui/bower_components/page.js/.bower.json delete mode 100644 dashboard-ui/bower_components/page.js/.jsbeautifyrc delete mode 100644 dashboard-ui/bower_components/page.js/.jshintrc delete mode 100644 dashboard-ui/bower_components/page.js/bower.json delete mode 100644 dashboard-ui/bower_components/page.js/index.js delete mode 100644 dashboard-ui/bower_components/page.js/page.js diff --git a/dashboard-ui/bower_components/emby-webcomponents/.bower.json b/dashboard-ui/bower_components/emby-webcomponents/.bower.json index dc66978d91..76e9cf0f7a 100644 --- a/dashboard-ui/bower_components/emby-webcomponents/.bower.json +++ b/dashboard-ui/bower_components/emby-webcomponents/.bower.json @@ -16,12 +16,12 @@ }, "devDependencies": {}, "ignore": [], - "version": "1.1.55", - "_release": "1.1.55", + "version": "1.1.58", + "_release": "1.1.58", "_resolution": { "type": "version", - "tag": "1.1.55", - "commit": "7f6cfae7cd55d4270933f5747a7939006d890c33" + "tag": "1.1.58", + "commit": "65c791671a6bc768533a150998a75565b8e17cff" }, "_source": "git://github.com/MediaBrowser/emby-webcomponents.git", "_target": "~1.1.5", diff --git a/dashboard-ui/bower_components/emby-webcomponents/page.js/page.js b/dashboard-ui/bower_components/emby-webcomponents/page.js/page.js new file mode 100644 index 0000000000..200d1cc4c9 --- /dev/null +++ b/dashboard-ui/bower_components/emby-webcomponents/page.js/page.js @@ -0,0 +1,975 @@ +define([], function () { + + /** + * Detect click event + */ + var clickEvent = ('undefined' !== typeof document) && document.ontouchstart ? 'touchstart' : 'click'; + + /** + * To work properly with the URL + * history.location generated polyfill in https://github.com/devote/HTML5-History-API + */ + + var location = ('undefined' !== typeof window) && (window.history.location || window.location); + + /** + * Perform initial dispatch. + */ + + var dispatch = true; + + + /** + * Decode URL components (query string, pathname, hash). + * Accommodates both regular percent encoding and x-www-form-urlencoded format. + */ + var decodeURLComponents = true; + + /** + * Base path. + */ + + var base = ''; + + /** + * Running flag. + */ + + var running; + + /** + * HashBang option + */ + + var hashbang = false; + + var enableHistory = false; + + /** + * Previous context, for capturing + * page exit events. + */ + + var prevContext; + + /** + * Register `path` with callback `fn()`, + * or route `path`, or redirection, + * or `page.start()`. + * + * page(fn); + * page('*', fn); + * page('/user/:id', load, user); + * page('/user/' + user.id, { some: 'thing' }); + * page('/user/' + user.id); + * page('/from', '/to') + * page(); + * + * @param {String|Function} path + * @param {Function} fn... + * @api public + */ + + function page(path, fn) { + // + if ('function' === typeof path) { + return page('*', path); + } + + // route to + if ('function' === typeof fn) { + var route = new Route(path); + for (var i = 1; i < arguments.length; ++i) { + page.callbacks.push(route.middleware(arguments[i])); + } + // show with [state] + } else if ('string' === typeof path) { + page['string' === typeof fn ? 'redirect' : 'show'](path, fn); + // start [options] + } else { + page.start(path); + } + } + + /** + * Callback functions. + */ + + page.callbacks = []; + page.exits = []; + + /** + * Current path being processed + * @type {String} + */ + page.current = ''; + + /** + * Number of pages navigated to. + * @type {number} + * + * page.len == 0; + * page('/login'); + * page.len == 1; + */ + + page.len = 0; + + /** + * Get or set basepath to `path`. + * + * @param {String} path + * @api public + */ + + page.base = function (path) { + if (0 === arguments.length) return base; + base = path; + }; + + /** + * Bind with the given `options`. + * + * Options: + * + * - `click` bind to click events [true] + * - `popstate` bind to popstate [true] + * - `dispatch` perform initial dispatch [true] + * + * @param {Object} options + * @api public + */ + + page.start = function (options) { + options = options || {}; + if (running) return; + running = true; + if (false === options.dispatch) dispatch = false; + if (false === options.decodeURLComponents) decodeURLComponents = false; + if (false !== options.popstate) window.addEventListener('popstate', onpopstate, false); + if (false !== options.click) { + document.addEventListener(clickEvent, onclick, false); + } + if (options.enableHistory != null) enableHistory = options.enableHistory; + if (true === options.hashbang) hashbang = true; + if (!dispatch) return; + var url = (hashbang && ~location.hash.indexOf('#!')) ? location.hash.substr(2) + location.search : location.pathname + location.search + location.hash; + page.replace(url, null, true, dispatch); + }; + + /** + * Unbind click and popstate event handlers. + * + * @api public + */ + + page.stop = function () { + if (!running) return; + page.current = ''; + page.len = 0; + running = false; + document.removeEventListener(clickEvent, onclick, false); + window.removeEventListener('popstate', onpopstate, false); + }; + + /** + * Show `path` with optional `state` object. + * + * @param {String} path + * @param {Object} state + * @param {Boolean} dispatch + * @return {Context} + * @api public + */ + + page.show = function (path, state, dispatch, push, isBack) { + var ctx = new Context(path, state); + ctx.isBack = isBack; + page.current = ctx.path; + if (false !== dispatch) page.dispatch(ctx); + if (false !== ctx.handled && false !== push) ctx.pushState(); + return ctx; + }; + + /** + * Goes back in the history + * Back should always let the current route push state and then go back. + * + * @param {String} path - fallback path to go back if no more history exists, if undefined defaults to page.base + * @param {Object} [state] + * @api public + */ + + page.back = function (path, state) { + + if (enableHistory) { + // Keep it simple and mimic browser back + history.back(); + return; + } + + if (page.len > 0) { + // this may need more testing to see if all browsers + // wait for the next tick to go back in history + if (enableHistory) { + history.back(); + } else { + + if (backStack.length > 2) { + backStack.length--; + var previousState = backStack[backStack.length - 1]; + page.show(previousState.path, previousState.state, true, false, true); + } + } + page.len--; + } else if (path) { + setTimeout(function () { + page.show(path, state); + }); + } else { + setTimeout(function () { + page.show(base, state); + }); + } + }; + + page.enableNativeHistory = function () { + return enableHistory; + }; + + page.canGoBack = function () { + if (enableHistory) { + return history.length > 1; + } + return (page.len || 0) > 0; + }; + + /** + * Register route to redirect from one path to other + * or just redirect to another route + * + * @param {String} from - if param 'to' is undefined redirects to 'from' + * @param {String} [to] + * @api public + */ + page.redirect = function (from, to) { + // Define route from a path to another + if ('string' === typeof from && 'string' === typeof to) { + page(from, function (e) { + setTimeout(function () { + page.replace(to); + }, 0); + }); + } + + // Wait for the push state and replace it with another + if ('string' === typeof from && 'undefined' === typeof to) { + setTimeout(function () { + page.replace(from); + }, 0); + } + }; + + /** + * Replace `path` with optional `state` object. + * + * @param {String} path + * @param {Object} state + * @return {Context} + * @api public + */ + + + page.replace = function (path, state, init, dispatch, isBack) { + var ctx = new Context(path, state); + ctx.isBack = isBack; + page.current = ctx.path; + ctx.init = init; + ctx.save(); // save before dispatching, which may redirect + if (false !== dispatch) page.dispatch(ctx); + return ctx; + }; + + /** + * Dispatch the given `ctx`. + * + * @param {Object} ctx + * @api private + */ + + page.dispatch = function (ctx) { + var prev = prevContext, + i = 0, + j = 0; + + prevContext = ctx; + + function nextExit() { + var fn = page.exits[j++]; + if (!fn) return nextEnter(); + fn(prev, nextExit); + } + + function nextEnter() { + var fn = page.callbacks[i++]; + + if (ctx.path !== page.current) { + ctx.handled = false; + return; + } + if (!fn) return unhandled(ctx); + fn(ctx, nextEnter); + } + + if (prev) { + nextExit(); + } else { + nextEnter(); + } + }; + + /** + * Unhandled `ctx`. When it's not the initial + * popstate then redirect. If you wish to handle + * 404s on your own use `page('*', callback)`. + * + * @param {Context} ctx + * @api private + */ + + function unhandled(ctx) { + if (ctx.handled) return; + var current; + + if (hashbang) { + current = base + location.hash.replace('#!', ''); + } else { + current = location.pathname + location.search; + } + + if (current === ctx.canonicalPath) return; + page.stop(); + ctx.handled = false; + location.href = ctx.canonicalPath; + } + + /** + * Register an exit route on `path` with + * callback `fn()`, which will be called + * on the previous context when a new + * page is visited. + */ + page.exit = function (path, fn) { + if (typeof path === 'function') { + return page.exit('*', path); + } + + var route = new Route(path); + for (var i = 1; i < arguments.length; ++i) { + page.exits.push(route.middleware(arguments[i])); + } + }; + + /** + * Remove URL encoding from the given `str`. + * Accommodates whitespace in both x-www-form-urlencoded + * and regular percent-encoded form. + * + * @param {str} URL component to decode + */ + function decodeURLEncodedURIComponent(val) { + if (typeof val !== 'string') { return val; } + return decodeURLComponents ? decodeURIComponent(val.replace(/\+/g, ' ')) : val; + } + + /** + * Initialize a new "request" `Context` + * with the given `path` and optional initial `state`. + * + * @param {String} path + * @param {Object} state + * @api public + */ + + function Context(path, state) { + if ('/' === path[0] && 0 !== path.indexOf(base)) path = base + (hashbang ? '#!' : '') + path; + var i = path.indexOf('?'); + + this.canonicalPath = path; + this.path = path.replace(base, '') || '/'; + if (hashbang) this.path = this.path.replace('#!', '') || '/'; + + this.title = document.title; + this.state = state || {}; + this.state.path = path; + this.querystring = ~i ? decodeURLEncodedURIComponent(path.slice(i + 1)) : ''; + this.pathname = decodeURLEncodedURIComponent(~i ? path.slice(0, i) : path); + this.params = {}; + + // fragment + this.hash = ''; + if (!hashbang) { + if (!~this.path.indexOf('#')) return; + var parts = this.path.split('#'); + this.path = parts[0]; + this.hash = decodeURLEncodedURIComponent(parts[1]) || ''; + this.querystring = this.querystring.split('#')[0]; + } + } + + /** + * Expose `Context`. + */ + + page.Context = Context; + var backStack = []; + + /** + * Push state. + * + * @api private + */ + + Context.prototype.pushState = function () { + page.len++; + + if (enableHistory) { + history.pushState(this.state, this.title, hashbang && this.path !== '/' ? '#!' + this.path : this.canonicalPath); + } else { + backStack.push({ + state: this.state, + title: this.title, + url: (hashbang && this.path !== '/' ? '#!' + this.path : this.canonicalPath), + path: this.path + }); + } + }; + + /** + * Save the context state. + * + * @api public + */ + + Context.prototype.save = function () { + + if (enableHistory) { + history.replaceState(this.state, this.title, hashbang && this.path !== '/' ? '#!' + this.path : this.canonicalPath); + } else { + backStack[page.len || 0] = { + state: this.state, + title: this.title, + url: (hashbang && this.path !== '/' ? '#!' + this.path : this.canonicalPath), + path: this.path + }; + } + }; + + /** + * Initialize `Route` with the given HTTP `path`, + * and an array of `callbacks` and `options`. + * + * Options: + * + * - `sensitive` enable case-sensitive routes + * - `strict` enable strict matching for trailing slashes + * + * @param {String} path + * @param {Object} options. + * @api private + */ + + function Route(path, options) { + options = options || {}; + this.path = (path === '*') ? '(.*)' : path; + this.method = 'GET'; + this.regexp = pathToRegexp(this.path, + this.keys = [], + options.sensitive, + options.strict); + } + + /** + * Expose `Route`. + */ + + page.Route = Route; + + /** + * Return route middleware with + * the given callback `fn()`. + * + * @param {Function} fn + * @return {Function} + * @api public + */ + + Route.prototype.middleware = function (fn) { + var self = this; + return function (ctx, next) { + if (self.match(ctx.path, ctx.params)) return fn(ctx, next); + next(); + }; + }; + + /** + * Check if this route matches `path`, if so + * populate `params`. + * + * @param {String} path + * @param {Object} params + * @return {Boolean} + * @api private + */ + + Route.prototype.match = function (path, params) { + var keys = this.keys, + qsIndex = path.indexOf('?'), + pathname = ~qsIndex ? path.slice(0, qsIndex) : path, + m = this.regexp.exec(decodeURIComponent(pathname)); + + if (!m) return false; + + for (var i = 1, len = m.length; i < len; ++i) { + var key = keys[i - 1]; + var val = decodeURLEncodedURIComponent(m[i]); + if (val !== undefined || !(hasOwnProperty.call(params, key.name))) { + params[key.name] = val; + } + } + + return true; + }; + + + var previousPopState = {}; + + function ignorePopState(event) { + + var state = event.state || {}; + + if (previousPopState.navigate === false) { + // Ignore + previousPopState = state; + return true; + } + + previousPopState = state; + return false; + } + + page.pushState = function (state, title, url) { + history.pushState(state, title, url); + previousPopState = state; + }; + + /** + * Handle "populate" events. + */ + + var onpopstate = (function () { + var loaded = false; + if ('undefined' === typeof window) { + return; + } + if (document.readyState === 'complete') { + loaded = true; + } else { + window.addEventListener('load', function () { + setTimeout(function () { + loaded = true; + }, 0); + }); + } + return function onpopstate(e) { + if (!loaded) return; + if (ignorePopState(e)) return; + if (e.state) { + var path = e.state.path; + page.replace(path, e.state, null, null, true); + } else { + page.show(location.pathname + location.hash, undefined, undefined, false, true); + } + }; + })(); + /** + * Handle "click" events. + */ + + function onclick(e) { + + if (1 !== which(e)) return; + + if (e.metaKey || e.ctrlKey || e.shiftKey) return; + if (e.defaultPrevented) return; + + + + // ensure link + var el = e.target; + while (el && 'A' !== el.nodeName) el = el.parentNode; + if (!el || 'A' !== el.nodeName) return; + + + + // Ignore if tag has + // 1. "download" attribute + // 2. rel="external" attribute + if (el.hasAttribute('download') || el.getAttribute('rel') === 'external') return; + + // ensure non-hash for the same path + var link = el.getAttribute('href'); + if (!hashbang && el.pathname === location.pathname && (el.hash || '#' === link)) return; + + + + // Check for mailto: in the href + if (link && link.indexOf('mailto:') > -1) return; + + // check target + if (el.target) return; + + // x-origin + if (!sameOrigin(el.href)) return; + + + + // rebuild path + var path = el.pathname + el.search + (el.hash || ''); + + // same page + var orig = path; + + if (path.indexOf(base) === 0) { + path = path.substr(base.length); + } + + if (hashbang) path = path.replace('#!', ''); + + if (base && orig === path) return; + + e.preventDefault(); + page.show(orig); + } + + /** + * Event button. + */ + + function which(e) { + e = e || window.event; + return null === e.which ? e.button : e.which; + } + + /** + * Check if `href` is the same origin. + */ + + function sameOrigin(href) { + var origin = location.protocol + '//' + location.hostname; + if (location.port) origin += ':' + location.port; + return (href && (0 === href.indexOf(origin))); + } + + page.sameOrigin = sameOrigin; + + /** + * The main path matching regexp utility. + * + * @type {RegExp} + */ + var PATH_REGEXP = new RegExp([ + // Match escaped characters that would otherwise appear in future matches. + // This allows the user to escape special characters that won't transform. + '(\\\\.)', + // Match Express-style parameters and un-named parameters with a prefix + // and optional suffixes. Matches appear as: + // + // "/:test(\\d+)?" => ["/", "test", "\d+", undefined, "?", undefined] + // "/route(\\d+)" => [undefined, undefined, undefined, "\d+", undefined, undefined] + // "/*" => ["/", undefined, undefined, undefined, undefined, "*"] + '([\\/.])?(?:(?:\\:(\\w+)(?:\\(((?:\\\\.|[^()])+)\\))?|\\(((?:\\\\.|[^()])+)\\))([+*?])?|(\\*))' + ].join('|'), 'g') + + /** + * Parse a string for the raw tokens. + * + * @param {String} str + * @return {Array} + */ + function parse(str) { + var tokens = [] + var key = 0 + var index = 0 + var path = '' + var res + + while ((res = PATH_REGEXP.exec(str)) != null) { + var m = res[0] + var escaped = res[1] + var offset = res.index + path += str.slice(index, offset) + index = offset + m.length + + // Ignore already escaped sequences. + if (escaped) { + path += escaped[1] + continue + } + + // Push the current path onto the tokens. + if (path) { + tokens.push(path) + path = '' + } + + var prefix = res[2] + var name = res[3] + var capture = res[4] + var group = res[5] + var suffix = res[6] + var asterisk = res[7] + + var repeat = suffix === '+' || suffix === '*' + var optional = suffix === '?' || suffix === '*' + var delimiter = prefix || '/' + var pattern = capture || group || (asterisk ? '.*' : '[^' + delimiter + ']+?') + + tokens.push({ + name: name || key++, + prefix: prefix || '', + delimiter: delimiter, + optional: optional, + repeat: repeat, + pattern: escapeGroup(pattern) + }) + } + + // Match any characters still remaining. + if (index < str.length) { + path += str.substr(index) + } + + // If the path exists, push it onto the end. + if (path) { + tokens.push(path) + } + + return tokens + } + + var isarray = Array.isArray || function (arr) { + return Object.prototype.toString.call(arr) == '[object Array]'; + }; + + /** + * Escape a regular expression string. + * + * @param {String} str + * @return {String} + */ + function escapeString(str) { + return str.replace(/([.+*?=^!:${}()[\]|\/])/g, '\\$1') + } + + /** + * Escape the capturing group by escaping special characters and meaning. + * + * @param {String} group + * @return {String} + */ + function escapeGroup(group) { + return group.replace(/([=!:$\/()])/g, '\\$1') + } + + /** + * Attach the keys as a property of the regexp. + * + * @param {RegExp} re + * @param {Array} keys + * @return {RegExp} + */ + function attachKeys(re, keys) { + re.keys = keys + return re + } + + /** + * Get the flags for a regexp from the options. + * + * @param {Object} options + * @return {String} + */ + function flags(options) { + return options.sensitive ? '' : 'i' + } + + /** + * Pull out keys from a regexp. + * + * @param {RegExp} path + * @param {Array} keys + * @return {RegExp} + */ + function regexpToRegexp(path, keys) { + // Use a negative lookahead to match only capturing groups. + var groups = path.source.match(/\((?!\?)/g) + + if (groups) { + for (var i = 0; i < groups.length; i++) { + keys.push({ + name: i, + prefix: null, + delimiter: null, + optional: false, + repeat: false, + pattern: null + }) + } + } + + return attachKeys(path, keys) + } + + /** + * Transform an array into a regexp. + * + * @param {Array} path + * @param {Array} keys + * @param {Object} options + * @return {RegExp} + */ + function arrayToRegexp(path, keys, options) { + var parts = [] + + for (var i = 0; i < path.length; i++) { + parts.push(pathToRegexp(path[i], keys, options).source) + } + + var regexp = new RegExp('(?:' + parts.join('|') + ')', flags(options)) + + return attachKeys(regexp, keys) + } + + /** + * Create a path regexp from string input. + * + * @param {String} path + * @param {Array} keys + * @param {Object} options + * @return {RegExp} + */ + function stringToRegexp(path, keys, options) { + var tokens = parse(path) + var re = tokensToRegExp(tokens, options) + + // Attach keys back to the regexp. + for (var i = 0; i < tokens.length; i++) { + if (typeof tokens[i] !== 'string') { + keys.push(tokens[i]) + } + } + + return attachKeys(re, keys) + } + + /** + * Expose a function for taking tokens and returning a RegExp. + * + * @param {Array} tokens + * @param {Array} keys + * @param {Object} options + * @return {RegExp} + */ + function tokensToRegExp(tokens, options) { + options = options || {} + + var strict = options.strict + var end = options.end !== false + var route = '' + var lastToken = tokens[tokens.length - 1] + var endsWithSlash = typeof lastToken === 'string' && /\/$/.test(lastToken) + + // Iterate over the tokens and create our regexp string. + for (var i = 0; i < tokens.length; i++) { + var token = tokens[i] + + if (typeof token === 'string') { + route += escapeString(token) + } else { + var prefix = escapeString(token.prefix) + var capture = token.pattern + + if (token.repeat) { + capture += '(?:' + prefix + capture + ')*' + } + + if (token.optional) { + if (prefix) { + capture = '(?:' + prefix + '(' + capture + '))?' + } else { + capture = '(' + capture + ')?' + } + } else { + capture = prefix + '(' + capture + ')' + } + + route += capture + } + } + + // In non-strict mode we allow a slash at the end of match. If the path to + // match already ends with a slash, we remove it for consistency. The slash + // is valid at the end of a path match, not in the middle. This is important + // in non-ending mode, where "/test/" shouldn't match "/test//route". + if (!strict) { + route = (endsWithSlash ? route.slice(0, -2) : route) + '(?:\\/(?=$))?' + } + + if (end) { + route += '$' + } else { + // In non-ending mode, we need the capturing groups to match as much as + // possible by using a positive lookahead to the end or next path segment. + route += strict && endsWithSlash ? '' : '(?=\\/|$)' + } + + return new RegExp('^' + route, flags(options)) + } + + /** + * Normalize the given path string, returning a regular expression. + * + * An empty array can be passed in for the keys, which will hold the + * placeholder key descriptions. For example, using `/user/:id`, `keys` will + * contain `[{ name: 'id', delimiter: '/', optional: false, repeat: false }]`. + * + * @param {(String|RegExp|Array)} path + * @param {Array} [keys] + * @param {Object} [options] + * @return {RegExp} + */ + function pathToRegexp(path, keys, options) { + keys = keys || [] + + if (!isarray(keys)) { + options = keys + keys = [] + } else if (!options) { + options = {} + } + + if (path instanceof RegExp) { + return regexpToRegexp(path, keys, options) + } + + if (isarray(path)) { + return arrayToRegexp(path, keys, options) + } + + return stringToRegexp(path, keys, options) + } + + return page; + +}); \ No newline at end of file diff --git a/dashboard-ui/bower_components/emby-webcomponents/router.js b/dashboard-ui/bower_components/emby-webcomponents/router.js index 60496bf247..dd3431b9e5 100644 --- a/dashboard-ui/bower_components/emby-webcomponents/router.js +++ b/dashboard-ui/bower_components/emby-webcomponents/router.js @@ -185,7 +185,7 @@ define(['loading', 'viewManager', 'skinManager', 'pluginManager', 'backdrop', 'b loading.hide(); page({ - click: false, + click: true, hashbang: true, enableHistory: enableHistory() }); diff --git a/dashboard-ui/bower_components/emby-webcomponents/viewmanager.js b/dashboard-ui/bower_components/emby-webcomponents/viewmanager.js index 86030e3781..2d4c0be17f 100644 --- a/dashboard-ui/bower_components/emby-webcomponents/viewmanager.js +++ b/dashboard-ui/bower_components/emby-webcomponents/viewmanager.js @@ -1,6 +1,7 @@ define(['viewcontainer', 'focusManager', 'queryString', 'connectionManager', 'events'], function (viewcontainer, focusManager, queryString, connectionManager, events) { var currentView; + var dispatchPageEvents; viewcontainer.setOnBeforeChange(function (newView, isRestored, options) { @@ -18,7 +19,8 @@ define(['viewcontainer', 'focusManager', 'queryString', 'connectionManager', 'ev // Use controller method var controller = new options.controllerFactory(newView, eventDetail.detail.params); - + } else if (dispatchPageEvents) { + dispatchViewEvent(newView, 'viewinit'); } } @@ -45,7 +47,11 @@ define(['viewcontainer', 'focusManager', 'queryString', 'connectionManager', 'ev view.activeElement.focus(); } - view.dispatchEvent(new CustomEvent("viewshow", eventDetail)); + view.dispatchEvent(new CustomEvent('viewshow', eventDetail)); + + if (dispatchPageEvents) { + view.dispatchEvent(new CustomEvent('pageshow', eventDetail)); + } } function dispatchViewEvent(view, eventName, isRestored) { @@ -58,6 +64,17 @@ define(['viewcontainer', 'focusManager', 'queryString', 'connectionManager', 'ev bubbles: true, cancelable: false })); + + if (dispatchPageEvents) { + view.dispatchEvent(new CustomEvent(eventName.replace('view', 'page'), { + detail: { + type: view.getAttribute('data-type'), + isRestored: isRestored + }, + bubbles: true, + cancelable: false + })); + } } function getViewEventDetail(view, options, isRestore) { @@ -144,6 +161,10 @@ define(['viewcontainer', 'focusManager', 'queryString', 'connectionManager', 'ev tryRestoreInternal(viewcontainer, options, resolve, reject); }); }; + + self.dispatchPageEvents = function (value) { + dispatchPageEvents = value; + }; } return new ViewManager(); diff --git a/dashboard-ui/bower_components/page.js/.bower.json b/dashboard-ui/bower_components/page.js/.bower.json deleted file mode 100644 index 1f3872eaba..0000000000 --- a/dashboard-ui/bower_components/page.js/.bower.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "name": "page", - "description": "Tiny client-side router", - "keywords": [ - "page", - "route", - "router", - "routes", - "pushState" - ], - "main": "page.js", - "repository": { - "type": "git", - "url": "git://github.com/visionmedia/page.js" - }, - "ignore": [ - ".gitignore", - ".npmignore", - ".travis.yml", - "component.json", - "examples", - "History.md", - "Makefile", - "package.json", - "Readme.md", - "test" - ], - "license": "MIT", - "homepage": "https://github.com/visionmedia/page.js", - "version": "1.6.4", - "_release": "1.6.4", - "_resolution": { - "type": "version", - "tag": "1.6.4", - "commit": "d11509f1f0fed0309391d995919c25dce84b8abd" - }, - "_source": "git://github.com/visionmedia/page.js.git", - "_target": "~1.6.3", - "_originalSource": "page.js" -} \ No newline at end of file diff --git a/dashboard-ui/bower_components/page.js/.jsbeautifyrc b/dashboard-ui/bower_components/page.js/.jsbeautifyrc deleted file mode 100644 index 5067554a76..0000000000 --- a/dashboard-ui/bower_components/page.js/.jsbeautifyrc +++ /dev/null @@ -1,5 +0,0 @@ -{ - "indent_size": 2, - "indent_char": " ", - "indent_with_tabs": false -} \ No newline at end of file diff --git a/dashboard-ui/bower_components/page.js/.jshintrc b/dashboard-ui/bower_components/page.js/.jshintrc deleted file mode 100644 index cc5fa19a97..0000000000 --- a/dashboard-ui/bower_components/page.js/.jshintrc +++ /dev/null @@ -1,23 +0,0 @@ -{ - "browser": true, - "node":true, - "expr": true, - "laxcomma": true, - "-W079": true, - "-W014": true, - "curly": false, - "eqeqeq": true, - "immed": true, - "latedef": true, - "newcap": true, - "noarg": true, - "quotmark": "single", - "regexp": true, - "undef": true, - "unused": false, - "strict": true, - "trailing": false, - "smarttabs": true, - "latedef": false, - "indent": 2 -} diff --git a/dashboard-ui/bower_components/page.js/bower.json b/dashboard-ui/bower_components/page.js/bower.json deleted file mode 100644 index faf30fd876..0000000000 --- a/dashboard-ui/bower_components/page.js/bower.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "name": "page", - "description": "Tiny client-side router", - "keywords": ["page", "route", "router", "routes", "pushState"], - "main": "page.js", - "repository": { - "type": "git", - "url": "git://github.com/visionmedia/page.js" - }, - "ignore": [ - ".gitignore", - ".npmignore", - ".travis.yml", - "component.json", - "examples", - "History.md", - "Makefile", - "package.json", - "Readme.md", - "test" - ], - "license": "MIT" -} diff --git a/dashboard-ui/bower_components/page.js/index.js b/dashboard-ui/bower_components/page.js/index.js deleted file mode 100644 index d37374f236..0000000000 --- a/dashboard-ui/bower_components/page.js/index.js +++ /dev/null @@ -1,619 +0,0 @@ - /* globals require, module */ - - 'use strict'; - - /** - * Module dependencies. - */ - - var pathtoRegexp = require('path-to-regexp'); - - /** - * Module exports. - */ - - module.exports = page; - - /** - * Detect click event - */ - var clickEvent = ('undefined' !== typeof document) && document.ontouchstart ? 'touchstart' : 'click'; - - /** - * To work properly with the URL - * history.location generated polyfill in https://github.com/devote/HTML5-History-API - */ - - var location = ('undefined' !== typeof window) && (window.history.location || window.location); - - /** - * Perform initial dispatch. - */ - - var dispatch = true; - - - /** - * Decode URL components (query string, pathname, hash). - * Accommodates both regular percent encoding and x-www-form-urlencoded format. - */ - var decodeURLComponents = true; - - /** - * Base path. - */ - - var base = ''; - - /** - * Running flag. - */ - - var running; - - /** - * HashBang option - */ - - var hashbang = false; - - /** - * Previous context, for capturing - * page exit events. - */ - - var prevContext; - - /** - * Register `path` with callback `fn()`, - * or route `path`, or redirection, - * or `page.start()`. - * - * page(fn); - * page('*', fn); - * page('/user/:id', load, user); - * page('/user/' + user.id, { some: 'thing' }); - * page('/user/' + user.id); - * page('/from', '/to') - * page(); - * - * @param {String|Function} path - * @param {Function} fn... - * @api public - */ - - function page(path, fn) { - // - if ('function' === typeof path) { - return page('*', path); - } - - // route to - if ('function' === typeof fn) { - var route = new Route(path); - for (var i = 1; i < arguments.length; ++i) { - page.callbacks.push(route.middleware(arguments[i])); - } - // show with [state] - } else if ('string' === typeof path) { - page['string' === typeof fn ? 'redirect' : 'show'](path, fn); - // start [options] - } else { - page.start(path); - } - } - - /** - * Callback functions. - */ - - page.callbacks = []; - page.exits = []; - - /** - * Current path being processed - * @type {String} - */ - page.current = ''; - - /** - * Number of pages navigated to. - * @type {number} - * - * page.len == 0; - * page('/login'); - * page.len == 1; - */ - - page.len = 0; - - /** - * Get or set basepath to `path`. - * - * @param {String} path - * @api public - */ - - page.base = function(path) { - if (0 === arguments.length) return base; - base = path; - }; - - /** - * Bind with the given `options`. - * - * Options: - * - * - `click` bind to click events [true] - * - `popstate` bind to popstate [true] - * - `dispatch` perform initial dispatch [true] - * - * @param {Object} options - * @api public - */ - - page.start = function(options) { - options = options || {}; - if (running) return; - running = true; - if (false === options.dispatch) dispatch = false; - if (false === options.decodeURLComponents) decodeURLComponents = false; - if (false !== options.popstate) window.addEventListener('popstate', onpopstate, false); - if (false !== options.click) { - document.addEventListener(clickEvent, onclick, false); - } - if (true === options.hashbang) hashbang = true; - if (!dispatch) return; - var url = (hashbang && ~location.hash.indexOf('#!')) ? location.hash.substr(2) + location.search : location.pathname + location.search + location.hash; - page.replace(url, null, true, dispatch); - }; - - /** - * Unbind click and popstate event handlers. - * - * @api public - */ - - page.stop = function() { - if (!running) return; - page.current = ''; - page.len = 0; - running = false; - document.removeEventListener(clickEvent, onclick, false); - window.removeEventListener('popstate', onpopstate, false); - }; - - /** - * Show `path` with optional `state` object. - * - * @param {String} path - * @param {Object} state - * @param {Boolean} dispatch - * @return {Context} - * @api public - */ - - page.show = function(path, state, dispatch, push) { - var ctx = new Context(path, state); - page.current = ctx.path; - if (false !== dispatch) page.dispatch(ctx); - if (false !== ctx.handled && false !== push) ctx.pushState(); - return ctx; - }; - - /** - * Goes back in the history - * Back should always let the current route push state and then go back. - * - * @param {String} path - fallback path to go back if no more history exists, if undefined defaults to page.base - * @param {Object} [state] - * @api public - */ - - page.back = function(path, state) { - if (page.len > 0) { - // this may need more testing to see if all browsers - // wait for the next tick to go back in history - history.back(); - page.len--; - } else if (path) { - setTimeout(function() { - page.show(path, state); - }); - }else{ - setTimeout(function() { - page.show(base, state); - }); - } - }; - - - /** - * Register route to redirect from one path to other - * or just redirect to another route - * - * @param {String} from - if param 'to' is undefined redirects to 'from' - * @param {String} [to] - * @api public - */ - page.redirect = function(from, to) { - // Define route from a path to another - if ('string' === typeof from && 'string' === typeof to) { - page(from, function(e) { - setTimeout(function() { - page.replace(to); - }, 0); - }); - } - - // Wait for the push state and replace it with another - if ('string' === typeof from && 'undefined' === typeof to) { - setTimeout(function() { - page.replace(from); - }, 0); - } - }; - - /** - * Replace `path` with optional `state` object. - * - * @param {String} path - * @param {Object} state - * @return {Context} - * @api public - */ - - - page.replace = function(path, state, init, dispatch) { - var ctx = new Context(path, state); - page.current = ctx.path; - ctx.init = init; - ctx.save(); // save before dispatching, which may redirect - if (false !== dispatch) page.dispatch(ctx); - return ctx; - }; - - /** - * Dispatch the given `ctx`. - * - * @param {Object} ctx - * @api private - */ - - page.dispatch = function(ctx) { - var prev = prevContext, - i = 0, - j = 0; - - prevContext = ctx; - - function nextExit() { - var fn = page.exits[j++]; - if (!fn) return nextEnter(); - fn(prev, nextExit); - } - - function nextEnter() { - var fn = page.callbacks[i++]; - - if (ctx.path !== page.current) { - ctx.handled = false; - return; - } - if (!fn) return unhandled(ctx); - fn(ctx, nextEnter); - } - - if (prev) { - nextExit(); - } else { - nextEnter(); - } - }; - - /** - * Unhandled `ctx`. When it's not the initial - * popstate then redirect. If you wish to handle - * 404s on your own use `page('*', callback)`. - * - * @param {Context} ctx - * @api private - */ - - function unhandled(ctx) { - if (ctx.handled) return; - var current; - - if (hashbang) { - current = base + location.hash.replace('#!', ''); - } else { - current = location.pathname + location.search; - } - - if (current === ctx.canonicalPath) return; - page.stop(); - ctx.handled = false; - location.href = ctx.canonicalPath; - } - - /** - * Register an exit route on `path` with - * callback `fn()`, which will be called - * on the previous context when a new - * page is visited. - */ - page.exit = function(path, fn) { - if (typeof path === 'function') { - return page.exit('*', path); - } - - var route = new Route(path); - for (var i = 1; i < arguments.length; ++i) { - page.exits.push(route.middleware(arguments[i])); - } - }; - - /** - * Remove URL encoding from the given `str`. - * Accommodates whitespace in both x-www-form-urlencoded - * and regular percent-encoded form. - * - * @param {str} URL component to decode - */ - function decodeURLEncodedURIComponent(val) { - if (typeof val !== 'string') { return val; } - return decodeURLComponents ? decodeURIComponent(val.replace(/\+/g, ' ')) : val; - } - - /** - * Initialize a new "request" `Context` - * with the given `path` and optional initial `state`. - * - * @param {String} path - * @param {Object} state - * @api public - */ - - function Context(path, state) { - if ('/' === path[0] && 0 !== path.indexOf(base)) path = base + (hashbang ? '#!' : '') + path; - var i = path.indexOf('?'); - - this.canonicalPath = path; - this.path = path.replace(base, '') || '/'; - if (hashbang) this.path = this.path.replace('#!', '') || '/'; - - this.title = document.title; - this.state = state || {}; - this.state.path = path; - this.querystring = ~i ? decodeURLEncodedURIComponent(path.slice(i + 1)) : ''; - this.pathname = decodeURLEncodedURIComponent(~i ? path.slice(0, i) : path); - this.params = {}; - - // fragment - this.hash = ''; - if (!hashbang) { - if (!~this.path.indexOf('#')) return; - var parts = this.path.split('#'); - this.path = parts[0]; - this.hash = decodeURLEncodedURIComponent(parts[1]) || ''; - this.querystring = this.querystring.split('#')[0]; - } - } - - /** - * Expose `Context`. - */ - - page.Context = Context; - - /** - * Push state. - * - * @api private - */ - - Context.prototype.pushState = function() { - page.len++; - history.pushState(this.state, this.title, hashbang && this.path !== '/' ? '#!' + this.path : this.canonicalPath); - }; - - /** - * Save the context state. - * - * @api public - */ - - Context.prototype.save = function() { - history.replaceState(this.state, this.title, hashbang && this.path !== '/' ? '#!' + this.path : this.canonicalPath); - }; - - /** - * Initialize `Route` with the given HTTP `path`, - * and an array of `callbacks` and `options`. - * - * Options: - * - * - `sensitive` enable case-sensitive routes - * - `strict` enable strict matching for trailing slashes - * - * @param {String} path - * @param {Object} options. - * @api private - */ - - function Route(path, options) { - options = options || {}; - this.path = (path === '*') ? '(.*)' : path; - this.method = 'GET'; - this.regexp = pathtoRegexp(this.path, - this.keys = [], - options.sensitive, - options.strict); - } - - /** - * Expose `Route`. - */ - - page.Route = Route; - - /** - * Return route middleware with - * the given callback `fn()`. - * - * @param {Function} fn - * @return {Function} - * @api public - */ - - Route.prototype.middleware = function(fn) { - var self = this; - return function(ctx, next) { - if (self.match(ctx.path, ctx.params)) return fn(ctx, next); - next(); - }; - }; - - /** - * Check if this route matches `path`, if so - * populate `params`. - * - * @param {String} path - * @param {Object} params - * @return {Boolean} - * @api private - */ - - Route.prototype.match = function(path, params) { - var keys = this.keys, - qsIndex = path.indexOf('?'), - pathname = ~qsIndex ? path.slice(0, qsIndex) : path, - m = this.regexp.exec(decodeURIComponent(pathname)); - - if (!m) return false; - - for (var i = 1, len = m.length; i < len; ++i) { - var key = keys[i - 1]; - var val = decodeURLEncodedURIComponent(m[i]); - if (val !== undefined || !(hasOwnProperty.call(params, key.name))) { - params[key.name] = val; - } - } - - return true; - }; - - - /** - * Handle "populate" events. - */ - - var onpopstate = (function () { - var loaded = false; - if ('undefined' === typeof window) { - return; - } - if (document.readyState === 'complete') { - loaded = true; - } else { - window.addEventListener('load', function() { - setTimeout(function() { - loaded = true; - }, 0); - }); - } - return function onpopstate(e) { - if (!loaded) return; - if (e.state) { - var path = e.state.path; - page.replace(path, e.state); - } else { - page.show(location.pathname + location.hash, undefined, undefined, false); - } - }; - })(); - /** - * Handle "click" events. - */ - - function onclick(e) { - - if (1 !== which(e)) return; - - if (e.metaKey || e.ctrlKey || e.shiftKey) return; - if (e.defaultPrevented) return; - - - - // ensure link - var el = e.target; - while (el && 'A' !== el.nodeName) el = el.parentNode; - if (!el || 'A' !== el.nodeName) return; - - - - // Ignore if tag has - // 1. "download" attribute - // 2. rel="external" attribute - if (el.hasAttribute('download') || el.getAttribute('rel') === 'external') return; - - // ensure non-hash for the same path - var link = el.getAttribute('href'); - if (!hashbang && el.pathname === location.pathname && (el.hash || '#' === link)) return; - - - - // Check for mailto: in the href - if (link && link.indexOf('mailto:') > -1) return; - - // check target - if (el.target) return; - - // x-origin - if (!sameOrigin(el.href)) return; - - - - // rebuild path - var path = el.pathname + el.search + (el.hash || ''); - - // strip leading "/[drive letter]:" on NW.js on Windows - if (typeof process !== 'undefined' && path.match(/^\/[a-zA-Z]:\//)) { - path = path.replace(/^\/[a-zA-Z]:\//, '/'); - } - - // same page - var orig = path; - - if (path.indexOf(base) === 0) { - path = path.substr(base.length); - } - - if (hashbang) path = path.replace('#!', ''); - - if (base && orig === path) return; - - e.preventDefault(); - page.show(orig); - } - - /** - * Event button. - */ - - function which(e) { - e = e || window.event; - return null === e.which ? e.button : e.which; - } - - /** - * Check if `href` is the same origin. - */ - - function sameOrigin(href) { - var origin = location.protocol + '//' + location.hostname; - if (location.port) origin += ':' + location.port; - return (href && (0 === href.indexOf(origin))); - } - - page.sameOrigin = sameOrigin; diff --git a/dashboard-ui/bower_components/page.js/page.js b/dashboard-ui/bower_components/page.js/page.js deleted file mode 100644 index 51f2190078..0000000000 --- a/dashboard-ui/bower_components/page.js/page.js +++ /dev/null @@ -1,1184 +0,0 @@ -!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.page=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o - if ('function' === typeof path) { - return page('*', path); - } - - // route to - if ('function' === typeof fn) { - var route = new Route(path); - for (var i = 1; i < arguments.length; ++i) { - page.callbacks.push(route.middleware(arguments[i])); - } - // show with [state] - } else if ('string' === typeof path) { - page['string' === typeof fn ? 'redirect' : 'show'](path, fn); - // start [options] - } else { - page.start(path); - } - } - - /** - * Callback functions. - */ - - page.callbacks = []; - page.exits = []; - - /** - * Current path being processed - * @type {String} - */ - page.current = ''; - - /** - * Number of pages navigated to. - * @type {number} - * - * page.len == 0; - * page('/login'); - * page.len == 1; - */ - - page.len = 0; - - /** - * Get or set basepath to `path`. - * - * @param {String} path - * @api public - */ - - page.base = function(path) { - if (0 === arguments.length) return base; - base = path; - }; - - /** - * Bind with the given `options`. - * - * Options: - * - * - `click` bind to click events [true] - * - `popstate` bind to popstate [true] - * - `dispatch` perform initial dispatch [true] - * - * @param {Object} options - * @api public - */ - - page.start = function(options) { - options = options || {}; - if (running) return; - running = true; - if (false === options.dispatch) dispatch = false; - if (false === options.decodeURLComponents) decodeURLComponents = false; - if (false !== options.popstate) window.addEventListener('popstate', onpopstate, false); - if (false !== options.click) { - document.addEventListener(clickEvent, onclick, false); - } - if (options.enableHistory != null) enableHistory = options.enableHistory; - if (true === options.hashbang) hashbang = true; - if (!dispatch) return; - var url = (hashbang && ~location.hash.indexOf('#!')) ? location.hash.substr(2) + location.search : location.pathname + location.search + location.hash; - page.replace(url, null, true, dispatch); - }; - - /** - * Unbind click and popstate event handlers. - * - * @api public - */ - - page.stop = function() { - if (!running) return; - page.current = ''; - page.len = 0; - running = false; - document.removeEventListener(clickEvent, onclick, false); - window.removeEventListener('popstate', onpopstate, false); - }; - - /** - * Show `path` with optional `state` object. - * - * @param {String} path - * @param {Object} state - * @param {Boolean} dispatch - * @return {Context} - * @api public - */ - - page.show = function(path, state, dispatch, push, isBack) { - var ctx = new Context(path, state); - ctx.isBack = isBack; - page.current = ctx.path; - if (false !== dispatch) page.dispatch(ctx); - if (false !== ctx.handled && false !== push) ctx.pushState(); - return ctx; - }; - - /** - * Goes back in the history - * Back should always let the current route push state and then go back. - * - * @param {String} path - fallback path to go back if no more history exists, if undefined defaults to page.base - * @param {Object} [state] - * @api public - */ - - page.back = function (path, state) { - - if (enableHistory) { - // Keep it simple and mimic browser back - history.back(); - return; - } - - if (page.len > 0) { - // this may need more testing to see if all browsers - // wait for the next tick to go back in history - if (enableHistory) { - history.back(); - } else { - - if (backStack.length > 2) { - backStack.length--; - var previousState = backStack[backStack.length - 1]; - page.show(previousState.path, previousState.state, true, false, true); - } - } - page.len--; - } else if (path) { - setTimeout(function() { - page.show(path, state); - }); - }else{ - setTimeout(function() { - page.show(base, state); - }); - } - }; - - page.enableNativeHistory = function () { - return enableHistory; - }; - - page.canGoBack = function () { - if (enableHistory) { - return history.length > 1; - } - return (page.len || 0) > 0; - }; - - /** - * Register route to redirect from one path to other - * or just redirect to another route - * - * @param {String} from - if param 'to' is undefined redirects to 'from' - * @param {String} [to] - * @api public - */ - page.redirect = function(from, to) { - // Define route from a path to another - if ('string' === typeof from && 'string' === typeof to) { - page(from, function(e) { - setTimeout(function() { - page.replace(to); - }, 0); - }); - } - - // Wait for the push state and replace it with another - if ('string' === typeof from && 'undefined' === typeof to) { - setTimeout(function() { - page.replace(from); - }, 0); - } - }; - - /** - * Replace `path` with optional `state` object. - * - * @param {String} path - * @param {Object} state - * @return {Context} - * @api public - */ - - - page.replace = function(path, state, init, dispatch, isBack) { - var ctx = new Context(path, state); - ctx.isBack = isBack; - page.current = ctx.path; - ctx.init = init; - ctx.save(); // save before dispatching, which may redirect - if (false !== dispatch) page.dispatch(ctx); - return ctx; - }; - - /** - * Dispatch the given `ctx`. - * - * @param {Object} ctx - * @api private - */ - - page.dispatch = function(ctx) { - var prev = prevContext, - i = 0, - j = 0; - - prevContext = ctx; - - function nextExit() { - var fn = page.exits[j++]; - if (!fn) return nextEnter(); - fn(prev, nextExit); - } - - function nextEnter() { - var fn = page.callbacks[i++]; - - if (ctx.path !== page.current) { - ctx.handled = false; - return; - } - if (!fn) return unhandled(ctx); - fn(ctx, nextEnter); - } - - if (prev) { - nextExit(); - } else { - nextEnter(); - } - }; - - /** - * Unhandled `ctx`. When it's not the initial - * popstate then redirect. If you wish to handle - * 404s on your own use `page('*', callback)`. - * - * @param {Context} ctx - * @api private - */ - - function unhandled(ctx) { - if (ctx.handled) return; - var current; - - if (hashbang) { - current = base + location.hash.replace('#!', ''); - } else { - current = location.pathname + location.search; - } - - if (current === ctx.canonicalPath) return; - page.stop(); - ctx.handled = false; - location.href = ctx.canonicalPath; - } - - /** - * Register an exit route on `path` with - * callback `fn()`, which will be called - * on the previous context when a new - * page is visited. - */ - page.exit = function(path, fn) { - if (typeof path === 'function') { - return page.exit('*', path); - } - - var route = new Route(path); - for (var i = 1; i < arguments.length; ++i) { - page.exits.push(route.middleware(arguments[i])); - } - }; - - /** - * Remove URL encoding from the given `str`. - * Accommodates whitespace in both x-www-form-urlencoded - * and regular percent-encoded form. - * - * @param {str} URL component to decode - */ - function decodeURLEncodedURIComponent(val) { - if (typeof val !== 'string') { return val; } - return decodeURLComponents ? decodeURIComponent(val.replace(/\+/g, ' ')) : val; - } - - /** - * Initialize a new "request" `Context` - * with the given `path` and optional initial `state`. - * - * @param {String} path - * @param {Object} state - * @api public - */ - - function Context(path, state) { - if ('/' === path[0] && 0 !== path.indexOf(base)) path = base + (hashbang ? '#!' : '') + path; - var i = path.indexOf('?'); - - this.canonicalPath = path; - this.path = path.replace(base, '') || '/'; - if (hashbang) this.path = this.path.replace('#!', '') || '/'; - - this.title = document.title; - this.state = state || {}; - this.state.path = path; - this.querystring = ~i ? decodeURLEncodedURIComponent(path.slice(i + 1)) : ''; - this.pathname = decodeURLEncodedURIComponent(~i ? path.slice(0, i) : path); - this.params = {}; - - // fragment - this.hash = ''; - if (!hashbang) { - if (!~this.path.indexOf('#')) return; - var parts = this.path.split('#'); - this.path = parts[0]; - this.hash = decodeURLEncodedURIComponent(parts[1]) || ''; - this.querystring = this.querystring.split('#')[0]; - } - } - - /** - * Expose `Context`. - */ - - page.Context = Context; - var backStack = []; - - /** - * Push state. - * - * @api private - */ - - Context.prototype.pushState = function() { - page.len++; - - if (enableHistory) { - history.pushState(this.state, this.title, hashbang && this.path !== '/' ? '#!' + this.path : this.canonicalPath); - } else { - backStack.push({ - state: this.state, - title: this.title, - url: (hashbang && this.path !== '/' ? '#!' + this.path : this.canonicalPath), - path: this.path - }); - } - }; - - /** - * Save the context state. - * - * @api public - */ - - Context.prototype.save = function () { - - if (enableHistory) { - history.replaceState(this.state, this.title, hashbang && this.path !== '/' ? '#!' + this.path : this.canonicalPath); - } else { - backStack[page.len || 0] = { - state: this.state, - title: this.title, - url: (hashbang && this.path !== '/' ? '#!' + this.path : this.canonicalPath), - path: this.path - }; - } - }; - - /** - * Initialize `Route` with the given HTTP `path`, - * and an array of `callbacks` and `options`. - * - * Options: - * - * - `sensitive` enable case-sensitive routes - * - `strict` enable strict matching for trailing slashes - * - * @param {String} path - * @param {Object} options. - * @api private - */ - - function Route(path, options) { - options = options || {}; - this.path = (path === '*') ? '(.*)' : path; - this.method = 'GET'; - this.regexp = pathtoRegexp(this.path, - this.keys = [], - options.sensitive, - options.strict); - } - - /** - * Expose `Route`. - */ - - page.Route = Route; - - /** - * Return route middleware with - * the given callback `fn()`. - * - * @param {Function} fn - * @return {Function} - * @api public - */ - - Route.prototype.middleware = function(fn) { - var self = this; - return function(ctx, next) { - if (self.match(ctx.path, ctx.params)) return fn(ctx, next); - next(); - }; - }; - - /** - * Check if this route matches `path`, if so - * populate `params`. - * - * @param {String} path - * @param {Object} params - * @return {Boolean} - * @api private - */ - - Route.prototype.match = function(path, params) { - var keys = this.keys, - qsIndex = path.indexOf('?'), - pathname = ~qsIndex ? path.slice(0, qsIndex) : path, - m = this.regexp.exec(decodeURIComponent(pathname)); - - if (!m) return false; - - for (var i = 1, len = m.length; i < len; ++i) { - var key = keys[i - 1]; - var val = decodeURLEncodedURIComponent(m[i]); - if (val !== undefined || !(hasOwnProperty.call(params, key.name))) { - params[key.name] = val; - } - } - - return true; - }; - - - var previousPopState = {}; - - function ignorePopState(event) { - - var state = event.state || {}; - - if (previousPopState.navigate === false) { - // Ignore - previousPopState = state; - return true; - } - - previousPopState = state; - return false; - } - - page.pushState = function (state, title, url) { - history.pushState(state, title, url); - previousPopState = state; - }; - - /** - * Handle "populate" events. - */ - - var onpopstate = (function () { - var loaded = false; - if ('undefined' === typeof window) { - return; - } - if (document.readyState === 'complete') { - loaded = true; - } else { - window.addEventListener('load', function() { - setTimeout(function() { - loaded = true; - }, 0); - }); - } - return function onpopstate(e) { - if (!loaded) return; - if (ignorePopState(e)) return; - if (e.state) { - var path = e.state.path; - page.replace(path, e.state, null, null, true); - } else { - page.show(location.pathname + location.hash, undefined, undefined, false, true); - } - }; - })(); - /** - * Handle "click" events. - */ - - function onclick(e) { - - if (1 !== which(e)) return; - - if (e.metaKey || e.ctrlKey || e.shiftKey) return; - if (e.defaultPrevented) return; - - - - // ensure link - var el = e.target; - while (el && 'A' !== el.nodeName) el = el.parentNode; - if (!el || 'A' !== el.nodeName) return; - - - - // Ignore if tag has - // 1. "download" attribute - // 2. rel="external" attribute - if (el.hasAttribute('download') || el.getAttribute('rel') === 'external') return; - - // ensure non-hash for the same path - var link = el.getAttribute('href'); - if (!hashbang && el.pathname === location.pathname && (el.hash || '#' === link)) return; - - - - // Check for mailto: in the href - if (link && link.indexOf('mailto:') > -1) return; - - // check target - if (el.target) return; - - // x-origin - if (!sameOrigin(el.href)) return; - - - - // rebuild path - var path = el.pathname + el.search + (el.hash || ''); - - // strip leading "/[drive letter]:" on NW.js on Windows - if (typeof process !== 'undefined' && path.match(/^\/[a-zA-Z]:\//)) { - path = path.replace(/^\/[a-zA-Z]:\//, '/'); - } - - // same page - var orig = path; - - if (path.indexOf(base) === 0) { - path = path.substr(base.length); - } - - if (hashbang) path = path.replace('#!', ''); - - if (base && orig === path) return; - - e.preventDefault(); - page.show(orig); - } - - /** - * Event button. - */ - - function which(e) { - e = e || window.event; - return null === e.which ? e.button : e.which; - } - - /** - * Check if `href` is the same origin. - */ - - function sameOrigin(href) { - var origin = location.protocol + '//' + location.hostname; - if (location.port) origin += ':' + location.port; - return (href && (0 === href.indexOf(origin))); - } - - page.sameOrigin = sameOrigin; - -}).call(this,require('_process')) -},{"_process":2,"path-to-regexp":3}],2:[function(require,module,exports){ -// shim for using process in browser - -var process = module.exports = {}; - -process.nextTick = (function () { - var canSetImmediate = typeof window !== 'undefined' - && window.setImmediate; - var canMutationObserver = typeof window !== 'undefined' - && window.MutationObserver; - var canPost = typeof window !== 'undefined' - && window.postMessage && window.addEventListener - ; - - if (canSetImmediate) { - return function (f) { return window.setImmediate(f) }; - } - - var queue = []; - - if (canMutationObserver) { - var hiddenDiv = document.createElement("div"); - var observer = new MutationObserver(function () { - var queueList = queue.slice(); - queue.length = 0; - queueList.forEach(function (fn) { - fn(); - }); - }); - - observer.observe(hiddenDiv, { attributes: true }); - - return function nextTick(fn) { - if (!queue.length) { - hiddenDiv.setAttribute('yes', 'no'); - } - queue.push(fn); - }; - } - - if (canPost) { - window.addEventListener('message', function (ev) { - var source = ev.source; - if ((source === window || source === null) && ev.data === 'process-tick') { - ev.stopPropagation(); - if (queue.length > 0) { - var fn = queue.shift(); - fn(); - } - } - }, true); - - return function nextTick(fn) { - queue.push(fn); - window.postMessage('process-tick', '*'); - }; - } - - return function nextTick(fn) { - setTimeout(fn, 0); - }; -})(); - -process.title = 'browser'; -process.browser = true; -process.env = {}; -process.argv = []; - -function noop() {} - -process.on = noop; -process.addListener = noop; -process.once = noop; -process.off = noop; -process.removeListener = noop; -process.removeAllListeners = noop; -process.emit = noop; - -process.binding = function (name) { - throw new Error('process.binding is not supported'); -}; - -// TODO(shtylman) -process.cwd = function () { return '/' }; -process.chdir = function (dir) { - throw new Error('process.chdir is not supported'); -}; - -},{}],3:[function(require,module,exports){ -var isarray = require('isarray') - -/** - * Expose `pathToRegexp`. - */ -module.exports = pathToRegexp -module.exports.parse = parse -module.exports.compile = compile -module.exports.tokensToFunction = tokensToFunction -module.exports.tokensToRegExp = tokensToRegExp - -/** - * The main path matching regexp utility. - * - * @type {RegExp} - */ -var PATH_REGEXP = new RegExp([ - // Match escaped characters that would otherwise appear in future matches. - // This allows the user to escape special characters that won't transform. - '(\\\\.)', - // Match Express-style parameters and un-named parameters with a prefix - // and optional suffixes. Matches appear as: - // - // "/:test(\\d+)?" => ["/", "test", "\d+", undefined, "?", undefined] - // "/route(\\d+)" => [undefined, undefined, undefined, "\d+", undefined, undefined] - // "/*" => ["/", undefined, undefined, undefined, undefined, "*"] - '([\\/.])?(?:(?:\\:(\\w+)(?:\\(((?:\\\\.|[^()])+)\\))?|\\(((?:\\\\.|[^()])+)\\))([+*?])?|(\\*))' -].join('|'), 'g') - -/** - * Parse a string for the raw tokens. - * - * @param {String} str - * @return {Array} - */ -function parse (str) { - var tokens = [] - var key = 0 - var index = 0 - var path = '' - var res - - while ((res = PATH_REGEXP.exec(str)) != null) { - var m = res[0] - var escaped = res[1] - var offset = res.index - path += str.slice(index, offset) - index = offset + m.length - - // Ignore already escaped sequences. - if (escaped) { - path += escaped[1] - continue - } - - // Push the current path onto the tokens. - if (path) { - tokens.push(path) - path = '' - } - - var prefix = res[2] - var name = res[3] - var capture = res[4] - var group = res[5] - var suffix = res[6] - var asterisk = res[7] - - var repeat = suffix === '+' || suffix === '*' - var optional = suffix === '?' || suffix === '*' - var delimiter = prefix || '/' - var pattern = capture || group || (asterisk ? '.*' : '[^' + delimiter + ']+?') - - tokens.push({ - name: name || key++, - prefix: prefix || '', - delimiter: delimiter, - optional: optional, - repeat: repeat, - pattern: escapeGroup(pattern) - }) - } - - // Match any characters still remaining. - if (index < str.length) { - path += str.substr(index) - } - - // If the path exists, push it onto the end. - if (path) { - tokens.push(path) - } - - return tokens -} - -/** - * Compile a string to a template function for the path. - * - * @param {String} str - * @return {Function} - */ -function compile (str) { - return tokensToFunction(parse(str)) -} - -/** - * Expose a method for transforming tokens into the path function. - */ -function tokensToFunction (tokens) { - // Compile all the tokens into regexps. - var matches = new Array(tokens.length) - - // Compile all the patterns before compilation. - for (var i = 0; i < tokens.length; i++) { - if (typeof tokens[i] === 'object') { - matches[i] = new RegExp('^' + tokens[i].pattern + '$') - } - } - - return function (obj) { - var path = '' - var data = obj || {} - - for (var i = 0; i < tokens.length; i++) { - var token = tokens[i] - - if (typeof token === 'string') { - path += token - - continue - } - - var value = data[token.name] - var segment - - if (value == null) { - if (token.optional) { - continue - } else { - throw new TypeError('Expected "' + token.name + '" to be defined') - } - } - - if (isarray(value)) { - if (!token.repeat) { - throw new TypeError('Expected "' + token.name + '" to not repeat, but received "' + value + '"') - } - - if (value.length === 0) { - if (token.optional) { - continue - } else { - throw new TypeError('Expected "' + token.name + '" to not be empty') - } - } - - for (var j = 0; j < value.length; j++) { - segment = encodeURIComponent(value[j]) - - if (!matches[i].test(segment)) { - throw new TypeError('Expected all "' + token.name + '" to match "' + token.pattern + '", but received "' + segment + '"') - } - - path += (j === 0 ? token.prefix : token.delimiter) + segment - } - - continue - } - - segment = encodeURIComponent(value) - - if (!matches[i].test(segment)) { - throw new TypeError('Expected "' + token.name + '" to match "' + token.pattern + '", but received "' + segment + '"') - } - - path += token.prefix + segment - } - - return path - } -} - -/** - * Escape a regular expression string. - * - * @param {String} str - * @return {String} - */ -function escapeString (str) { - return str.replace(/([.+*?=^!:${}()[\]|\/])/g, '\\$1') -} - -/** - * Escape the capturing group by escaping special characters and meaning. - * - * @param {String} group - * @return {String} - */ -function escapeGroup (group) { - return group.replace(/([=!:$\/()])/g, '\\$1') -} - -/** - * Attach the keys as a property of the regexp. - * - * @param {RegExp} re - * @param {Array} keys - * @return {RegExp} - */ -function attachKeys (re, keys) { - re.keys = keys - return re -} - -/** - * Get the flags for a regexp from the options. - * - * @param {Object} options - * @return {String} - */ -function flags (options) { - return options.sensitive ? '' : 'i' -} - -/** - * Pull out keys from a regexp. - * - * @param {RegExp} path - * @param {Array} keys - * @return {RegExp} - */ -function regexpToRegexp (path, keys) { - // Use a negative lookahead to match only capturing groups. - var groups = path.source.match(/\((?!\?)/g) - - if (groups) { - for (var i = 0; i < groups.length; i++) { - keys.push({ - name: i, - prefix: null, - delimiter: null, - optional: false, - repeat: false, - pattern: null - }) - } - } - - return attachKeys(path, keys) -} - -/** - * Transform an array into a regexp. - * - * @param {Array} path - * @param {Array} keys - * @param {Object} options - * @return {RegExp} - */ -function arrayToRegexp (path, keys, options) { - var parts = [] - - for (var i = 0; i < path.length; i++) { - parts.push(pathToRegexp(path[i], keys, options).source) - } - - var regexp = new RegExp('(?:' + parts.join('|') + ')', flags(options)) - - return attachKeys(regexp, keys) -} - -/** - * Create a path regexp from string input. - * - * @param {String} path - * @param {Array} keys - * @param {Object} options - * @return {RegExp} - */ -function stringToRegexp (path, keys, options) { - var tokens = parse(path) - var re = tokensToRegExp(tokens, options) - - // Attach keys back to the regexp. - for (var i = 0; i < tokens.length; i++) { - if (typeof tokens[i] !== 'string') { - keys.push(tokens[i]) - } - } - - return attachKeys(re, keys) -} - -/** - * Expose a function for taking tokens and returning a RegExp. - * - * @param {Array} tokens - * @param {Array} keys - * @param {Object} options - * @return {RegExp} - */ -function tokensToRegExp (tokens, options) { - options = options || {} - - var strict = options.strict - var end = options.end !== false - var route = '' - var lastToken = tokens[tokens.length - 1] - var endsWithSlash = typeof lastToken === 'string' && /\/$/.test(lastToken) - - // Iterate over the tokens and create our regexp string. - for (var i = 0; i < tokens.length; i++) { - var token = tokens[i] - - if (typeof token === 'string') { - route += escapeString(token) - } else { - var prefix = escapeString(token.prefix) - var capture = token.pattern - - if (token.repeat) { - capture += '(?:' + prefix + capture + ')*' - } - - if (token.optional) { - if (prefix) { - capture = '(?:' + prefix + '(' + capture + '))?' - } else { - capture = '(' + capture + ')?' - } - } else { - capture = prefix + '(' + capture + ')' - } - - route += capture - } - } - - // In non-strict mode we allow a slash at the end of match. If the path to - // match already ends with a slash, we remove it for consistency. The slash - // is valid at the end of a path match, not in the middle. This is important - // in non-ending mode, where "/test/" shouldn't match "/test//route". - if (!strict) { - route = (endsWithSlash ? route.slice(0, -2) : route) + '(?:\\/(?=$))?' - } - - if (end) { - route += '$' - } else { - // In non-ending mode, we need the capturing groups to match as much as - // possible by using a positive lookahead to the end or next path segment. - route += strict && endsWithSlash ? '' : '(?=\\/|$)' - } - - return new RegExp('^' + route, flags(options)) -} - -/** - * Normalize the given path string, returning a regular expression. - * - * An empty array can be passed in for the keys, which will hold the - * placeholder key descriptions. For example, using `/user/:id`, `keys` will - * contain `[{ name: 'id', delimiter: '/', optional: false, repeat: false }]`. - * - * @param {(String|RegExp|Array)} path - * @param {Array} [keys] - * @param {Object} [options] - * @return {RegExp} - */ -function pathToRegexp (path, keys, options) { - keys = keys || [] - - if (!isarray(keys)) { - options = keys - keys = [] - } else if (!options) { - options = {} - } - - if (path instanceof RegExp) { - return regexpToRegexp(path, keys, options) - } - - if (isarray(path)) { - return arrayToRegexp(path, keys, options) - } - - return stringToRegexp(path, keys, options) -} - -},{"isarray":4}],4:[function(require,module,exports){ -module.exports = Array.isArray || function (arr) { - return Object.prototype.toString.call(arr) == '[object Array]'; -}; - -},{}]},{},[1])(1) -}); \ No newline at end of file diff --git a/dashboard-ui/components/viewcontainer-lite.js b/dashboard-ui/components/viewcontainer-lite.js index d166495e7f..4c785df6d1 100644 --- a/dashboard-ui/components/viewcontainer-lite.js +++ b/dashboard-ui/components/viewcontainer-lite.js @@ -20,36 +20,64 @@ define([], function () { pageIndex = 0; } + var newView = normalizeNewView(options); + + var dependencies = typeof (newView) == 'string' ? null : newView.getAttribute('data-require'); + dependencies = dependencies ? dependencies.split(',') : []; + + require(dependencies, function () { + var allPages = animatedPages.querySelectorAll('.mainAnimatedPage'); + var animatable = allPages[pageIndex]; + + var currentPage = animatable.querySelector('.page-view'); + + if (currentPage) { + triggerDestroy(currentPage); + } + + for (var i = 0, length = allPages.length; i < length; i++) { + if (pageIndex == i) { + allPages[i].classList.remove('hide'); + } else { + allPages[i].classList.add('hide'); + } + } + + if (typeof (newView) == 'string') { + animatable.innerHTML = newView; + } else { + animatable.innerHTML = ''; + animatable.appendChild(newView); + } + + var view = animatable.querySelector('.page-view'); + + if (onBeforeChange) { + onBeforeChange(view, false, options); + } + + $.mobile = $.mobile || {}; + $.mobile.activePage = view; + + resolve(view); + }); + }); + } + + function normalizeNewView(options) { + + if (options.view.indexOf('data-role="page"') == -1) { var html = '
'; html += options.view; html += '
'; + return html; + } - var allPages = animatedPages.querySelectorAll('.mainAnimatedPage'); - var animatable = allPages[pageIndex]; - - var currentPage = animatable.querySelector('.page-view'); - - if (currentPage) { - triggerDestroy(currentPage); - } - - for (var i = 0, length = allPages.length; i < length; i++) { - if (pageIndex == i) { - allPages[i].classList.remove('hide'); - } else { - allPages[i].classList.add('hide'); - } - } - animatable.innerHTML = html; - - var view = animatable.querySelector('.page-view'); - - if (onBeforeChange) { - onBeforeChange(view, false, options); - } - - sendResolve(resolve, view); - }); + var elem = $(options.view)[0]; + elem.classList.add('page-view'); + elem.setAttribute('data-type', options.type || ''); + elem.setAttribute('data-url', options.url); + return elem; } var onBeforeChange; @@ -57,16 +85,6 @@ define([], function () { onBeforeChange = fn; } - function sendResolve(resolve, view) { - - // Don't report completion until the animation has finished, otherwise rendering may not perform well - setTimeout(function () { - - resolve(view); - - }, animationDuration); - } - function getSelectedIndex(animatedPages) { var allPages = animatedPages.querySelectorAll('.mainAnimatedPage'); for (var i = 0, length = allPages.length; i < length; i++) { @@ -132,7 +150,10 @@ define([], function () { } } - sendResolve(resolve, view); + $.mobile = $.mobile || {}; + $.mobile.activePage = view; + + resolve(view); return; } }