diff --git a/dashboard-ui/bower_components/emby-webcomponents/.bower.json b/dashboard-ui/bower_components/emby-webcomponents/.bower.json index a5e409c1d0..baeb7e95b3 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.48", - "_release": "1.1.48", + "version": "1.1.49", + "_release": "1.1.49", "_resolution": { "type": "version", - "tag": "1.1.48", - "commit": "1301ee3681a29577ba045c0ce20fd0e914cee168" + "tag": "1.1.49", + "commit": "6fe9a4ae4aa727695fbd17a40486065ce46c0892" }, "_source": "git://github.com/MediaBrowser/emby-webcomponents.git", "_target": "~1.1.5", diff --git a/dashboard-ui/bower_components/emby-webcomponents/router.js b/dashboard-ui/bower_components/emby-webcomponents/router.js new file mode 100644 index 0000000000..aa87e64344 --- /dev/null +++ b/dashboard-ui/bower_components/emby-webcomponents/router.js @@ -0,0 +1,527 @@ +define(['loading', 'viewManager', 'skinManager', 'pluginManager', 'backdrop', 'browser'], function (loading, viewManager, skinManager, pluginManager, backdrop, browser) { + + var connectionManager; + + function isStartup(ctx) { + var path = ctx.pathname; + + if (path.indexOf('welcome') != -1) { + return true; + } + + if (path.indexOf('connectlogin') != -1) { + return true; + } + + if (path.indexOf('login') != -1) { + return true; + } + + if (path.indexOf('manuallogin') != -1) { + return true; + } + + if (path.indexOf('manualserver') != -1) { + return true; + } + + if (path.indexOf('selectserver') != -1) { + return true; + } + + if (path.indexOf('localpin') != -1) { + return true; + } + + return false; + } + + function allowAnonymous(ctx) { + + return isStartup(ctx); + } + + function redirectToLogin() { + + backdrop.clear(); + + loading.show(); + + connectionManager.connect().then(function (result) { + handleConnectionResult(result, loading); + }); + } + + function handleConnectionResult(result, loading) { + + switch (result.State) { + + case MediaBrowser.ConnectionState.SignedIn: + { + loading.hide(); + skinManager.loadUserSkin(); + } + break; + case MediaBrowser.ConnectionState.ServerSignIn: + { + result.ApiClient.getPublicUsers().then(function (users) { + + if (users.length) { + show('/startup/login.html?serverid=' + result.Servers[0].Id); + } else { + goToLocalLogin(result.ApiClient, result.Servers[0].Id); + } + }); + } + break; + case MediaBrowser.ConnectionState.ServerSelection: + { + show('/startup/selectserver.html'); + } + break; + case MediaBrowser.ConnectionState.ConnectSignIn: + { + show('/startup/welcome.html'); + } + break; + case MediaBrowser.ConnectionState.ServerUpdateNeeded: + { + require(['alert'], function (alert) { + alert(Globalize.translate('core#ServerUpdateNeeded', 'https://emby.media')).then(function () { + show('/startup/selectserver.html'); + }); + }); + } + break; + default: + break; + } + } + + function goToLocalLogin(apiClient, serverId) { + + show('/startup/manuallogin.html?serverid=' + serverId); + } + + var cacheParam = new Date().getTime(); + function loadContentUrl(ctx, next, route, request) { + + var url = route.contentPath || route.path; + + if (url.toLowerCase().indexOf('http') != 0 && url.indexOf('file:') != 0) { + url = baseUrl() + '/' + url; + } + + url += url.indexOf('?') == -1 ? '?' : '&'; + url += 'v=' + cacheParam; + + var xhr = new XMLHttpRequest(); + xhr.onload = xhr.onerror = function () { + if (this.status < 400) { + loadContent(ctx, route, this.response, request); + } else { + next(); + } + }; + xhr.onerror = next; + xhr.open('GET', url, true); + xhr.send(); + } + + function handleRoute(ctx, next, route) { + + authenticate(ctx, route, function () { + initRoute(ctx, next, route); + }); + } + + function initRoute(ctx, next, route) { + + var onInitComplete = function (controllerFactory) { + sendRouteToViewManager(ctx, next, route, controllerFactory); + }; + + require(route.dependencies || [], function () { + + if (route.controller) { + require([route.controller], onInitComplete); + } else { + onInitComplete(); + } + }); + } + + function cancelCurrentLoadRequest() { + var currentRequest = currentViewLoadRequest; + if (currentRequest) { + currentRequest.cancel = true; + } + } + + var currentViewLoadRequest; + function sendRouteToViewManager(ctx, next, route, controllerFactory) { + + cancelCurrentLoadRequest(); + + var isBackNav = ctx.isBack; + + var currentRequest = { + url: baseUrl() + ctx.path, + transition: route.transition, + isBack: isBackNav, + state: ctx.state, + type: route.type, + controllerFactory: controllerFactory, + options: { + supportsThemeMedia: route.supportsThemeMedia || false + } + }; + currentViewLoadRequest = currentRequest; + + var onNewViewNeeded = function () { + if (typeof route.path === 'string') { + + loadContentUrl(ctx, next, route, currentRequest); + + } else { + // ? TODO + next(); + } + }; + + if (!isBackNav) { + // Don't force a new view for home due to the back menu + if (route.type != 'home') { + onNewViewNeeded(); + return; + } + } + + viewManager.tryRestoreView(currentRequest).then(function () { + + // done + currentRouteInfo = { + route: route, + path: ctx.path + }; + + }, onNewViewNeeded); + } + + var firstConnectionResult; + function start() { + + loading.show(); + + require(['connectionManager'], function (connectionManagerInstance) { + + connectionManager = connectionManagerInstance; + + connectionManager.connect().then(function (result) { + + firstConnectionResult = result; + + loading.hide(); + + page({ + click: false, + hashbang: true, + enableHistory: enableHistory() + }); + }); + }); + } + + function enableHistory() { + + if (browser.xboxOne) { + return false; + } + + return true; + } + + function enableNativeHistory() { + return page.enableNativeHistory(); + } + + function authenticate(ctx, route, callback) { + + var firstResult = firstConnectionResult; + if (firstResult) { + + firstConnectionResult = null; + + if (firstResult.State != MediaBrowser.ConnectionState.SignedIn) { + + handleConnectionResult(firstResult, loading); + return; + } + } + + var server = connectionManager.currentLoggedInServer(); + var pathname = ctx.pathname.toLowerCase(); + + console.log('Emby.Page - processing path request ' + pathname); + + if (server) { + + console.log('Emby.Page - user is authenticated'); + + if (ctx.isBack && (route.isDefaultRoute /*|| isStartup(ctx)*/)) { + handleBackToDefault(); + } + else if (route.isDefaultRoute) { + console.log('Emby.Page - loading skin home page'); + skinManager.loadUserSkin(); + } else { + console.log('Emby.Page - next()'); + callback(); + } + return; + } + + console.log('Emby.Page - user is not authenticated'); + + if (!allowAnonymous(ctx)) { + + console.log('Emby.Page - route does not allow anonymous access, redirecting to login'); + redirectToLogin(); + } + else { + + console.log('Emby.Page - proceeding to ' + pathname); + callback(); + } + } + + var isHandlingBackToDefault; + function handleBackToDefault() { + + skinManager.loadUserSkin(); + + if (isHandlingBackToDefault) { + return; + } + + isHandlingBackToDefault = true; + + // This must result in a call to either + // skinManager.loadUserSkin(); + // Logout + // Or exit app + + skinManager.getCurrentSkin().showBackMenu().then(function () { + + isHandlingBackToDefault = false; + }); + } + + function loadContent(ctx, route, html, request) { + + html = Globalize.translateDocument(html, route.dictionary); + request.view = html; + + viewManager.loadView(request); + + currentRouteInfo = { + route: route, + path: ctx.path + }; + //next(); + + ctx.handled = true; + } + + var baseRoute = window.location.href.split('?')[0].replace('/index.html', ''); + // support hashbang + baseRoute = baseRoute.split('#')[0]; + if (baseRoute.lastIndexOf('/') == baseRoute.length - 1) { + baseRoute = baseRoute.substring(0, baseRoute.length - 1); + } + function baseUrl() { + return baseRoute; + } + + function getHandler(route) { + return function (ctx, next) { + handleRoute(ctx, next, route); + }; + } + + function getWindowLocationSearch(win) { + + var currentPath = currentRouteInfo ? (currentRouteInfo.path || '') : ''; + + var index = currentPath.indexOf('?'); + var search = ''; + + if (index != -1) { + search = currentPath.substring(index); + } + + return search || ''; + } + + function param(name, url) { + name = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]"); + var regexS = "[\\?&]" + name + "=([^&#]*)"; + var regex = new RegExp(regexS, "i"); + + var results = regex.exec(url || getWindowLocationSearch()); + if (results == null) + return ""; + else + return decodeURIComponent(results[1].replace(/\+/g, " ")); + } + + function back() { + + page.back(); + } + function canGoBack() { + + var curr = current(); + + if (!curr) { + return false; + } + + if (curr.type == 'home') { + return false; + } + return page.canGoBack(); + } + function show(path, options) { + + return new Promise(function (resolve, reject) { + + var baseRoute = baseUrl(); + path = path.replace(baseRoute, ''); + + if (currentRouteInfo && currentRouteInfo.path == path) { + + // can't use this with home right now due to the back menu + if (currentRouteInfo.route.type != 'home') { + resolve(); + return; + } + } + + page.show(path, options); + setTimeout(resolve, 500); + }); + } + + var currentRouteInfo; + function current() { + return currentRouteInfo ? currentRouteInfo.route : null; + } + + function goHome() { + + var skin = skinManager.getCurrentSkin(); + + var homeRoute = skin.getRoutes().filter(function (r) { + return r.type == 'home'; + })[0]; + + return show(pluginManager.mapRoute(skin, homeRoute)); + } + + function showItem(item) { + + if (typeof (item) === 'string') { + Emby.Models.item(item).then(showItem); + + } else { + skinManager.getCurrentSkin().showItem(item); + } + } + + function setTitle(title) { + skinManager.getCurrentSkin().setTitle(title); + } + + function gotoSettings() { + show('/settings/settings.html'); + } + + function selectServer() { + show('/startup/selectserver.html'); + } + + function showVideoOsd() { + var skin = skinManager.getCurrentSkin(); + + var homeRoute = skin.getRoutes().filter(function (r) { + return r.type == 'video-osd'; + })[0]; + + return show(pluginManager.mapRoute(skin, homeRoute)); + } + + var allRoutes = []; + + function addRoute(path, newRoute) { + + page(path, getHandler(newRoute)); + allRoutes.push(newRoute); + } + + function getRoutes() { + return allRoutes; + } + + function setTransparency(level) { + + if (level == 'full' || level == Emby.TransparencyLevel.Full) { + backdrop.clear(true); + document.documentElement.classList.add('transparentDocument'); + } + else if (level == 'backdrop' || level == Emby.TransparencyLevel.Backdrop) { + backdrop.externalBackdrop(true); + document.documentElement.classList.add('transparentDocument'); + } else { + backdrop.externalBackdrop(false); + document.documentElement.classList.remove('transparentDocument'); + } + } + + function pushState(state, title, url) { + + state.navigate = false; + + page.pushState(state, title, url); + } + + return { + addRoute: addRoute, + param: param, + back: back, + show: show, + start: start, + baseUrl: baseUrl, + canGoBack: canGoBack, + current: current, + redirectToLogin: redirectToLogin, + goHome: goHome, + gotoSettings: gotoSettings, + showItem: showItem, + setTitle: setTitle, + selectServer: selectServer, + showVideoOsd: showVideoOsd, + setTransparency: setTransparency, + getRoutes: getRoutes, + + pushState: pushState, + + TransparencyLevel: { + None: 0, + Backdrop: 1, + Full: 2 + }, + enableNativeHistory: enableNativeHistory + }; + +}); \ No newline at end of file diff --git a/dashboard-ui/bower_components/iron-icon/.bower.json b/dashboard-ui/bower_components/iron-icon/.bower.json index f0167baf13..9784e3a3b7 100644 --- a/dashboard-ui/bower_components/iron-icon/.bower.json +++ b/dashboard-ui/bower_components/iron-icon/.bower.json @@ -32,14 +32,14 @@ "web-component-tester": "^4.0.0", "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0" }, - "homepage": "https://github.com/polymerelements/iron-icon", + "homepage": "https://github.com/PolymerElements/iron-icon", "_release": "1.0.8", "_resolution": { "type": "version", "tag": "v1.0.8", "commit": "f36b38928849ef3853db727faa8c9ef104d611eb" }, - "_source": "git://github.com/polymerelements/iron-icon.git", + "_source": "git://github.com/PolymerElements/iron-icon.git", "_target": "^1.0.0", - "_originalSource": "polymerelements/iron-icon" + "_originalSource": "PolymerElements/iron-icon" } \ No newline at end of file diff --git a/dashboard-ui/bower_components/iron-selector/.bower.json b/dashboard-ui/bower_components/iron-selector/.bower.json index 66d7d54b0f..c6b481c2cb 100644 --- a/dashboard-ui/bower_components/iron-selector/.bower.json +++ b/dashboard-ui/bower_components/iron-selector/.bower.json @@ -36,7 +36,7 @@ "tag": "v1.3.0", "commit": "1662093611cda3fd29125cdab94a61d3d88093da" }, - "_source": "git://github.com/PolymerElements/iron-selector.git", + "_source": "git://github.com/polymerelements/iron-selector.git", "_target": "^1.0.0", - "_originalSource": "PolymerElements/iron-selector" + "_originalSource": "polymerelements/iron-selector" } \ No newline at end of file diff --git a/dashboard-ui/bower_components/page.js/.bower.json b/dashboard-ui/bower_components/page.js/.bower.json new file mode 100644 index 0000000000..1f3872eaba --- /dev/null +++ b/dashboard-ui/bower_components/page.js/.bower.json @@ -0,0 +1,40 @@ +{ + "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 new file mode 100644 index 0000000000..5067554a76 --- /dev/null +++ b/dashboard-ui/bower_components/page.js/.jsbeautifyrc @@ -0,0 +1,5 @@ +{ + "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 new file mode 100644 index 0000000000..cc5fa19a97 --- /dev/null +++ b/dashboard-ui/bower_components/page.js/.jshintrc @@ -0,0 +1,23 @@ +{ + "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 new file mode 100644 index 0000000000..faf30fd876 --- /dev/null +++ b/dashboard-ui/bower_components/page.js/bower.json @@ -0,0 +1,23 @@ +{ + "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 new file mode 100644 index 0000000000..d37374f236 --- /dev/null +++ b/dashboard-ui/bower_components/page.js/index.js @@ -0,0 +1,619 @@ + /* 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 new file mode 100644 index 0000000000..51f2190078 --- /dev/null +++ b/dashboard-ui/bower_components/page.js/page.js @@ -0,0 +1,1184 @@ +!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/bower_components/paper-behaviors/.bower.json b/dashboard-ui/bower_components/paper-behaviors/.bower.json index a6b333d335..2b04bf9f8a 100644 --- a/dashboard-ui/bower_components/paper-behaviors/.bower.json +++ b/dashboard-ui/bower_components/paper-behaviors/.bower.json @@ -45,7 +45,7 @@ "tag": "v1.0.11", "commit": "e3c1ab0c72905b58fb4d9adc2921ea73b5c085a5" }, - "_source": "git://github.com/polymerelements/paper-behaviors.git", + "_source": "git://github.com/PolymerElements/paper-behaviors.git", "_target": "^1.0.0", - "_originalSource": "polymerelements/paper-behaviors" + "_originalSource": "PolymerElements/paper-behaviors" } \ No newline at end of file diff --git a/dashboard-ui/bower_components/paper-ripple/.bower.json b/dashboard-ui/bower_components/paper-ripple/.bower.json index 2f654d71c6..157225ee71 100644 --- a/dashboard-ui/bower_components/paper-ripple/.bower.json +++ b/dashboard-ui/bower_components/paper-ripple/.bower.json @@ -32,14 +32,14 @@ "iron-test-helpers": "PolymerElements/iron-test-helpers#^1.0.0" }, "ignore": [], - "homepage": "https://github.com/polymerelements/paper-ripple", + "homepage": "https://github.com/PolymerElements/paper-ripple", "_release": "1.0.5", "_resolution": { "type": "version", "tag": "v1.0.5", "commit": "d72e7a9a8ab518b901ed18dde492df3b87a93be5" }, - "_source": "git://github.com/polymerelements/paper-ripple.git", + "_source": "git://github.com/PolymerElements/paper-ripple.git", "_target": "^1.0.0", - "_originalSource": "polymerelements/paper-ripple" + "_originalSource": "PolymerElements/paper-ripple" } \ No newline at end of file