~ubuntu-branches/ubuntu/raring/maas/raring-updates

« back to all changes in this revision

Viewing changes to src/maasserver/static/jslibs/yui/3.4.1/build/controller/controller-debug.js

  • Committer: Package Import Robot
  • Author(s): Andres Rodriguez
  • Date: 2012-07-03 17:42:37 UTC
  • mfrom: (1.1.13)
  • Revision ID: package-import@ubuntu.com-20120703174237-p8l0keuuznfg721k
Tags: 0.1+bzr709+dfsg-0ubuntu1
* New Upstream release
* debian/control:
  - Depends on python-celery, python-tempita, libjs-yui3-{full,min},
    libjs-raphael
* debian/maas.install:
  - Install apiclient, celeryconfig.py, maas-import-pxe-files, preseeds_v2.
  - Update to install various files from chroot, rather tha manually copy
    them from the source.
* debian/maas.links: symlink celeryconfig.py
* debian/maas.maas-celery.upstart: Add job.
* debian/rules:
  - Install celery upstart job.
  - Do not install jslibs as packages are now used.
  - Drop copying of maas_local_settings_sample.py as source now ships
    a maas_local_settings.py
* debian/patches:
  - 04-maas-http-fix.patch: Drop. Merged upstream.
  - 01-fix-database-settings.patch: Refreshed.
  - 99_enums_js.patch: Added until creation of enum.js / build process
    is fixed.
* debian/maas.postinst: Update bzr version to correctly handle upgrades.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
YUI 3.4.1 (build 4118)
3
 
Copyright 2011 Yahoo! Inc. All rights reserved.
4
 
Licensed under the BSD License.
5
 
http://yuilibrary.com/license/
6
 
*/
7
 
