3 * https://www.mediawiki.org/wiki/OOjs
5 * Copyright 2011-2016 OOjs Team and other contributors.
6 * Released under the MIT license
7 * http://oojs-router.mit-license.org
9 * Date: 2016-05-05T19:27:58Z
16 * Does hash match entry.path? If it does apply the
17 * callback for the Entry object.
22 * @param {string} hash string to match
23 * @param {Object} entry Entry object
24 * @return {boolean} Whether hash matches entry.path
26 function matchRoute( hash, entry ) {
27 var match = hash.match( entry.path );
29 entry.callback.apply( this, match.slice( 1 ) );
36 * Provides navigation routing and location information
39 * @mixins OO.EventEmitter
43 OO.EventEmitter.call( this );
44 // use an object instead of an array for routes so that we don't
45 // duplicate entries that already exist
48 this.oldHash = this.getPath();
50 $( window ).on( 'popstate', function () {
51 self.emit( 'popstate' );
54 $( window ).on( 'hashchange', function () {
55 self.emit( 'hashchange' );
58 this.on( 'hashchange', function () {
59 // ev.originalEvent.newURL is undefined on Android 2.x
63 routeEv = $.Event( 'route', {
66 self.emit( 'route', routeEv );
68 if ( !routeEv.isDefaultPrevented() ) {
71 // if route was prevented, ignore the next hash change and revert the
72 // hash to its old value
74 self.navigate( self.oldHash );
80 self.oldHash = self.getPath();
83 OO.mixinClass( Router, OO.EventEmitter );
86 * Check the current route and run appropriate callback if it matches.
90 Router.prototype.checkRoute = function () {
91 var hash = this.getPath();
93 $.each( this.routes, function ( id, entry ) {
94 return !matchRoute( hash, entry );
99 * Bind a specific callback to a hash-based route, e.g.
102 * route( 'alert', function () { alert( 'something' ); } );
103 * route( /hi-(.*)/, function ( name ) { alert( 'Hi ' + name ) } );
104 * Note that after defining all available routes it is up to the caller
105 * to check the existing route via the checkRoute method.
108 * @param {Object} path string or RegExp to match.
109 * @param {Function} callback Callback to be run when hash changes to one
112 Router.prototype.route = function ( path, callback ) {
114 path: typeof path === 'string' ?
115 new RegExp( '^' + path.replace( /[\\^$*+?.()|[\]{}]/g, '\\$&' ) + '$' )
119 this.routes[ entry.path ] = entry;
123 * Navigate to a specific route.
126 * @param {string} path string with a route (hash without #).
128 Router.prototype.navigate = function ( path ) {
129 var history = window.history;
130 // Take advantage of `pushState` when available, to clear the hash and
131 // not leave `#` in the history. An entry with `#` in the history has
132 // the side-effect of resetting the scroll position when navigating the
134 if ( path === '' && history && history.pushState ) {
135 // To clear the hash we need to cut the hash from the URL.
136 path = window.location.href.replace( /#.*$/, '' );
137 history.pushState( null, document.title, path );
140 window.location.hash = path;
145 * Triggers back on the window
147 Router.prototype.goBack = function () {
148 window.history.back();
152 * Navigate to the previous route. This is a wrapper for window.history.back
155 * @return {jQuery.Deferred}
157 Router.prototype.back = function () {
158 var deferredRequest = $.Deferred(),
162 this.once( 'popstate', function () {
163 clearTimeout( timeoutID );
164 deferredRequest.resolve();
169 // If for some reason (old browser, bug in IE/windows 8.1, etc) popstate doesn't fire,
170 // resolve manually. Since we don't know for sure which browsers besides IE10/11 have
171 // this problem, it's better to fall back this way rather than singling out browsers
172 // and resolving the deferred request for them individually.
173 // See https://connect.microsoft.com/IE/feedback/details/793618/history-back-popstate-not-working-as-expected-in-webview-control
174 // Give browser a few ms to update its history.
175 timeoutID = setTimeout( function () {
176 self.off( 'popstate' );
177 deferredRequest.resolve();
180 return deferredRequest;
184 * Get current path (hash).
187 * @return {string} Current path.
189 Router.prototype.getPath = function () {
190 return window.location.hash.slice( 1 );
194 * Determine if current browser supports onhashchange event
199 Router.prototype.isSupported = function () {
200 return 'onhashchange' in window;
203 module.exports = Router;