diff options
Diffstat (limited to 'www/wiki/resources/lib/oojs-router/oojs-router.js')
-rw-r--r-- | www/wiki/resources/lib/oojs-router/oojs-router.js | 205 |
1 files changed, 205 insertions, 0 deletions
diff --git a/www/wiki/resources/lib/oojs-router/oojs-router.js b/www/wiki/resources/lib/oojs-router/oojs-router.js new file mode 100644 index 00000000..b136923e --- /dev/null +++ b/www/wiki/resources/lib/oojs-router/oojs-router.js @@ -0,0 +1,205 @@ +/*! + * OOjs Router v0.1.0 + * https://www.mediawiki.org/wiki/OOjs + * + * Copyright 2011-2016 OOjs Team and other contributors. + * Released under the MIT license + * http://oojs-router.mit-license.org + * + * Date: 2016-05-05T19:27:58Z + */ +( function ( $ ) { + +'use strict'; + +/** + * Does hash match entry.path? If it does apply the + * callback for the Entry object. + * + * @method + * @private + * @ignore + * @param {string} hash string to match + * @param {Object} entry Entry object + * @return {boolean} Whether hash matches entry.path + */ +function matchRoute( hash, entry ) { + var match = hash.match( entry.path ); + if ( match ) { + entry.callback.apply( this, match.slice( 1 ) ); + return true; + } + return false; +} + +/** + * Provides navigation routing and location information + * + * @class Router + * @mixins OO.EventEmitter + */ +function Router() { + var self = this; + OO.EventEmitter.call( this ); + // use an object instead of an array for routes so that we don't + // duplicate entries that already exist + this.routes = {}; + this.enabled = true; + this.oldHash = this.getPath(); + + $( window ).on( 'popstate', function () { + self.emit( 'popstate' ); + } ); + + $( window ).on( 'hashchange', function () { + self.emit( 'hashchange' ); + } ); + + this.on( 'hashchange', function () { + // ev.originalEvent.newURL is undefined on Android 2.x + var routeEv; + + if ( self.enabled ) { + routeEv = $.Event( 'route', { + path: self.getPath() + } ); + self.emit( 'route', routeEv ); + + if ( !routeEv.isDefaultPrevented() ) { + self.checkRoute(); + } else { + // if route was prevented, ignore the next hash change and revert the + // hash to its old value + self.enabled = false; + self.navigate( self.oldHash ); + } + } else { + self.enabled = true; + } + + self.oldHash = self.getPath(); + } ); +} +OO.mixinClass( Router, OO.EventEmitter ); + +/** + * Check the current route and run appropriate callback if it matches. + * + * @method + */ +Router.prototype.checkRoute = function () { + var hash = this.getPath(); + + $.each( this.routes, function ( id, entry ) { + return !matchRoute( hash, entry ); + } ); +}; + +/** + * Bind a specific callback to a hash-based route, e.g. + * + * @example + * route( 'alert', function () { alert( 'something' ); } ); + * route( /hi-(.*)/, function ( name ) { alert( 'Hi ' + name ) } ); + * Note that after defining all available routes it is up to the caller + * to check the existing route via the checkRoute method. + * + * @method + * @param {Object} path string or RegExp to match. + * @param {Function} callback Callback to be run when hash changes to one + * that matches. + */ +Router.prototype.route = function ( path, callback ) { + var entry = { + path: typeof path === 'string' ? + new RegExp( '^' + path.replace( /[\\^$*+?.()|[\]{}]/g, '\\$&' ) + '$' ) + : path, + callback: callback + }; + this.routes[ entry.path ] = entry; +}; + +/** + * Navigate to a specific route. + * + * @method + * @param {string} path string with a route (hash without #). + */ +Router.prototype.navigate = function ( path ) { + var history = window.history; + // Take advantage of `pushState` when available, to clear the hash and + // not leave `#` in the history. An entry with `#` in the history has + // the side-effect of resetting the scroll position when navigating the + // history. + if ( path === '' && history && history.pushState ) { + // To clear the hash we need to cut the hash from the URL. + path = window.location.href.replace( /#.*$/, '' ); + history.pushState( null, document.title, path ); + this.checkRoute(); + } else { + window.location.hash = path; + } +}; + +/** + * Triggers back on the window + */ +Router.prototype.goBack = function () { + window.history.back(); +}; + +/** + * Navigate to the previous route. This is a wrapper for window.history.back + * + * @method + * @return {jQuery.Deferred} + */ +Router.prototype.back = function () { + var deferredRequest = $.Deferred(), + self = this, + timeoutID; + + this.once( 'popstate', function () { + clearTimeout( timeoutID ); + deferredRequest.resolve(); + } ); + + this.goBack(); + + // If for some reason (old browser, bug in IE/windows 8.1, etc) popstate doesn't fire, + // resolve manually. Since we don't know for sure which browsers besides IE10/11 have + // this problem, it's better to fall back this way rather than singling out browsers + // and resolving the deferred request for them individually. + // See https://connect.microsoft.com/IE/feedback/details/793618/history-back-popstate-not-working-as-expected-in-webview-control + // Give browser a few ms to update its history. + timeoutID = setTimeout( function () { + self.off( 'popstate' ); + deferredRequest.resolve(); + }, 50 ); + + return deferredRequest; +}; + +/** + * Get current path (hash). + * + * @method + * @return {string} Current path. + */ +Router.prototype.getPath = function () { + return window.location.hash.slice( 1 ); +}; + +/** + * Determine if current browser supports onhashchange event + * + * @method + * @return {boolean} + */ +Router.prototype.isSupported = function () { + return 'onhashchange' in window; +}; + +module.exports = Router; + +}( jQuery ) ); |