~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/docs/controller/index.html

  • 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
 
<!DOCTYPE html>
2
 
<html lang="en">
3
 
<head>
4
 
    <meta charset="utf-8">
5
 
    <title>Controller</title>
6
 
    <link rel="stylesheet" href="http://yui.yahooapis.com/3.4.0pr3/build/cssgrids/grids-min.css">
7
 
    <link rel="stylesheet" href="../assets/css/main.css">
8
 
    <link rel="stylesheet" href="../assets/vendor/prettify/prettify-min.css">
9
 
    <script src="../../build/yui/yui-min.js"></script>
10
 
</head>
11
 
<body>
12
 
 
13
 
<div id="doc">
14
 
    <h1>Controller</h1>
15
 
 
16
 
    
17
 
        <a href="#toc" class="jump">Jump to Table of Contents</a>
18
 
    
19
 
 
20
 
    <div class="yui3-g">
21
 
        <div id="main" class="yui3-u">
22
 
            <div class="content"><div class="intro">
23
 
<p>
24
 
Controller provides URL-based same-page routing using <a href="https://developer.mozilla.org/en/DOM/Manipulating_the_browser_history">HTML5 history</a> (<code>pushState</code>) or the location hash, depending on what the user's browser supports.
25
 
</p>
26
 
 
27
 
<p>
28
 
This makes it easy to wire up route handlers for different application states while providing full back/forward navigation support and bookmarkable, shareable URLs, all handled entirely on the client.
29
 
</p>
30
 
 
31
 
<p>
32
 
If you've used a server-side routing framework like <a href="http://expressjs.com/">Express</a> or <a href="http://www.sinatrarb.com/">Sinatra</a>, Controller will look very familiar to you. This is no accident!
33
 
</p>
34
 
</div>
35
 
 
36
 
<h2 id="getting-started">Getting Started</h2>
37
 
 
38
 
<p>
39
 
To include the source files for Controller and its dependencies, first load
40
 
the YUI seed file if you haven't already loaded it.
41
 
</p>
42
 
 
43
 
<pre class="code prettyprint">&lt;script src=&quot;http:&#x2F;&#x2F;yui.yahooapis.com&#x2F;3.4.1&#x2F;build&#x2F;yui&#x2F;yui-min.js&quot;&gt;&lt;&#x2F;script&gt;</pre>
44
 
 
45
 
 
46
 
<p>
47
 
Next, create a new YUI instance for your application and populate it with the
48
 
modules you need by specifying them as arguments to the <code>YUI().use()</code> method.
49
 
YUI will automatically load any dependencies required by the modules you
50
 
specify.
51
 
</p>
52
 
 
53
 
<pre class="code prettyprint">&#x2F;&#x2F; Create a new YUI instance and populate it with the required modules.
54
 
YUI().use(&#x27;controller&#x27;, function (Y) {
55
 
    &#x2F;&#x2F; Controller is available and ready for use. Add implementation
56
 
    &#x2F;&#x2F; code here.
57
 
});</pre>
58
 
 
59
 
 
60
 
<p>
61
 
For more information on creating YUI instances and on the
62
 
<a href="http://yuilibrary.com/yui/docs/api/classes/YUI.html#method_use"><code>use()</code> method</a>, see the
63
 
documentation for the <a href="../yui/index.html">YUI Global object</a>.
64
 
</p>
65
 
 
66
 
 
67
 
<h2 id="url-based-routing-on-the-client">URL-based Routing on the Client?</h2>
68
 
 
69
 
<p>
70
 
You bet! URLs are an excellent way to maintain state in a web app, since they're easy to read, easy to change, and can be bookmarked and shared.
71
 
</p>
72
 
 
73
 
<p>
74
 
Server-side web frameworks use URLs to maintain state by routing them to different pages and by storing information in query strings. These same techniques can now be used by client-side web apps to achieve better parity with server-side logic and to provide a more seamless user experience.
75
 
</p>
76
 
 
77
 
<p>
78
 
Controller allows you to define routes that map to callback functions. Whenever the user navigates to a URL that matches a route you've defined, that route's callback function is executed and can update the UI, make Ajax requests, or perform any other necessary actions. See <a href="#routing">Routing</a> for more details on how this works.
79
 
</p>
80
 
 
81
 
<p>
82
 
Often you'll want to change the URL in order to trigger a route handler, perhaps because the user has taken an action that should change the state of your application. Controller provides a <code>save()</code> method that sets a new URL and saves it to the user's browser history. There's also a <code>replace()</code> method to replace the current URL in the user's browser history without creating a new entry. The <a href="#updating-the-url">Updating the URL</a> section describes these methods in detail.
83
 
</p>
84
 
 
85
 
<h2 id="sample-urls">Sample URLs</h2>
86
 
 
87
 
<p>
88
 
In browsers that support the HTML5 history API, Controller generates real URLs that can gracefully degrade to allow server-side handling of initial pageviews or pageviews without JS enabled. Most modern browsers (including recent versions of Firefox, Chrome, Safari, and Mobile Safari) support HTML5 history.
89
 
</p>
90
 
 
91
 
<p>
92
 
In browsers that don't support the HTML5 history API, Controller falls back on using the location hash to store URL information and trigger history changes. This mostly applies only to older browsers and Internet Explorer. Unfortunately, even Internet Explorer 9 doesn't support the HTML5 history API.
93
 
</p>
94
 
 
95
 
<p>
96
 
The table below contains examples of the kinds of URLs Controller might generate when the <code>save()</code> method is called on a Controller instance, starting from an initial URL of <code>http:&#x2F;&#x2F;example.com&#x2F;</code>.
97
 
</p>
98
 
 
99
 
<table>
100
 
  <thead>
101
 
    <tr>
102
 
      <th>Code</th>
103
 
      <th>HTML5 URL</th>
104
 
      <th>Legacy URL</th>
105
 
    </tr>
106
 
  </thead>
107
 
  <tbody>
108
 
    <tr>