YUI.add('controller', function(Y) {
8
 
 
9
 
/**
10
 
The app framework provides simple MVC-like building blocks (models, model lists,
11
 
views, and controllers) for writing single-page JavaScript applications.
12
 
 
13
 
@main app
14
 
@module app
15
 
@since 3.4.0
16
 
**/
17
 
 
18
 
/**
19
 
Provides URL-based routing using HTML5 `pushState()` or the location hash.
20
 
 
21
 
@submodule controller
22
 
@since 3.4.0
23
 
**/
24
 
 
25
 
/**
26
 
Provides URL-based routing using HTML5 `pushState()` or the location hash.
27
 
 
28
 
This makes it easy to wire up route handlers for different application states
29
 
while providing full back/forward navigation support and bookmarkable, shareable
30
 
URLs.
31
 
 
32
 
@class Controller
33
 
@constructor
34
 
@extends Base
35
 
@since 3.4.0
36
 
**/
37
 
 
38
 
var HistoryHash = Y.HistoryHash,
39
 
    Lang        = Y.Lang,
40
 
    QS          = Y.QueryString,
41
 
    YArray      = Y.Array,
42
 
 
43
 
    // Android versions lower than 3.0 are buggy and don't update
44
 
    // window.location after a pushState() call, so we fall back to hash-based
45
 
    // history for them.
46
 
    //
47
 
    // See http://code.google.com/p/android/issues/detail?id=17471
48
 
    html5    = Y.HistoryBase.html5 && (!Y.UA.android || Y.UA.android >= 3),
49
 
    win      = Y.config.win,
50
 
    location = win.location,
51
 
 
52
 
    // We have to queue up pushState calls to avoid race conditions, since the
53
 
    // popstate event doesn't actually provide any info on what URL it's
54
 
    // associated with.
55
 
    saveQueue = [],
56
 
 
57
 
    /**
58
 
    Fired when the controller is ready to begin dispatching to route handlers.
59
 
 
60
 
    You shouldn't need to wait for this event unless you plan to implement some
61
 
    kind of custom dispatching logic. It's used internally in order to avoid
62
 
    dispatching to an initial route if a browser history change occurs first.
63
 
 
64
 
    @event ready
65
 
    @param {Boolean} dispatched `true` if routes have already been dispatched
66
 
      (most likely due to a history change).
67
 
    @fireOnce
68
 
    **/
69
 
    EVT_READY = 'ready';
70
 
 
71
 
function Controller() {
72
 
    Controller.superclass.constructor.apply(this, arguments);
73
 
}
74
 
 
75
 
Y.Controller = Y.extend(Controller, Y.Base, {
76
 
    // -- Public Properties ----------------------------------------------------
77
 
 
78
 
    /**
79
 
    Whether or not this browser is capable of using HTML5 history.
80
 
 
81
 
    This property is for informational purposes only. It's not configurable, and
82
 
    changing it will have no effect.
83
 
 
84
 
    @property html5
85
 
    @type Boolean
86
 
    **/
87
 
    html5: html5,
88
 
 
89
 
    /**
90
 
    Absolute root path from which all routes should be evaluated.
91
 
 
92
 
    For example, if your controller is running on a page at
93
 
    `http://example.com/myapp/` and you add a route with the path `/`, your
94
 
    route will never execute, because the path will always be preceded by
95
 
    `/myapp`. Setting `root` to `/myapp` would cause all routes to be evaluated
96
 
    relative to that root URL, so the `/` route would then execute when the
97
 
    user browses to `http://example.com/myapp/`.
98
 
 
99
 
    This property may be overridden in a subclass, set after instantiation, or
100
 
    passed as a config attribute when instantiating a `Y.Controller`-based
101
 
    class.
102
 
 
103
 
    @property root
104
 
    @type String
105
 
    @default `''`
106
 
    **/
107
 
    root: '',
108
 
 
109
 
    /**
110
 
    Array of route objects specifying routes to be created at instantiation
111
 
    time.
112
 
 
113
 
    Each item in the array must be an object with the following properties:
114
 
 
115
 
      * `path`: String or regex representing the path to match. See the docs for
116
 
        the `route()` method for more details.
117
 
      * `callback`: Function or a string representing the name of a function on
118
 
        this controller instance that should be called when the route is
119
 
        triggered. See the docs for the `route()` method for more details.
120
 
 
121
 
    This property may be overridden in a subclass or passed as a config
122
 
    attribute when instantiating a `Y.Controller`-based class, but setting it
123
 
    after instantiation will have no effect (use the `route()` method instead).
124
 
 
125
 
    If routes are passed at instantiation time, they will override any routes
126
 
    set on the prototype.
127
 
 
128
 
    @property routes
129
 
    @type Object[]
130
 
    @default `[]`
131
 
    **/
132
 
    routes: [],
133
 
 
134
 
    // -- Protected Properties -------------------------------------------------
135
 
 
136
 
    /**
137
 
    Whether or not `_dispatch()` has been called since this controller was
138
 
    instantiated.
139
 
 
140
 
    @property _dispatched
141
 
    @type Boolean
142
 
    @default undefined
143
 
    @protected
144
 
    **/
145
 
 
146
 
    /**
147
 
    Whether or not we're currently in the process of dispatching to routes.
148
 
 
149
 
    @property _dispatching
150
 
    @type Boolean
151
 
    @default undefined
152
 
    @protected
153
 
    **/
154
 
 
155
 
    /**
156
 
    Whether or not the `ready` event has fired yet.
157
 
 
158
 
    @property _ready
159
 
    @type Boolean
160
 
    @default undefined
161
 
    @protected
162
 
    **/
163
 
 
164
 
    /**
165
 
    Regex used to match parameter placeholders in route paths.
166
 
 
167
 
    Subpattern captures:
168
 
 
169
 
      1. Parameter prefix character. Either a `:` for subpath parameters that
170
 
         should only match a single level of a path, or `*` for splat parameters
171
 
         that should match any number of path levels.
172
 
      2. Parameter name.
173
 
 
174
 
    @property _regexPathParam
175
 
    @type RegExp
176
 
    @protected
177
 
    **/
178
 
    _regexPathParam: /([:*])([\w-]+)/g,
179
 
 
180
 
    /**
181
 
    Regex that matches and captures the query portion of a URL, minus the
182
 
    preceding `?` character, and discarding the hash portion of the URL if any.
183
 
 
184
 
    @property _regexUrlQuery
185
 
    @type RegExp
186
 
    @protected
187
 
    **/
188
 
    _regexUrlQuery: /\?([^#]*).*$/,
189
 
 
190
 
    /**
191
 
    Regex that matches everything before the path portion of an HTTP or HTTPS
192
 
    URL. This will be used to strip this part of the URL from a string when we
193
 
    only want the path.
194
 
 
195
 
    @property _regexUrlStrip
196
 
    @type RegExp
197
 
    @protected
198
 
    **/
199
 
    _regexUrlStrip: /^https?:\/\/[^\/]*/i,
200
 
 
201
 
    // -- Lifecycle Methods ----------------------------------------------------
202
 
    initializer: function (config) {
203
 
        var self = this;
204
 
 
205
 
        // Set config properties.
206
 
        config || (config = {});
207
 
 
208
 
        config.routes && (self.routes = config.routes);
209
 
        Lang.isValue(config.root) && (self.root = config.root);
210
 
 
211
 
        // Create routes.
212
 
        self._routes = [];
213
 
 
214
 
        YArray.each(self.routes, function (route) {
215
 
            self.route(route.path, route.callback);
216
 
        });
217
 
 
218
 
        // Set up a history instance or hashchange listener.
219
 
        if (html5) {
220
 
            self._history = new Y.HistoryHTML5({force: true});
221
 
            self._history.after('change', self._afterHistoryChange, self);
222
 
        } else {
223
 
            Y.on('hashchange', self._afterHistoryChange, win, self);
224
 
        }
225
 
 
226
 
        // Fire a 'ready' event once we're ready to route. We wait first for all
227
 
        // subclass initializers to finish, then for window.onload, and then an
228
 
        // additional 20ms to allow the browser to fire a useless initial
229
 
        // `popstate` event if it wants to (and Chrome always wants to).
230
 
        self.publish(EVT_READY, {
231
 
            defaultFn  : self._defReadyFn,
232
 
            fireOnce   : true,
233
 
            preventable: false
234
 
        });
235
 
 
236
 
        self.once('initializedChange', function () {
237
 
            Y.once('load', function () {
238
 
                setTimeout(function () {
239
 
                    self.fire(EVT_READY, {dispatched: !!self._dispatched});
240
 
                }, 20);
241
 
            });
242
 
        });
243
 
    },
244
 
 
245
 
    destructor: function () {
246
 
        if (html5) {
247
 
            this._history.detachAll();
248
 
        } else {
249
 
            Y.detach('hashchange', this._afterHistoryChange, win);
250
 
        }
251
 
    },
252
 
 
253
 
    // -- Public Methods -------------------------------------------------------
254
 
 
255
 
    /**
256
 
    Dispatches to the first route handler that matches the current URL, if any.
257
 
 
258
 
    If `dispatch()` is called before the `ready` event has fired, it will
259
 
    automatically wait for the `ready` event before dispatching. Otherwise it
260
 
    will dispatch immediately.
261
 
 
262
 
    @method dispatch
263
 
    @chainable
264
 
    **/
265
 
    dispatch: function () {
266
 
        this.once(EVT_READY, function () {
267
 
            this._ready = true;
268
 
 
269
 
            if (html5 && this.upgrade()) {
270
 
                return;
271
 
            } else {
272
 
                this._dispatch(this._getPath());
273
 
            }
274
 
        });
275
 
 
276
 
        return this;
277
 
    },
278
 
 
279
 
    /**
280
 
    Gets the current route path, relative to the `root` (if any).
281
 
 
282
 
    @method getPath
283
 
    @return {String} Current route path.
284
 
    **/
285
 
    getPath: function () {
286
 
        return this._getPath();
287
 
    },
288
 
 
289
 
    /**
290
 
    Returns `true` if this controller has at least one route that matches the
291
 
    specified URL path, `false` otherwise.
292
 
 
293
 
    @method hasRoute
294
 
    @param {String} path URL path to match.
295
 
    @return {Boolean} `true` if there's at least one matching route, `false`
296
 
      otherwise.
297
 
    **/
298
 
    hasRoute: function (path) {
299
 
        return !!this.match(path).length;
300
 
    },
301
 
 
302
 
    /**
303
 
    Returns an array of route objects that match the specified URL path.
304
 
 
305
 
    This method is called internally to determine which routes match the current
306
 
    path whenever the URL changes. You may override it if you want to customize
307
 
    the route matching logic, although this usually shouldn't be necessary.
308
 
 
309
 
    Each returned route object has the following properties:
310
 
 
311
 
      * `callback`: A function or a string representing the name of a function
312
 
        this controller that should be executed when the route is triggered.
313
 
      * `keys`: An array of strings representing the named parameters defined in
314
 
        the route's path specification, if any.
315
 
      * `path`: The route's path specification, which may be either a string or
316
 
        a regex.
317
 
      * `regex`: A regular expression version of the route's path specification.
318
 
        This regex is used to determine whether the route matches a given path.
319
 
 
320
 
    @example
321
 
        controller.route('/foo', function () {});
322
 
        controller.match('/foo');
323
 
        // => [{callback: ..., keys: [], path: '/foo', regex: ...}]
324
 
 
325
 
    @method match
326
 
    @param {String} path URL path to match.
327
 
    @return {Object[]} Array of route objects that match the specified path.
328
 
    **/
329
 
    match: function (path) {
330
 
        return YArray.filter(this._routes, function (route) {
331
 
            return path.search(route.regex) > -1;
332
 
        });
333
 
    },
334
 
 
335
 
    /**
336
 
    Removes the `root` URL from the from of _path_ (if it's there) and returns
337
 
    the result. The returned path will always have a leading `/`.
338
 
 
339
 
    @method removeRoot
340
 
    @param {String} path URL path.
341
 
    @return {String} Rootless path.
342
 
    **/
343
 
    removeRoot: function (path) {
344
 
        var root = this.root;
345
 
 
346
 
        // Strip out the non-path part of the URL, if any (e.g.
347
 
        // "http://foo.com"), so that we're left with just the path.
348
 
        path = path.replace(this._regexUrlStrip, '');
349
 
 
350
 
        if (root && path.indexOf(root) === 0) {
351
 
            path = path.substring(root.length);
352
 
        }
353
 
 
354
 
        return path.charAt(0) === '/' ? path : '/' + path;
355
 
    },
356
 
 
357
 
    /**
358
 
    Replaces the current browser history entry with a new one, and dispatches to
359
 
    the first matching route handler, if any.
360
 
 
361
 
    Behind the scenes, this method uses HTML5 `pushState()` in browsers that
362
 
    support it (or the location hash in older browsers and IE) to change the
363
 
    URL.
364
 
 
365
 
    The specified URL must share the same origin (i.e., protocol, host, and
366
 
    port) as the current page, or an error will occur.
367
 
 
368
 
    @example
369
 
        // Starting URL: http://example.com/
370
 
 
371
 
        controller.replace('/path/');
372
 
        // New URL: http://example.com/path/
373
 
 
374
 
        controller.replace('/path?foo=bar');
375
 
        // New URL: http://example.com/path?foo=bar
376
 
 
377
 
        controller.replace('/');
378
 
        // New URL: http://example.com/
379
 
 
380
 
    @method replace
381
 
    @param {String} [url] URL to set. Should be a relative URL. If this
382
 
      controller's `root` property is set, this URL must be relative to the
383
 
      root URL. If no URL is specified, the page's current URL will be used.
384
 
    @chainable
385
 
    @see save()
386
 
    **/
387
 
    replace: function (url) {
388
 
        return this._queue(url, true);
389
 
    },
390
 
 
391
 
    /**
392
 
    Adds a route handler for the specified URL _path_.
393
 
 
394
 
    The _path_ parameter may be either a string or a regular expression. If it's
395
 
    a string, it may contain named parameters: `:param` will match any single
396
 
    part of a URL path (not including `/` characters), and `*param` will match
397
 
    any number of parts of a URL path (including `/` characters). These named
398
 
    parameters will be made available as keys on the `req.params` object that's
399
 
    passed to route handlers.
400
 
 
401
 
    If the _path_ parameter is a regex, all pattern matches will be made
402
 
    available as numbered keys on `req.params`, starting with `0` for the full
403
 
    match, then `1` for the first subpattern match, and so on.
404
 
 
405
 
    Here's a set of sample routes along with URL paths that they match:
406
 
 
407
 
      * Route: `/photos/:tag/:page`
408
 
        * URL: `/photos/kittens/1`, params: `{tag: 'kittens', page: '1'}`
409
 
        * URL: `/photos/puppies/2`, params: `{tag: 'puppies', page: '2'}`
410
 
 
411
 
      * Route: `/file/*path`
412
 
        * URL: `/file/foo/bar/baz.txt`, params: `{path: 'foo/bar/baz.txt'}`
413
 
        * URL: `/file/foo`, params: `{path: 'foo'}`
414
 
 
415
 
    If multiple route handlers match a given URL, they will be executed in the
416
 
    order they were added. The first route that was added will be the first to
417
 
    be executed.
418
 
 
419
 
    @example
420
 
        controller.route('/photos/:tag/:page', function (req, next) {
421
 
          Y.log('Current tag: ' + req.params.tag);
422
 
          Y.log('Current page number: ' + req.params.page);
423
 
        });
424
 
 
425
 
    @method route
426
 
    @param {String|RegExp} path Path to match. May be a string or a regular
427
 
      expression.
428
 
    @param {Function|String} callback Callback function to call whenever this
429
 
        route is triggered. If specified as a string, the named function will be
430
 
        called on this controller instance.
431
 
      @param {Object} callback.req Request object containing information about
432
 
          the request. It contains the following properties.
433
 
        @param {Array|Object} callback.req.params Captured parameters matched by
434
 
          the route path specification. If a string path was used and contained
435
 
          named parameters, then this will be a key/value hash mapping parameter
436
 
          names to their matched values. If a regex path was used, this will be
437
 
          an array of subpattern matches starting at index 0 for the full match,
438
 
          then 1 for the first subpattern match, and so on.
439
 
        @param {String} callback.req.path The current URL path.
440
 
        @param {Object} callback.req.query Query hash representing the URL query
441
 
          string, if any. Parameter names are keys, and are mapped to parameter
442
 
          values.
443
 
      @param {Function} callback.next Callback to pass control to the next
444
 
        matching route. If you don't call this function, then no further route
445
 
        handlers will be executed, even if there are more that match. If you do
446
 
        call this function, then the next matching route handler (if any) will
447
 
        be called, and will receive the same `req` object that was passed to
448
 
        this route (so you can use the request object to pass data along to
449
 
        subsequent routes).
450
 
    @chainable
451
 
    **/
452
 
    route: function (path, callback) {
453
 
        var keys = [];
454
 
 
455
 
        this._routes.push({
456
 
            callback: callback,
457
 
            keys    : keys,
458
 
            path    : path,
459
 
            regex   : this._getRegex(path, keys)
460
 
        });
461
 
 
462
 
        return this;
463
 
    },
464
 
 
465
 
    /**
466
 
    Saves a new browser history entry and dispatches to the first matching route
467
 
    handler, if any.
468
 
 
469
 
    Behind the scenes, this method uses HTML5 `pushState()` in browsers that
470
 
    support it (or the location hash in older browsers and IE) to change the
471
 
    URL and create a history entry.
472
 
 
473
 
    The specified URL must share the same origin (i.e., protocol, host, and
474
 
    port) as the current page, or an error will occur.
475
 
 
476
 
    @example
477
 
        // Starting URL: http://example.com/
478
 
 
479
 
        controller.save('/path/');
480
 
        // New URL: http://example.com/path/
481
 
 
482
 
        controller.save('/path?foo=bar');
483
 
        // New URL: http://example.com/path?foo=bar
484
 
 
485
 
        controller.save('/');
486
 
        // New URL: http://example.com/
487
 
 
488
 
    @method save
489
 
    @param {String} [url] URL to set. Should be a relative URL. If this
490
 
      controller's `root` property is set, this URL must be relative to the
491
 
      root URL. If no URL is specified, the page's current URL will be used.
492
 
    @chainable
493
 
    @see replace()
494
 
    **/
495
 
    save: function (url) {
496
 
        return this._queue(url);
497
 
    },
498
 
 
499
 
    /**
500
 
    Upgrades a hash-based URL to an HTML5 URL if necessary. In non-HTML5
501
 
    browsers, this method is a noop.
502
 
 
503
 
    @method upgrade
504
 
    @return {Boolean} `true` if the URL was upgraded, `false` otherwise.
505
 
    **/
506
 
    upgrade: html5 ? function () {
507
 
        var hash = this._getHashPath();
508
 
 
509
 
        if (hash && hash.charAt(0) === '/') {
510
 
            // This is an HTML5 browser and we have a hash-based path in the
511
 
            // URL, so we need to upgrade the URL to a non-hash URL. This
512
 
            // will trigger a `history:change` event, which will in turn
513
 
            // trigger a dispatch.
514
 
            this.once(EVT_READY, function () {
515
 
                this.replace(hash);
516
 
            });
517
 
 
518
 
            return true;
519
 
        }
520
 
 
521
 
        return false;
522
 
    } : function () { return false; },
523
 
 
524
 
    // -- Protected Methods ----------------------------------------------------
525
 
 
526
 
    /**
527
 
    Wrapper around `decodeURIComponent` that also converts `+` chars into
528
 
    spaces.
529
 
 
530
 
    @method _decode
531
 
    @param {String} string String to decode.
532
 
    @return {String} Decoded string.
533
 
    @protected
534
 
    **/
535
 
    _decode: function (string) {
536
 
        return decodeURIComponent(string.replace(/\+/g, ' '));
537
 
    },
538
 
 
539
 
    /**
540
 
    Shifts the topmost `_save()` call off the queue and executes it. Does
541
 
    nothing if the queue is empty.
542
 
 
543
 
    @method _dequeue
544
 
    @chainable
545
 
    @see _queue
546
 
    @protected
547
 
    **/
548
 
    _dequeue: function () {
549
 
        var self = this,
550
 
            fn;
551
 
 
552
 
        // If window.onload hasn't yet fired, wait until it has before
553
 
        // dequeueing. This will ensure that we don't call pushState() before an
554
 
        // initial popstate event has fired.
555
 
        if (!YUI.Env.windowLoaded) {
556
 
            Y.once('load', function () {
557
 
                self._dequeue();
558
 
            });
559
 
 
560
 
            return this;
561
 
        }
562
 
 
563
 
        fn = saveQueue.shift();
564
 
        return fn ? fn() : this;
565
 
    },
566
 
 
567
 
    /**
568
 
    Dispatches to the first route handler that matches the specified _path_.
569
 
 
570
 
    If called before the `ready` event has fired, the dispatch will be aborted.
571
 
    This ensures normalized behavior between Chrome (which fires a `popstate`
572
 
    event on every pageview) and other browsers (which do not).
573
 
 
574
 
    @method _dispatch
575
 
    @param {String} path URL path.
576
 
    @chainable
577
 
    @protected
578
 
    **/
579
 
    _dispatch: function (path) {
580
 
        var self   = this,
581
 
            routes = self.match(path),
582
 
            req;
583
 
 
584
 
        self._dispatching = self._dispatched = true;
585
 
 
586
 
        if (!routes || !routes.length) {
587
 
            self._dispatching = false;
588
 
            return self;
589
 
        }
590
 
 
591
 
        req = self._getRequest(path);
592
 
 
593
 
        req.next = function (err) {
594
 
            var callback, matches, route;
595
 
 
596
 
            if (err) {
597
 
                Y.error(err);
598
 
            } else if ((route = routes.shift())) {
599
 
                matches  = route.regex.exec(path);
600
 
                callback = typeof route.callback === 'string' ?
601
 
                        self[route.callback] : route.callback;
602
 
 
603
 
                // Use named keys for parameter names if the route path contains
604
 
                // named keys. Otherwise, use numerical match indices.
605
 
                if (matches.length === route.keys.length + 1) {
606
 
                    req.params = YArray.hash(route.keys, matches.slice(1));
607
 
                } else {
608
 
                    req.params = matches.concat();
609
 
                }
610
 
 
611
 
                callback.call(self, req, req.next);
612
 
            }
613
 
        };
614
 
 
615
 
        req.next();
616
 
 
617
 
        self._dispatching = false;
618
 
        return self._dequeue();
619
 
    },
620
 
 
621
 
    /**
622
 
    Gets the current path from the location hash, or an empty string if the
623
 
    hash is empty.
624
 
 
625
 
    @method _getHashPath
626
 
    @return {String} Current hash path, or an empty string if the hash is empty.
627
 
    @protected
628
 
    **/
629
 
    _getHashPath: function () {
630
 
        return HistoryHash.getHash().replace(this._regexUrlQuery, '');
631
 
    },
632
 
 
633
 
    /**
634
 
    Gets the current route path.
635
 
 
636
 
    @method _getPath
637
 
    @return {String} Current route path.
638
 
    @protected
639
 
    **/
640
 
    _getPath: html5 ? function () {
641
 
        return this.removeRoot(location.pathname);
642
 
    } : function () {
643
 
        return this._getHashPath() || this.removeRoot(location.pathname);
644
 
    },
645
 
 
646
 
    /**
647
 
    Gets the current route query string.
648
 
 
649
 
    @method _getQuery
650
 
    @return {String} Current route query string.
651
 
    @protected
652
 
    **/
653
 
    _getQuery: html5 ? function () {
654
 
        return location.search.substring(1);
655
 
    } : function () {
656
 
        var hash    = HistoryHash.getHash(),
657
 
            matches = hash.match(this._regexUrlQuery);
658
 
 
659
 
        return hash && matches ? matches[1] : location.search.substring(1);
660
 
    },
661
 
 
662
 
    /**
663
 
    Creates a regular expression from the given route specification. If _path_
664
 
    is already a regex, it will be returned unmodified.
665
 
 
666
 
    @method _getRegex
667
 
    @param {String|RegExp} path Route path specification.
668
 
    @param {Array} keys Array reference to which route parameter names will be
669
 
      added.
670
 
    @return {RegExp} Route regex.
671
 
    @protected
672
 
    **/
673
 
    _getRegex: function (path, keys) {
674
 
        if (path instanceof RegExp) {
675
 
            return path;
676
 
        }
677
 
 
678
 
        path = path.replace(this._regexPathParam, function (match, operator, key) {
679
 
            keys.push(key);
680
 
            return operator === '*' ? '(.*?)' : '([^/]*)';
681
 
        });
682
 
 
683
 
        return new RegExp('^' + path + '$');
684
 
    },
685
 
 
686
 
    /**
687
 
    Gets a request object that can be passed to a route handler.
688
 
 
689
 
    @method _getRequest
690
 
    @param {String} path Current path being dispatched.
691
 
    @return {Object} Request object.
692
 
    @protected
693
 
    **/
694
 
    _getRequest: function (path) {
695
 
        return {
696
 
            path : path,
697
 
            query: this._parseQuery(this._getQuery())
698
 
        };
699
 
    },
700
 
 
701
 
    /**
702
 
    Joins the `root` URL to the specified _url_, normalizing leading/trailing
703
 
    `/` characters.
704
 
 
705
 
    @example
706
 
        controller.root = '/foo'
707
 
        controller._joinURL('bar');  // => '/foo/bar'
708
 
        controller._joinURL('/bar'); // => '/foo/bar'
709
 
 
710
 
        controller.root = '/foo/'
711
 
        controller._joinURL('bar');  // => '/foo/bar'
712
 
        controller._joinURL('/bar'); // => '/foo/bar'
713
 
 
714
 
    @method _joinURL
715
 
    @param {String} url URL to append to the `root` URL.
716
 
    @return {String} Joined URL.
717
 
    @protected
718
 
    **/
719
 
    _joinURL: function (url) {
720
 
        var root = this.root;
721
 
 
722
 
        if (url.charAt(0) === '/') {
723
 
            url = url.substring(1);
724
 
        }
725
 
 
726
 
        return root && root.charAt(root.length - 1) === '/' ?
727
 
                root + url :
728
 
                root + '/' + url;
729
 
    },
730
 
 
731
 
    /**
732
 
    Parses a URL query string into a key/value hash. If `Y.QueryString.parse` is
733
 
    available, this method will be an alias to that.
734
 
 
735
 
    @method _parseQuery
736
 
    @param {String} query Query string to parse.
737
 
    @return {Object} Hash of key/value pairs for query parameters.
738
 
    @protected
739
 
    **/
740
 
    _parseQuery: QS && QS.parse ? QS.parse : function (query) {
741
 
        var decode = this._decode,
742
 
            params = query.split('&'),
743
 
            i      = 0,
744
 
            len    = params.length,
745
 
            result = {},
746
 
            param;
747
 
 
748
 
        for (; i < len; ++i) {
749
 
            param = params[i].split('=');
750
 
 
751
 
            if (param[0]) {
752
 
                result[decode(param[0])] = decode(param[1] || '');
753
 
            }
754
 
        }
755
 
 
756
 
        return result;
757
 
    },
758
 
 
759
 
    /**
760
 
    Queues up a `_save()` call to run after all previously-queued calls have
761
 
    finished.
762
 
 
763
 
    This is necessary because if we make multiple `_save()` calls before the
764
 
    first call gets dispatched, then both calls will dispatch to the last call's
765
 
    URL.
766
 
 
767
 
    All arguments passed to `_queue()` will be passed on to `_save()` when the
768
 
    queued function is executed.
769
 
 
770
 
    @method _queue
771
 
    @chainable
772
 
    @see _dequeue
773
 
    @protected
774
 
    **/
775
 
    _queue: function () {
776
 
        var args = arguments,
777
 
            self = this;
778
 
 
779
 
        saveQueue.push(function () {
780
 
            if (html5) {
781
 
                if (Y.UA.ios && Y.UA.ios < 5) {
782
 
                    // iOS <5 has buggy HTML5 history support, and needs to be
783
 
                    // synchronous.
784
 
                    self._save.apply(self, args);
785
 
                } else {
786
 
                    // Wrapped in a timeout to ensure that _save() calls are
787
 
                    // always processed asynchronously. This ensures consistency
788
 
                    // between HTML5- and hash-based history.
789
 
                    setTimeout(function () {
790
 
                        self._save.apply(self, args);
791
 
                    }, 1);
792
 
                }
793
 
            } else {
794
 
                self._dispatching = true; // otherwise we'll dequeue too quickly
795
 
                self._save.apply(self, args);
796
 
            }
797
 
 
798
 
            return self;
799
 
        });
800
 
 
801
 
        return !this._dispatching ? this._dequeue() : this;
802
 
    },
803
 
 
804
 
    /**
805
 
    Saves a history entry using either `pushState()` or the location hash.
806
 
 
807
 
    @method _save
808
 
    @param {String} [url] URL for the history entry.
809
 
    @param {Boolean} [replace=false] If `true`, the current history entry will
810
 
      be replaced instead of a new one being added.
811
 
    @chainable
812
 
    @protected
813
 
    **/
814
 
    _save: html5 ? function (url, replace) {
815
 
        // Force _ready to true to ensure that the history change is handled
816
 
        // even if _save is called before the `ready` event fires.
817
 
        this._ready = true;
818
 
 
819
 
        this._history[replace ? 'replace' : 'add'](null, {
820
 
            url: typeof url === 'string' ? this._joinURL(url) : url
821
 
        });
822
 
 
823
 
        return this;
824
 
    } : function (url, replace) {
825
 
        this._ready = true;
826
 
 
827
 
        if (typeof url === 'string' && url.charAt(0) !== '/') {
828
 
            url = '/' + url;
829
 
        }
830
 
 
831
 
        HistoryHash[replace ? 'replaceHash' : 'setHash'](url);
832
 
        return this;
833
 
    },
834
 
 
835
 
    // -- Protected Event Handlers ---------------------------------------------
836
 
 
837
 
    /**
838
 
    Handles `history:change` and `hashchange` events.
839
 
 
840
 
    @method _afterHistoryChange
841
 
    @param {EventFacade} e
842
 
    @protected
843
 
    **/
844
 
    _afterHistoryChange: function (e) {
845
 
        var self = this;
846
 
 
847
 
        if (self._ready) {
848
 
            self._dispatch(self._getPath());
849
 
        }
850
 
    },
851
 
 
852
 
    // -- Default Event Handlers -----------------------------------------------
853
 
 
854
 
    /**
855
 
    Default handler for the `ready` event.
856
 
 
857
 
    @method _defReadyFn
858
 
    @param {EventFacade} e
859
 
    @protected
860
 
    **/
861
 
    _defReadyFn: function (e) {
862
 
        this._ready = true;
863
 
    }
864
 
}, {
865
 
    NAME: 'controller'
866
 
});
867
 
 
868
 
 
869
 
}, '3.4.1' ,{optional:['querystring-parse'], requires:['array-extras', 'base-build', 'history']});