109
 
      <td><code>controller.save(&#x27;&#x2F;&#x27;)</code></td>
110
 
      <td><code>http:&#x2F;&#x2F;example.com&#x2F;</code></td>
111
 
      <td><code>http:&#x2F;&#x2F;example.com&#x2F;#&#x2F;</code></td>
112
 
    </tr>
113
 
 
114
 
    <tr>
115
 
      <td><code>controller.save(&#x27;&#x2F;pie&#x2F;yum&#x27;)</code></td>
116
 
      <td><code>http:&#x2F;&#x2F;example.com&#x2F;pie&#x2F;yum</code></td>
117
 
      <td><code>http:&#x2F;&#x2F;example.com&#x2F;#&#x2F;pie&#x2F;yum</code></td>
118
 
    </tr>
119
 
 
120
 
    <tr>
121
 
      <td><code>controller.save(&#x27;&#x2F;pie?type=pecan&amp;icecream=true&#x27;)</code></td>
122
 
      <td><code>http:&#x2F;&#x2F;example.com&#x2F;pie?type=pecan&amp;icecream=true</code></td>
123
 
      <td><code>http:&#x2F;&#x2F;example.com&#x2F;#&#x2F;pie?type=pecan&amp;icecream=true</code></td>
124
 
    </tr>
125
 
  </tbody>
126
 
</table>
127
 
 
128
 
<h2 id="using-controller">Using Controller</h2>
129
 
 
130
 
<h3 id="instantiating-controller">Instantiating Controller</h3>
131
 
 
132
 
<p>
133
 
To begin adding route handlers, the first thing you'll need to do is create a new Controller instance.
134
 
</p>
135
 
 
136
 
<pre class="code prettyprint">var controller = new Y.Controller();</pre>
137
 
 
138
 
 
139
 
<p>
140
 
This is the simplest way of working with Controller, but you may also extend <code>Y.Controller</code> to create a custom Controller class that suits your needs. The <a href="#extending-ycontroller">Extending <code>Y.Controller</code></a> section explains how to do this.
141
 
</p>
142
 
 
143
 
<h4 id="config-properties">Config Properties</h4>
144
 
 
145
 
<p>
146
 
When instantiating Controller, you may optionally pass in a config object containing any of the following properties. For more details on these properties, see Controller's <a href="http://yuilibrary.com/yui/docs/api/modules/module_controller.html">API docs</a>.
147
 
</p>
148
 
 
149
 
<table>
150
 
  <thead>
151
 
    <tr>
152
 
      <th>Property</th>
153
 
      <th>Default</th>
154
 
      <th>Description</th>
155
 
    </tr>
156
 
  </thead>
157
 
  <tbody>
158
 
    <tr>
159
 
      <td><code>root</code></td>
160
 
      <td><code>&#x27;&#x27;</code></td>
161
 
      <td>
162
 
        <p>
163
 
        Root path from which all routes should be evaluated. See <a href="#setting-the-root-path">Setting the Root Path</a> for details.
164
 
        </p>
165
 
      </td>
166
 
    </tr>
167
 
 
168
 
    <tr>
169
 
      <td><code>routes</code></td>
170
 
      <td><code>[]</code></td>
171
 
      <td>
172
 
        <p>
173
 
        Array of route objects specifying routes to be created at instantiation time. This can be used as an alternative to adding routes via the <code>route()</code> method after instantiation.
174
 
        </p>
175
 
 
176
 
        <p>
177
 
        Each item in the array must be an object with the following properties:
178
 
        </p>
179
 
 
180
 
        <dl style="margin-top:1em">
181
 
          <dt><code>path</code></dt>
182
 
          <dd>
183
 
            String or regex representing the path to match. See <a href="#routing">Routing</a> for more details.
184
 
          </dd>
185
 
 
186
 
          <dt><code>callback</code></dt>
187
 
          <dd>
188
 
            A function or a string representing the name of a function on the Controller instance that should be called when the route is triggered. See <a href="#routing">Routing</a> for more details.
189
 
          </dd>
190
 
        </dl>
191
 
      </td>
192
 
    </tr>
193
 
  </tbody>
194
 
</table>
195
 
 
196
 
<p>
197
 
Here's an example that sets all the configurable properties:
198
 
</p>
199
 
 
200
 
<pre class="code prettyprint">var controller = new Y.Controller({
201
 
  root: &#x27;&#x2F;mysite&#x27;,
202
 
 
203
 
  routes: [
204
 
    {path: &#x27;&#x2F;&#x27;,    callback: function () { alert(&#x27;Hello!&#x27;); }},
205
 
    {path: &#x27;&#x2F;pie&#x27;, callback: function () { alert(&#x27;Mmm. Pie.&#x27;); }}
206
 
  ]
207
 
});</pre>
208
 
 
209
 
 
210
 
<h4 id="read-only-properties">Read-Only Properties</h4>
211
 
 
212
 
<p>
213
 
Controller instances currently expose a single informational property, <code>html5</code>, which is meant to be read-only.
214
 
</p>
215
 
 
216
 
<table>
217
 
  <thead>
218
 
    <tr>
219
 
      <th>Property</th>
220
 
      <th>Description</th>
221
 
    </tr>
222
 
  </thead>
223
 
  <tbody>
224
 
    <tr>
225
 
      <td><code>html5</code></td>
226
 
      <td>
227
 
        <p>
228
 
        Whether or not the user's browser is capable of using HTML5 history.
229
 
        </p>
230
 
 
231
 
        <p>
232
 
        This property is for informational purposes only. It's not configurable, and changing it will have no effect.
233
 
        </p>
234
 
      </td>
235
 
    </tr>
236
 
  </tbody>
237
 
</table>
238
 
 
239
 
<h4 id="setting-the-root-path">Setting the Root Path</h4>
240
 
 
241
 
<p>
242
 
Let's say the URL for your website is <code>http:&#x2F;&#x2F;example.com&#x2F;mysite&#x2F;</code>. Since Controller matches routes based on the URL path, it would look for routes beginning with <code>&#x2F;mysite&#x2F;</code>.
243
 
</p>
244
 
 
245
 
<p>
246
 
You could deal with this by ensuring that all your routes start with <code>&#x2F;mysite&#x2F;</code>, but that's tedious, and it won't work well if you're writing a component that might be used on various sites where you can't anticipate the root path.
247
 
</p>
248
 
 
249
 
<p>
250
 
This is where the <code>root</code> config property comes in. If you set <code>root</code> to <code>&#x27;&#x2F;mysite&#x27;</code>, then all routes will be evaluated relative to that root path, as illustrated below.
251
 
</p>
252
 
 
253
 
<p>
254
 
The <code>root</code> must be an absolute path.
255
 
</p>
256
 
 
257
 
<table>
258
 
  <thead>
259
 
    <tr>
260
 
      <th>URL</th>
261
 
      <th>Route (No root)</th>
262
 
      <th>Route (Root: /mysite)</th>
263
 
    </tr>
264
 
  </thead>
265
 
  <tbody>
266
 
    <tr>
267
 
      <td><code>http:&#x2F;&#x2F;example.com&#x2F;mysite&#x2F;</code></td>
268
 
      <td><code>&#x2F;mysite&#x2F;</code></td>
269
 
      <td><code>&#x2F;</code></td>
270
 
    </tr>
271
 
 
272
 
    <tr>
273
 
      <td><code>http:&#x2F;&#x2F;example.com&#x2F;mysite&#x2F;pie&#x2F;</code></td>
274
 
      <td><code>&#x2F;mysite&#x2F;pie&#x2F;</code></td>
275
 
      <td><code>&#x2F;pie&#x2F;</code></td>
276
 
    </tr>
277
 
 
278
 
    <tr>
279
 
      <td><code>http:&#x2F;&#x2F;example.com&#x2F;mysite&#x2F;ice-cream&#x2F;yum.html</code></td>
280
 
      <td><code>&#x2F;mysite&#x2F;ice-cream&#x2F;yum.html</code></td>
281
 
      <td><code>&#x2F;ice-cream&#x2F;yum.html</code></td>
282
 
    </tr>
283
 
  </tbody>
284
 
</table>
285
 
 
286
 
<h3 id="extending-ycontroller">Extending <code>Y.Controller</code></h3>
287
 
 
288
 
<p>
289
 
While <code>Y.Controller</code> may be instantiated and used directly, you might find it more convenient to extend the <code>Y.Controller</code> class to create a subclass customized for your particular needs.
290
 
</p>
291
 
 
292
 
<p>
293
 
Use the <code>Y.Base.create()</code> method to extend <code>Y.Controller</code> and add or override prototype and static members. You may also optionally specify one or more <a href="../base/index.html#extensions">Base extensions</a> to mix into your new class.
294
 
</p>
295
 
 
296
 
<pre class="code prettyprint">&#x2F;&#x2F; Create a Y.CustomController class that extends Y.Controller.
297
 
Y.CustomController = Y.Base.create(&#x27;customController&#x27;, Y.Controller, [], {
298
 
  &#x2F;&#x2F; Add or override prototype properties and methods here.
299
 
}, {
300
 
  &#x2F;&#x2F; Add static properties and methods here.
301
 
});</pre>
302
 
 
303
 
 
304
 
<p>
305
 
One benefit of extending <code>Y.Controller</code> is that you can easily add default config properties, routes, and route handlers to the prototype of your custom Controller class, and they'll be shared by all instances of that class unless overridden at the instance level:
306
 
</p>
307
 
 
308
 
<pre class="code prettyprint">Y.CustomController = Y.Base.create(&#x27;customController&#x27;, Y.Controller, [], {
309
 
  &#x2F;&#x2F; Evaluate all routes relative to this root path.
310
 
  root: &#x27;&#x2F;mysite&#x27;,
311
 
 
312
 
  &#x2F;&#x2F; Share these default routes with all CustomController instances.
313
 
  routes: [
314
 
    {path: &#x27;&#x2F;&#x27;,    callback: &#x27;index&#x27;},
315
 
    {path: &#x27;&#x2F;pie&#x27;, callback: &#x27;pie&#x27;}
316
 
  ],
317
 
 
318
 
  &#x2F;&#x2F; Default route handlers inherited by all CustomController instances.
319
 
  index: function (req) {
320
 
    &#x2F;&#x2F; ... handle the &#x2F; route ...
321
 
  },
322
 
 
323
 
  pie: function (req) {
324
 
    &#x2F;&#x2F; ... handle the &#x2F;pie route ...
325
 
  }
326
 
});
327
 
 
328
 
&#x2F;&#x2F; Create a CustomController instance that inherits the defaults and adds to
329
 
&#x2F;&#x2F; them.
330
 
var controller = new Y.CustomController();
331
 
 
332
 
controller.route(&#x27;&#x2F;cheesecake&#x27;, function (req) {
333
 
  &#x2F;&#x2F; ... handle the &#x2F;cheesecake route
334
 
});</pre>
335
 
 
336
 
 
337
 
<p>
338
 
Now all instances of <code>Y.CustomController</code> will inherit all the custom defaults and can add to or override them. The <code>controller</code> instance created here will handle the <code>&#x2F;</code> and <code>&#x2F;pie</code> routes in addition to its own <code>&#x2F;cheesecake</code> route, and will evaluate all routes from the <code>&#x2F;mysite</code> root path.
339
 
</p>
340
 
 
341
 
<h3 id="routing">Routing</h3>
342
 
 
343
 
<p>
344
 
Use the <code>route()</code> method to add a new route to a Controller instance. The first parameter is a path specification or regular expression that the URL path must match, and the second parameter is a callback function or function name to execute when the route is matched.
345
 
</p>
346
 
 
347
 
<pre class="code prettyprint">var controller = new Y.Controller();
348
 
 
349
 
&#x2F;&#x2F; Add a route using a string as the path specification.
350
 
controller.route(&#x27;&#x2F;pie&#x27;, function () {
351
 
  Y.log(&#x27;You visited http:&#x2F;&#x2F;example.com&#x2F;pie&#x27;);
352
 
});
353
 
 
354
 
&#x2F;&#x2F; Add a route using a regular expression as the path specification.
355
 
controller.route(&#x2F;^\&#x2F;cake$&#x2F;, function () {
356
 
  Y.log(&#x27;You visited http:&#x2F;&#x2F;example.com&#x2F;cake&#x27;);
357
 
});</pre>
358
 
 
359
 
 
360
 
<p>
361
 
Routes are always evaluated in the order they're added. The first route that matches a given URL is executed, and any subsequent routes that also match the URL will not be executed unless the first route passes control to the next route. See <a href="#chaining-routes">Chaining Routes</a> for more info on executing more than one route for a given URL.
362
 
</p>
363
 
 
364
 
<p>
365
 
When a route callback is specified as a string instead of a function, it's assumed to represent the name of a function on the Controller instance.
366
 
</p>
367
 
 
368
 
<pre class="code prettyprint">var controller = new Y.Controller();
369
 
 
370
 
controller.pie = function () {
371
 
  Y.log(&#x27;You visited http:&#x2F;&#x2F;example.com&#x2F;pie&#x27;);
372
 
};
373
 
 
374
 
&#x2F;&#x2F; Add a route using controller.pie as the route callback.
375
 
controller.route(&#x27;&#x2F;pie&#x27;, &#x27;pie&#x27;);</pre>
376
 
 
377
 
 
378
 
<p>
379
 
As an alternative to using the <code>route()</code> method, routes may be added at instantiation time using the <code>routes</code> config property:
380
 
</p>
381
 
 
382
 
<pre class="code prettyprint">var controller = new Y.Controller({
383
 
  routes: [
384
 
    {path: &#x27;&#x2F;pie&#x27;, callback: function () {
385
 
      Y.log(&#x27;You visited http:&#x2F;&#x2F;example.com&#x2F;pie&#x27;);
386
 
    }},
387
 
 
388
 
    {path: &#x2F;^\&#x2F;cake$&#x2F;, callback: function () {
389
 
      Y.log(&#x27;You visited http:&#x2F;&#x2F;example.com&#x2F;cake&#x27;);
390
 
    }}
391
 
  ]
392
 
});</pre>
393
 
 
394
 
 
395
 
<p>
396
 
This is functionally equivalent to adding the routes via the <code>route()</code> method, except that the routes are added during the Controller's initialization stage rather than after.
397
 
</p>
398
 
 
399
 
<h4 id="paths-placeholders-and-regexps">Paths, Placeholders, and RegExps</h4>
400
 
 
401
 
<p>
402
 
A route path may be specified as either a string or a regular expression. When a string is provided, it's compiled to a regular expression internally. If the regex matches the path portion (not including protocol, domain, query string, etc.) of a URL being dispatched, the route callback will be executed.
403
 
</p>
404
 
 
405
 
<p>
406
 
Path strings may contain placeholders. When a route is matched, the values of these placeholders will be made available to the route callback on the <code>req.params</code> object.
407
 
</p>
408
 
 
409
 
<p>
410
 
A placeholder prefixed by a <code>:</code>, like <code>:pie</code>, will match any character except for a path separator (<code>&#x2F;</code>).
411
 
</p>
412
 
 
413
 
<pre class="code prettyprint">controller.route(&#x27;&#x2F;pie&#x2F;:type&#x2F;:slices&#x27;, function (req) {
414
 
  Y.log(&quot;You ordered &quot; + req.params.slices + &quot; slices of &quot;
415
 
      + req.params.type + &quot; pie.&quot;);
416
 
});
417
 
 
418
 
controller.save(&#x27;&#x2F;pie&#x2F;apple&#x2F;2&#x27;);
419
 
&#x2F;&#x2F; =&gt; &quot;You ordered 2 slices of apple pie.&quot;
420
 
 
421
 
controller.save(&#x27;&#x2F;pie&#x2F;lemon+meringue&#x2F;42&#x27;);
422
 
&#x2F;&#x2F; =&gt; &quot;You ordered 42 slices of lemon meringue pie.&quot;</pre>
423
 
 
424
 
 
425
 
<p>
426
 
A placeholder prefixed by a <code>*</code>, like <code>*path</code>, will match as many characters as it can until the next character after the placeholder, including path separators.
427
 
</p>
428
 
 
429
 
<pre class="code prettyprint">controller.route(&#x27;&#x2F;files&#x2F;*path&#x27;, function (req) {
430
 
  Y.log(&quot;You requested the file &quot; + req.params.path);
431
 
});
432
 
 
433
 
controller.save(&#x27;&#x2F;files&#x2F;recipes&#x2F;pie&#x2F;pecan.html&#x27;);
434
 
&#x2F;&#x2F; =&gt; &quot;You requested the file recipes&#x2F;pie&#x2F;pecan.html&quot;</pre>
435
 
 
436
 
 
437
 
<p>
438
 
Placeholder names may contain any character in the range <code>[A-Za-z0-9_-]</code> (so <code>:foo-bar</code> is a valid placeholder but <code>:foo bar</code> is not).
439
 
</p>
440
 
 
441
 
<p>
442
 
When a regular expression is used as a path specification, <code>req.params</code> will be an array. The first item in the array is the entire matched string, and subsequent items are captured subpattern matches (if any).
443
 
</p>
444
 
 
445
 
<pre class="code prettyprint">controller.route(&#x2F;^\&#x2F;pie\&#x2F;([^\&#x2F;]*)\&#x2F;([^\&#x2F;]*)$&#x2F;, function (req) {
446
 
  Y.log(&quot;You ordered &quot; + req.params[1] + &quot; slices of &quot;
447
 
      + req.params[2] + &quot; pie.&quot;);
448
 
});
449
 
 
450
 
controller.save(&#x27;&#x2F;pie&#x2F;maple+custard&#x2F;infinity&#x27;);
451
 
&#x2F;&#x2F; =&gt; &quot;You ordered infinity slices of maple custard pie.&quot;</pre>
452
 
 
453
 
 
454
 
<h4 id="route-callbacks">Route Callbacks</h4>
455
 
 
456
 
<p>
457
 
When a route is matched, the callback function associated with that route will be executed, and will receive two parameters:
458
 
</p>
459
 
 
460
 
<dl>
461
 
  <dt><strong><code>req</code> (Object)</strong></dt>
462
 
  <dd>
463
 
    <p>
464
 
    An object that contains information about the request that triggered the route. It contains the following properties:
465
 
    </p>
466
 
 
467
 
    <dl>
468
 
      <dt><strong><code>params</code> (Object or Array)</strong></dt>
469
 
      <dd>
470
 
        <p>
471
 
        Parameters matched by the route path specification.
472
 
        </p>
473
 
 
474
 
        <p>
475
 
        If a string path was used and contained named parameters, then <code>params</code> will be a hash with parameter names as keys and the matched substrings as values. If a regex path was used, <code>params</code> will be an array of matches starting at index <code>0</code> for the full string matched, then <code>1</code> for the first subpattern match, and so on.
476
 
        </p>
477
 
      </dd>
478
 
 
479
 
      <dt><strong><code>path</code> (String)</strong></dt>
480
 
      <dd>
481
 
        <p>
482
 
        The current URL path matched by the route.
483
 
        </p>
484
 
      </dd>
485
 
 
486
 
      <dt><strong><code>query</code> (Object)</strong></dt>
487
 
      <dd>
488
 
        <p>
489
 
        Hash of query string parameters and values specified in the URL, if any.
490
 
        </p>
491
 
      </dd>
492
 
    </dl>
493
 
  </dd>
494
 
 
495
 
  <dt><strong><code>next</code> (Function)</strong></dt>
496
 
  <dd>
497
 
    <p>
498
 
    A function that will pass control to the next matching route (if any) when called. If not called, no further routes will be executed.
499
 
    </p>
500
 
  </dd>
501
 
</dl>
502
 
 
503
 
<p>
504
 
Inside a route callback, the <code>this</code> keyword will always refer to the Controller instance that executed that route.
505
 
</p>
506
 
 
507
 
<h4 id="chaining-routes">Chaining Routes</h4>
508
 
 
509
 
<p>
510
 
By default, only the first route that matches a URL will be executed, even if there are several routes that match. However, when a route is executed, it will receive a <code>next()</code> function as its second parameter. Calling this function will execute the next matching route if there is one.
511
 
</p>
512
 
 
513
 
<pre class="code prettyprint">controller.route(&#x27;&#x2F;pie&#x27;, function (req, next) {
514
 
  Y.log(&#x27;Callback #1 executed!&#x27;);
515
 
  next();
516
 
});
517
 
 
518
 
controller.route(&#x27;&#x2F;pie&#x27;, function (req) {
519
 
  Y.log(&#x27;Callback #2 executed!&#x27;);
520
 
});
521
 
 
522
 
controller.route(&#x27;&#x2F;pie&#x27;, function (req) {
523
 
  Y.log(&#x27;Callback #3 executed!&#x27;);
524
 
});
525
 
 
526
 
controller.save(&#x27;&#x2F;pie&#x27;);
527
 
&#x2F;&#x2F; =&gt; &quot;Callback #1 executed!&quot;
528
 
&#x2F;&#x2F; =&gt; &quot;Callback #2 executed!&quot;</pre>
529
 
 
530
 
 
531
 
<p>
532
 
If you want the first route callback to pass some data along to subsequent callbacks, you can attach that data to the <code>req</code> object, which is shared between all routes that are executed during a single request.
533
 
</p>
534
 
 
535
 
<pre class="code prettyprint">controller.route(&#x27;&#x2F;ice-cream&#x27;, function (req, next) {
536
 
  req.flavor = &#x27;cookie dough&#x27;;
537
 
  next();
538
 
});
539
 
 
540
 
controller.route(&#x27;&#x2F;ice-cream&#x27;, function (req) {
541
 
  Y.log(&quot;I sure do like &quot; + req.flavor + &quot; ice cream.&quot;);
542
 
});
543
 
 
544
 
controller.save(&#x27;&#x2F;ice-cream&#x27;);
545
 
&#x2F;&#x2F; =&gt; &quot;I sure do like cookie dough ice cream.&quot;</pre>
546
 
 
547
 
 
548
 
<h3 id="updating-the-url">Updating the URL</h3>
549
 
 
550
 
<p>
551
 
Call the <code>save()</code> or <code>replace()</code> methods to update the URL and store an entry in the browser's history.
552
 
</p>
553
 
 
554
 
<p>
555
 
The only difference between the two is that <code>save()</code> saves a new history entry, while <code>replace()</code> replaces the current history entry.
556
 
</p>
557
 
 
558
 
<pre class="code prettyprint">&#x2F;&#x2F; Save a new history entry.
559
 
controller.save(&#x27;&#x2F;cake&#x27;);
560
 
 
561
 
&#x2F;&#x2F; Replace the current history entry with a new one.
562
 
controller.replace(&#x27;&#x2F;pie&#x27;);</pre>
563
 
 
564
 
 
565
 
<p>
566
 
Changing the URL in this way will trigger a dispatch, and will execute the first route that matches the new URL (if any). A dispatch will also be triggered automatically whenever the user uses the browser's back or forward buttons.
567
 
</p>
568
 
 
569
 
<p>
570
 
When you need to provide state information to your route handlers, it's best to provide that information as query parameters in the URL. This way, your application's state is always reflected in the current URL, and can be properly restored when a URL is bookmarked or copied.
571
 
</p>
572
 
 
573
 
<pre class="code prettyprint">controller.route(&#x27;&#x2F;ice-cream&#x27;, function (req) {
574
 
  var flavor = req.query.flavor;
575
 
 
576
 
  if (flavor === &#x27;vanilla&#x27;) {
577
 
    Y.log(&#x27;Vanilla? How boring.&#x27;);
578
 
  } else if (flavor === &#x27;americone dream&#x27;) {
579
 
    Y.log(&#x27;Mmmm, Colbert-flavored ice cream.&#x27;);
580
 
  } else {
581
 
    Y.log(&#x27;Error! Error! Does not compute!&#x27;);
582
 
  }
583
 
});
584
 
 
585
 
controller.save(&#x27;&#x2F;ice-cream?flavor=vanilla&#x27;);
586
 
&#x2F;&#x2F; =&gt; &quot;Vanilla? How boring.&quot;
587
 
 
588
 
controller.save(&#x27;&#x2F;ice-cream?flavor=americone+dream&#x27;);
589
 
&#x2F;&#x2F; =&gt; &quot;Mmm, Colbert-flavored ice cream.&quot;
590
 
 
591
 
controller.save(&#x27;&#x2F;ice-cream?flavor=kitten&#x27;);
592
 
&#x2F;&#x2F; =&gt; &quot;Error! Error! Does not compute!&quot;</pre>
593
 
 
594
 
 
595
 
<h4 id="capturing-link-clicks">Capturing Link Clicks</h4>
596
 
 
597
 
<p>
598
 
An excellent way to provide progressive enhancement on a page with URLs that can be handled by both the server and the client is to use normal links, and then add a delegated event handler to capture clicks on those links and use client-side routing when JavaScript is available. You can also use this technique to selectively use client-side routing or server-side routing depending on which link is clicked.
599
 
</p>
600
 
 
601
 
<pre class="code prettyprint">&#x2F;&#x2F; This sample requires the &#x60;node-event-delegate&#x60; module.
602
 
YUI().use(&#x27;controller&#x27;, &#x27;node-event-delegate&#x27;, function (Y) {
603
 
  &#x2F;&#x2F; ... create a controller instance as described in the sections above ...
604
 
 
605
 
  &#x2F;&#x2F; Attach a delegated click handler to listen for clicks on all links on the
606
 
  &#x2F;&#x2F; page.
607
 
  Y.one(&#x27;body&#x27;).delegate(&#x27;click&#x27;, function (e) {
608
 
    &#x2F;&#x2F; Allow the native behavior on middle&#x2F;right-click, or when Ctrl or Command
609
 
    &#x2F;&#x2F; are pressed.
610
 
    if (e.button !== 1 || e.ctrlKey || e.metaKey) {
611
 
      return;
612
 
    }
613
 
 
614
 
    &#x2F;&#x2F; Remove the non-path portion of the URL, and any portion of the path that
615
 
    &#x2F;&#x2F; isn&#x27;t relative to this controller&#x27;s root path.
616
 
    var path = controller.removeRoot(e.currentTarget.get(&#x27;href&#x27;));
617
 
 
618
 
    &#x2F;&#x2F; If the controller has a route that matches this path, then use the
619
 
    &#x2F;&#x2F; controller to update the URL and handle the route. Otherwise, let the
620
 
    &#x2F;&#x2F; browser handle the click normally.
621
 
    if (controller.hasRoute(path)) {
622
 
      e.preventDefault();
623
 
      controller.save(path);
624
 
    }
625
 
  }, &#x27;a&#x27;);
626
 
});</pre>
627
 
 
628
 
 
629
 
<p>
630
 
Now a click on any link, such as <code>&lt;a href=&quot;http:&#x2F;&#x2F;example.com&#x2F;foo&quot;&gt;Click me!&lt;&#x2F;a&gt;</code>, will automatically be handled by the controller if there's a matching route. If there's no matching route, or if the user doesn't have JavaScript enabled, the link click will be handled normally by the browser.
631
 
</p>
632
 
 
633
 
<h3 id="to-dispatch-or-not-to-dispatch">To Dispatch or Not to Dispatch?</h3>
634
 
 
635
 
<p>
636
 
Dispatching is what it's called when Controller looks for routes that match the current URL and then executes the callback of the first matching route (if any).
637
 
</p>
638
 
 
639
 
<p>
640
 
A dispatch can be triggered in the following ways:
641
 
</p>
642
 
 
643
 
<ul>
644
 
  <li>Automatically, whenever the URL changes after the initial pageview.</li>
645
 
  <li>Manually, by calling the <code>dispatch()</code> method.</li>
646
 
  <li>Manually (but only in HTML5 browsers), by calling the <code>upgrade()</code> method when the URL contains a hash-based route that was copied from a legacy browser.</li>
647
 
</ul>
648
 
 
649
 
<p>
650
 
It's important to remember that if a user bookmarks or copies an HTML5 URL generated by Controller and visits that URL later, the full URL will be sent to the server. On the other hand, if a user bookmarks or copies a hash-based URL in a legacy browser and then visits it later, the hash portion of the URL <em>won't</em> be sent to the server.
651
 
</p>
652
 
 
653
 
<p>
654
 
For instance, let's say a user visits your website at <code>http:&#x2F;&#x2F;example.com&#x2F;</code>. On that page, you have the following code:
655
 
</p>
656
 
 
657
 
<pre class="code prettyprint">&lt;button id=&quot;pie&quot;&gt;Click me if you like pie!&lt;&#x2F;button&gt;
658
 
 
659
 
&lt;script&gt;
660
 
YUI().use(&#x27;controller&#x27;, &#x27;node&#x27;, function (Y) {
661
 
  var controller = new Y.Controller();
662
 
 
663
 
  controller.route(&#x27;&#x2F;pie&#x27;, function () {
664
 
    &#x2F;&#x2F; Show the user a photo of a delicious pie.
665
 
  });
666
 
 
667
 
  Y.one(&#x27;#pie&#x27;).on(&#x27;click&#x27;, function () {
668
 
    controller.save(&#x27;&#x2F;pie&#x27;);
669
 
  });
670
 
});
671
 
&lt;&#x2F;script&gt;</pre>
672
 
 
673
 
 
674
 
<p>
675
 
In an HTML5 browser, when the user clicks the button, Controller will change the URL to <code>http:&#x2F;&#x2F;example.com&#x2F;pie</code> and then execute the <code>&#x2F;pie</code> route, but the browser won't contact the server. If the user bookmarks this URL and then loads it again later, the web server will handle the request. If it doesn't recognize the path <code>&#x2F;pie</code>, it may return a 404 error, which would be no good.
676
 
</p>
677
 
 
678
 
<p>
679
 
In a legacy browser, clicking the button will cause the URL to change to <code>http:&#x2F;&#x2F;example.com&#x2F;#&#x2F;pie</code>, and the <code>&#x2F;pie</code> route will be executed, also without contacting the server. The difference is that if the user bookmarks this URL and loads it later, the hash portion won't be sent to the server. The server will only see <code>http:&#x2F;&#x2F;example.com&#x2F;</code>, so it will be necessary to handle the request on the client as well.
680
 
</p>
681
 
 
682
 
<p>
683
 
There are two ways to deal with this. One way would be to implement server-side logic to handle requests for <code>&#x2F;pie</code> and render the appropriate page. To provide fallback support for hash-based URLs, we can modify the client-side code to call the <code>dispatch()</code> method in legacy browsers, by adding the following:
684
 
</p>
685
 
 
686
 
<pre class="code prettyprint">&#x2F;&#x2F; Always dispatch to an initial route on legacy browsers. Only dispatch to an
687
 
&#x2F;&#x2F; initial route on HTML5 browsers if it&#x27;s necessary in order to upgrade a
688
 
&#x2F;&#x2F; legacy hash URL to an HTML5 URL.
689
 
if (controller.html5) {
690
 
  controller.upgrade();
691
 
} else {
692
 
  controller.dispatch();
693
 
}</pre>
694
 
 
695
 
 
696
 
<p>
697
 
The benefit of this is that HTML5 URLs will be rendered on the server and won't present any difficulties for search crawlers or users with JS disabled. Meanwhile, hash-based URLs will be handled on the client as long as JS is enabled. The drawback with this method is that it may require maintaining duplicate logic on both the server and the client.
698
 
</p>
699
 
 
700
 
<p>
701
 
The other way to handle this would be to configure the server to render the same initial page for all URLs (it would essentially route all paths to the same page), and always dispatch on the client, regardless of the browser's capabilities:
702
 
</p>
703
 
 
704
 
<pre class="code prettyprint">&#x2F;&#x2F; Dispatch to an initial route on all browsers.
705
 
controller.dispatch();</pre>
706
 
 
707
 
 
708
 
<p>
709
 
On the one hand, this solution avoids most of the server-side complexity and keeps all the controller logic on the client. On the other, it requires the client to support JavaScript, so it won't play well with search crawlers that can't run JavaScript or users who disable JavaScript.
710
 
</p>
711
 
 
712
 
<h2 id="best-practices">Best Practices</h2>
713
 
 
714
 
<h3 id="html5-urls-vs-hash-urls">HTML5 URLs vs. Hash URLs</h3>
715
 
 
716
 
<p>
717
 
As discussed in <a href="#to-dispatch-or-not-to-dispatch">To Dispatch or Not to Dispatch?</a>, there are benefits and drawbacks to both HTML5 URLs&mdash;that is, URLs generated via the HTML5 history API&mdash;and hash-based URLs. This adds difficulty to any client-side URL-based routing solution, and unfortunately it means you may need to make some tough choices about the architecture of your application.
718
 
</p>
719
 
 
720
 
<p>
721
 
The primary benefit of HTML5 URLs is that they can potentially be handled on the server as well as on the client. This has potential benefits for application architecture (rendering an initial pageview on the server is often faster than rendering it on the client) and for preventing link rot (if you rewrite or replace your application at some point, it's relatively easy to set up server-side redirects to point the old URLs to a new location). That said, HTML5 URLs may require more server-side logic in order to work correctly.
722
 
</p>
723
 
 
724
 
<p>
725
 
Hash-based URLs, on the other hand, can't be handled on the server because browsers don't send the hash portion of a URL to a server. This means they must always be rendered on the client using JavaScript. Even worse, if you eventually rewrite or replace your web app, it's extremely unlikely that you'll be able to keep that old JavaScript around just for the sake of redirecting old URLs, which means any old hash-based URLs in the wild are likely to stop working.
726
 
</p>
727
 
 
728
 
<p>
729
 
In general, the benefits of HTML5 URLs tend to outweigh the drawbacks when compared to hash-based URLs, which is why Controller automatically defaults to using HTML5 URLs in browsers that support it. The hash-based URLs generated by Controller should be used only as a fallback for legacy browsers.
730
 
</p>
731
 
 
732
 
<h3 id="cross-browser-url-compatibility">Cross-browser URL Compatibility</h3>
733
 
 
734
 
<p>
735
 
It's very important that any URL generated by Controller be usable in any browser. If a user of a legacy browser copies a URL and shares it with a friend who uses an HTML5 browser, that URL should still work. And if someone copies an HTML5 URL and shares it with a user of a legacy browser, that needs to work too.
736
 
</p>
737
 
 
738
 
<p>
739
 
When a hash-based URL is loaded in an HTML5 browser and <code>dispatch()</code> or <code>upgrade()</code> are called, Controller automatically upgrades the URL to an HTML5 URL. So a URL like <code>http:&#x2F;&#x2F;example.com&#x2F;#&#x2F;pie</code> will become <code>http:&#x2F;&#x2F;example.com&#x2F;pie</code>, and the HTML5 browser will execute the appropriate route handler.
740
 
</p>
741
 
 
742
 
<p>
743
 
The difference between the <code>dispatch()</code> and <code>upgrade()</code> methods is that <code>dispatch()</code> always dispatches to the first route that matches the current URL, whereas <code>upgrade()</code> will only dispatch if the browser is an HTML5 browser and the URL is a legacy hash-based URL that must be handled on the client in order to upgrade it to an HTML5 URL.
744
 
</p>
745
 
 
746
 
<p>
747
 
When an HTML5 URL like <code>http:&#x2F;&#x2F;example.com&#x2F;pie</code> is loaded in a legacy browser, what happens depends on how your server is configured:
748
 
</p>
749
 
 
750
 
<ul>
751
 
  <li><p>If the server is capable of handling the URL, then it should render the page in the appropriate state, and Controller won't need to do anything.</p></li>
752
 
  <li><p>If the server is not capable of handling the URL, then it should render the initial page state and you should call Controller's <code>dispatch()</code> method. Controller will parse the HTML5 URL and execute the appropriate route, even in a legacy browser.</p></li>
753
 
</ul>
754
 
 
755
 
<p>
756
 
For more on how dispatching works, see <a href="#to-dispatch-or-not-to-dispatch">To Dispatch or Not to Dispatch?</a>.
757
 
</p>
758
 
 
759
 
<h3 id="progressive-enhancement-and-seo">Progressive Enhancement and SEO</h3>
760
 
 
761
 
<p>
762
 
In general, HTML5 URLs that can be rendered by the server when necessary provide the best support for both progressive enhancement (by rendering initial pageviews more quickly, even in browsers with JavaScript disabled) and for search engine optimization (by allowing you to use real, non hash-based URLs that can be crawled by search bots, which may not support JavaScript).
763
 
</p>
764
 
 
765
 
<p>
766
 
Being able to render the same application states both on the server and the client may require you to write duplicate code. However, if you use JavaScript on the server, and in particular if you use <a href="http://nodejs.org/">Node.js</a>, you may be able to share code.
767
 
</p>
768
 
 
769
 
<p>
770
 
Controller's routing style and route specification format are intentionally very similar to those of the <a href="http://expressjs.com/">Express.js</a> server-side framework. With a little care, you may be able to use the same route handler code on both the server and the client.
771
 
</p>
772
 
 
773
 
<h4 id="supporting-googles-ajax-crawling-scheme">Supporting Google's Ajax Crawling Scheme</h4>
774
 
 
775
 
<p>
776
 
One of the problems with the hash-based URLs Controller uses to support legacy browsers is that most search engines don't distinguish between a URL with a hash fragment and one without. This means that, to a search bot, the URLs <code>http:&#x2F;&#x2F;example.com&#x2F;</code> and <code>http:&#x2F;&#x2F;example.com&#x2F;#&#x2F;pie</code> might look the same, even though they might represent completely different content in your application.
777
 
</p>
778
 
 
779
 
<p>
780
 
Google's <a href="http://code.google.com/web/ajaxcrawling/">Ajax Crawling Scheme</a> specifies a way to make hash-based URLs crawlable by the GoogleBot if you're willing to implement some extra server-side logic.
781
 
</p>
782
 
 
783
 
<p>
784
 
To indicate to the GoogleBot that your hash URLs are crawlable, the hash must be prefixed by <code>#!</code> instead of the default <code>#</code>. You can make Controller do this automatically by setting the value of the static <code>Y.HistoryHash.hashPrefix</code> property before initializing any Controller instances:
785
 
</p>
786
 
 
787
 
<pre class="code prettyprint">Y.HistoryHash.hashPrefix = &#x27;!&#x27;;</pre>
788
 
 
789
 
 
790
 
<p>
791
 
Next, read Google's <a href="http://code.google.com/web/ajaxcrawling/docs/getting-started.html">getting started guide</a> for a description of how the Ajax crawling scheme works and the additional changes you'll need to make to your application. Most of the changes you'll need to make will have to happen in your server-side logic.
792
 
</p>
793
 
 
794
 
<p>
795
 
Don't skip the server-side changes! Without them, using the <code>#!</code> prefix won't do you any good, and may even hurt the search ranking of your pages.
796
 
</p>
797
 
 
798
 
<h2 id="known-limitations">Known Limitations</h2>
799
 
 
800
 
<ul>
801
 
  <li><p><strong>HTML5 history is not currently supported by any version of Internet Explorer.</strong> This includes Internet Explorer 9 and developer preview releases of IE10 as of the time these docs were written. Please feel free to let Microsoft know how you feel about this.</p></li>
802
 
 
803
 
  <li><p><strong>Android 2.x is forced to use hash-based history due to <a href="http://code.google.com/p/android/issues/detail?id=17471">a bug</a> in Android's HTML5 history implementation.</strong> This bug does not affect Android 3.0 and higher.</p></li>
804
 
 
805
 
  <li><p><strong>Hash-based URLs are case-insensitive in Internet Explorer 8 and 9</strong>. Most browsers follow the HTML5 spec and treat URLs&mdash;including hash-based URLs&mdash;as case-sensitive. IE8 and IE9 ignore case in hash-based URLs, so changing a hash-based URL from <code>&#x2F;foo</code> to <code>&#x2F;Foo</code> won't trigger a dispatch.</p></li>
806
 
 
807
 
  <li><p><strong>Internet Explorer 6 and 7 only retain the most recent hash-based URL from a previous pageview after navigating to another page and returning.</strong> However, history entries created within a single pageview will persist for the duration of that pageview, and bookmarked URLs will still work in all cases.</p></li>
808
 
 
809
 
  <li><p><strong>In Internet Explorer 6 and 7, the page titles displayed for history entries in the browser's history dropdown menu are not correct.</strong> Instead of showing the title of each page, it shows part of the URL of each page.</p></li>
810
 
</ul>
811
 
</div>
812
 
        </div>
813
 
 
814
 
        <div id="sidebar" class="yui3-u">
815
 
            
816
 
                <div id="toc" class="sidebox">
817
 
                    <div class="hd">
818
 
                        <h2 class="no-toc">Table of Contents</h2>
819
 
                    </div>
820
 
 
821
 
                    <div class="bd">
822
 
                        <ul class="toc">
823
 
<li>
824
 
<a href="#getting-started">Getting Started</a>
825
 
</li>
826
 
<li>
827
 
<a href="#url-based-routing-on-the-client">URL-based Routing on the Client?</a>
828
 
</li>
829
 
<li>
830
 
<a href="#sample-urls">Sample URLs</a>
831
 
</li>
832
 
<li>
833
 
<a href="#using-controller">Using Controller</a>
834
 
<ul class="toc">
835
 
<li>
836
 
<a href="#instantiating-controller">Instantiating Controller</a>
837
 
<ul class="toc">
838
 
<li>
839
 
<a href="#config-properties">Config Properties</a>
840
 
</li>
841
 
<li>
842
 
<a href="#read-only-properties">Read-Only Properties</a>
843
 
</li>
844
 
<li>
845
 
<a href="#setting-the-root-path">Setting the Root Path</a>
846
 
</li>
847
 
</ul>
848
 
</li>
849
 
<li>
850
 
<a href="#extending-ycontroller">Extending <code>Y.Controller</code></a>
851
 
</li>
852
 
<li>
853
 
<a href="#routing">Routing</a>
854
 
<ul class="toc">
855
 
<li>
856
 
<a href="#paths-placeholders-and-regexps">Paths, Placeholders, and RegExps</a>
857
 
</li>
858
 
<li>
859
 
<a href="#route-callbacks">Route Callbacks</a>
860
 
</li>
861
 
<li>
862
 
<a href="#chaining-routes">Chaining Routes</a>
863
 
</li>
864
 
</ul>
865
 
</li>
866
 
<li>
867
 
<a href="#updating-the-url">Updating the URL</a>
868
 
<ul class="toc">
869
 
<li>
870
 
<a href="#capturing-link-clicks">Capturing Link Clicks</a>
871
 
</li>
872
 
</ul>
873
 
</li>
874
 
<li>
875
 
<a href="#to-dispatch-or-not-to-dispatch">To Dispatch or Not to Dispatch?</a>
876
 
</li>
877
 
</ul>
878
 
</li>
879
 
<li>
880
 
<a href="#best-practices">Best Practices</a>
881
 
<ul class="toc">
882
 
<li>
883
 
<a href="#html5-urls-vs-hash-urls">HTML5 URLs vs. Hash URLs</a>
884
 
</li>
885
 
<li>
886
 
<a href="#cross-browser-url-compatibility">Cross-browser URL Compatibility</a>
887
 
</li>
888
 
<li>
889
 
<a href="#progressive-enhancement-and-seo">Progressive Enhancement and SEO</a>
890
 
<ul class="toc">
891
 
<li>
892
 
<a href="#supporting-googles-ajax-crawling-scheme">Supporting Google's Ajax Crawling Scheme</a>
893
 
</li>
894
 
</ul>
895
 
</li>
896
 
</ul>
897
 
</li>
898
 
<li>
899
 
<a href="#known-limitations">Known Limitations</a>
900
 
</li>
901
 
</ul>
902
 
                    </div>
903
 
                </div>
904
 
            
905
 
 
906
 
            
907
 
 
908
 
            
909
 
        </div>
910
 
    </div>
911
 
</div>
912
 
 
913
 
<script src="../assets/vendor/prettify/prettify-min.js"></script>
914
 
<script>prettyPrint();</script>
915
 
 
916
 
</body>
917
 
</html>