wiselinks-artirix 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. checksums.yaml +7 -0
  2. data/.document +5 -0
  3. data/.gitignore +51 -0
  4. data/.rspec +2 -0
  5. data/.travis.yml +4 -0
  6. data/Gemfile +7 -0
  7. data/Gemfile.lock +184 -0
  8. data/LICENSE +20 -0
  9. data/README.md +479 -0
  10. data/Rakefile +10 -0
  11. data/app/views/layouts/wiselinks.html.erb +1 -0
  12. data/compiler.jar +0 -0
  13. data/lib/assets/javascripts/_dom_parser.js.coffee +50 -0
  14. data/lib/assets/javascripts/_form.js.coffee +74 -0
  15. data/lib/assets/javascripts/_link.js.coffee +44 -0
  16. data/lib/assets/javascripts/_page.js.coffee +108 -0
  17. data/lib/assets/javascripts/_request_manager.js.coffee +132 -0
  18. data/lib/assets/javascripts/_response.js.coffee +101 -0
  19. data/lib/assets/javascripts/lib/native.history.js +3337 -0
  20. data/lib/assets/javascripts/wiselinks.js.coffee +37 -0
  21. data/lib/wiselinks-artirix.rb +1 -0
  22. data/lib/wiselinks.rb +27 -0
  23. data/lib/wiselinks/controller_methods.rb +67 -0
  24. data/lib/wiselinks/helpers.rb +9 -0
  25. data/lib/wiselinks/logger.rb +11 -0
  26. data/lib/wiselinks/rails.rb +22 -0
  27. data/lib/wiselinks/rendering.rb +37 -0
  28. data/lib/wiselinks/request.rb +29 -0
  29. data/lib/wiselinks/version.rb +12 -0
  30. data/spec/cases/helpers_spec.rb +8 -0
  31. data/spec/cases/request_spec.rb +113 -0
  32. data/spec/dummy/README.rdoc +261 -0
  33. data/spec/dummy/Rakefile +7 -0
  34. data/spec/dummy/app/assets/javascripts/application.js.coffee +1 -0
  35. data/spec/dummy/app/assets/stylesheets/application.css +13 -0
  36. data/spec/dummy/app/controllers/application_controller.rb +9 -0
  37. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  38. data/spec/dummy/app/mailers/.gitkeep +0 -0
  39. data/spec/dummy/app/models/.gitkeep +0 -0
  40. data/spec/dummy/app/views/application/index.html.erb +1 -0
  41. data/spec/dummy/app/views/application/no_slash.html.erb +0 -0
  42. data/spec/dummy/app/views/application/trailing_slash.html.erb +0 -0
  43. data/spec/dummy/app/views/layouts/application.html.erb +15 -0
  44. data/spec/dummy/config.ru +4 -0
  45. data/spec/dummy/config/application.rb +59 -0
  46. data/spec/dummy/config/boot.rb +10 -0
  47. data/spec/dummy/config/database.yml +7 -0
  48. data/spec/dummy/config/environment.rb +5 -0
  49. data/spec/dummy/config/environments/development.rb +37 -0
  50. data/spec/dummy/config/environments/production.rb +67 -0
  51. data/spec/dummy/config/environments/test.rb +40 -0
  52. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  53. data/spec/dummy/config/initializers/inflections.rb +15 -0
  54. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  55. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  56. data/spec/dummy/config/initializers/session_store.rb +8 -0
  57. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  58. data/spec/dummy/config/locales/en.yml +5 -0
  59. data/spec/dummy/config/routes.rb +7 -0
  60. data/spec/dummy/lib/assets/.gitkeep +0 -0
  61. data/spec/dummy/log/.gitkeep +0 -0
  62. data/spec/dummy/log/development.log +27917 -0
  63. data/spec/dummy/public/404.html +26 -0
  64. data/spec/dummy/public/422.html +26 -0
  65. data/spec/dummy/public/500.html +25 -0
  66. data/spec/dummy/public/assets/application-a98e47eb93026a340a766faf55214702.js +2830 -0
  67. data/spec/dummy/public/assets/application-a98e47eb93026a340a766faf55214702.js.gz +0 -0
  68. data/spec/dummy/public/assets/application-ecf5beebe0b79251c8be40f0443074f2.css +14 -0
  69. data/spec/dummy/public/assets/application-ecf5beebe0b79251c8be40f0443074f2.css.gz +0 -0
  70. data/spec/dummy/public/assets/application.css +14 -0
  71. data/spec/dummy/public/assets/application.css.gz +0 -0
  72. data/spec/dummy/public/assets/application.js +2830 -0
  73. data/spec/dummy/public/assets/application.js.gz +0 -0
  74. data/spec/dummy/public/assets/manifest.yml +5 -0
  75. data/spec/dummy/public/favicon.ico +0 -0
  76. data/spec/dummy/public/javascripts/wisepdf.js +1 -0
  77. data/spec/dummy/public/stylesheets/wisepdf.css +1 -0
  78. data/spec/dummy/script/rails +6 -0
  79. data/spec/dummy/tmp/cache/assets/C26/0A0/sprockets%2F52456508a38f02f4559064b24980c87a +0 -0
  80. data/spec/dummy/tmp/cache/assets/C5E/890/sprockets%2Ffb6525457b6873e6905e2d522548091f +0 -0
  81. data/spec/dummy/tmp/cache/assets/C80/150/sprockets%2F0d3881005b0646df783d5c24683d34f5 +0 -0
  82. data/spec/dummy/tmp/cache/assets/CD8/370/sprockets%2F357970feca3ac29060c1e3861e2c0953 +0 -0
  83. data/spec/dummy/tmp/cache/assets/CF3/250/sprockets%2F7edb1809ce839a3d290508f935c89f42 +0 -0
  84. data/spec/dummy/tmp/cache/assets/D09/C40/sprockets%2Fcf317b95ed0500b7277e950a9c0c82e0 +0 -0
  85. data/spec/dummy/tmp/cache/assets/D32/A10/sprockets%2F13fe41fee1fe35b49d145bcc06610705 +0 -0
  86. data/spec/dummy/tmp/cache/assets/D45/760/sprockets%2Fbc659a6e09b5025e8be539b0b68f71f2 +0 -0
  87. data/spec/dummy/tmp/cache/assets/D4E/D00/sprockets%2F1a6846f0a837ae2524e2f9ec89e6ef43 +0 -0
  88. data/spec/dummy/tmp/cache/assets/D55/090/sprockets%2F4a21bc343a4696b7603ea4cc25c3c3ba +0 -0
  89. data/spec/dummy/tmp/cache/assets/D5A/EA0/sprockets%2Fd771ace226fc8215a3572e0aa35bb0d6 +0 -0
  90. data/spec/dummy/tmp/cache/assets/DC2/EB0/sprockets%2F75b4716f479a2acfac51b258fba0c2bd +0 -0
  91. data/spec/dummy/tmp/cache/assets/DF1/B80/sprockets%2Fcd0ee4f742908cb7223a1e7be4a4ccbc +0 -0
  92. data/spec/dummy/tmp/cache/assets/E11/4E0/sprockets%2F86e145a39f85cceeaffdff91ebb61449 +0 -0
  93. data/spec/factories/requests.rb +30 -0
  94. data/spec/helper.rb +26 -0
  95. data/spec/requests/trailing_slash_spec.rb +17 -0
  96. data/wiselinks-artirix.gemspec +40 -0
  97. metadata +432 -0
@@ -0,0 +1,14 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the top of the
9
+ * compiled file, but it's generally better to create a new file per style scope.
10
+ *
11
+
12
+
13
+ */
14
+
@@ -0,0 +1,14 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the top of the
9
+ * compiled file, but it's generally better to create a new file per style scope.
10
+ *
11
+
12
+
13
+ */
14
+
@@ -0,0 +1,2830 @@
1
+ /**
2
+ * History.js Core
3
+ * @author Benjamin Arthur Lupton <contact@balupton.com>
4
+ * @copyright 2010-2011 Benjamin Arthur Lupton <contact@balupton.com>
5
+ * @license New BSD License <http://creativecommons.org/licenses/BSD/>
6
+ */
7
+
8
+
9
+ (function(window,undefined){
10
+ "use strict";
11
+
12
+ // ========================================================================
13
+ // Initialise
14
+
15
+ // Localise Globals
16
+ var
17
+ console = window.console||undefined, // Prevent a JSLint complain
18
+ document = window.document, // Make sure we are using the correct document
19
+ navigator = window.navigator, // Make sure we are using the correct navigator
20
+ sessionStorage = window.sessionStorage||false, // sessionStorage
21
+ setTimeout = window.setTimeout,
22
+ clearTimeout = window.clearTimeout,
23
+ setInterval = window.setInterval,
24
+ clearInterval = window.clearInterval,
25
+ JSON = window.JSON,
26
+ alert = window.alert,
27
+ History = window.History = window.History||{}, // Public History Object
28
+ history = window.history; // Old History Object
29
+
30
+ // MooTools Compatibility
31
+ JSON.stringify = JSON.stringify||JSON.encode;
32
+ JSON.parse = JSON.parse||JSON.decode;
33
+
34
+ // Check Existence
35
+ if ( typeof History.init !== 'undefined' ) {
36
+ throw new Error('History.js Core has already been loaded...');
37
+ }
38
+
39
+ // Initialise History
40
+ History.init = function(){
41
+ // Check Load Status of Adapter
42
+ if ( typeof History.Adapter === 'undefined' ) {
43
+ return false;
44
+ }
45
+
46
+ // Check Load Status of Core
47
+ if ( typeof History.initCore !== 'undefined' ) {
48
+ History.initCore();
49
+ }
50
+
51
+ // Check Load Status of HTML4 Support
52
+ if ( typeof History.initHtml4 !== 'undefined' ) {
53
+ History.initHtml4();
54
+ }
55
+
56
+ // Return true
57
+ return true;
58
+ };
59
+
60
+
61
+ // ========================================================================
62
+ // Initialise Core
63
+
64
+ // Initialise Core
65
+ History.initCore = function(){
66
+ // Initialise
67
+ if ( typeof History.initCore.initialized !== 'undefined' ) {
68
+ // Already Loaded
69
+ return false;
70
+ }
71
+ else {
72
+ History.initCore.initialized = true;
73
+ }
74
+
75
+
76
+ // ====================================================================
77
+ // Options
78
+
79
+ /**
80
+ * History.options
81
+ * Configurable options
82
+ */
83
+ History.options = History.options||{};
84
+
85
+ /**
86
+ * History.options.hashChangeInterval
87
+ * How long should the interval be before hashchange checks
88
+ */
89
+ History.options.hashChangeInterval = History.options.hashChangeInterval || 100;
90
+
91
+ /**
92
+ * History.options.safariPollInterval
93
+ * How long should the interval be before safari poll checks
94
+ */
95
+ History.options.safariPollInterval = History.options.safariPollInterval || 500;
96
+
97
+ /**
98
+ * History.options.doubleCheckInterval
99
+ * How long should the interval be before we perform a double check
100
+ */
101
+ History.options.doubleCheckInterval = History.options.doubleCheckInterval || 500;
102
+
103
+ /**
104
+ * History.options.storeInterval
105
+ * How long should we wait between store calls
106
+ */
107
+ History.options.storeInterval = History.options.storeInterval || 1000;
108
+
109
+ /**
110
+ * History.options.busyDelay
111
+ * How long should we wait between busy events
112
+ */
113
+ History.options.busyDelay = History.options.busyDelay || 250;
114
+
115
+ /**
116
+ * History.options.debug
117
+ * If true will enable debug messages to be logged
118
+ */
119
+ History.options.debug = History.options.debug || false;
120
+
121
+ /**
122
+ * History.options.initialTitle
123
+ * What is the title of the initial state
124
+ */
125
+ History.options.initialTitle = History.options.initialTitle || document.title;
126
+
127
+
128
+ // ====================================================================
129
+ // Interval record
130
+
131
+ /**
132
+ * History.intervalList
133
+ * List of intervals set, to be cleared when document is unloaded.
134
+ */
135
+ History.intervalList = [];
136
+
137
+ /**
138
+ * History.clearAllIntervals
139
+ * Clears all setInterval instances.
140
+ */
141
+ History.clearAllIntervals = function(){
142
+ var i, il = History.intervalList;
143
+ if (typeof il !== "undefined" && il !== null) {
144
+ for (i = 0; i < il.length; i++) {
145
+ clearInterval(il[i]);
146
+ }
147
+ History.intervalList = null;
148
+ }
149
+ };
150
+
151
+
152
+ // ====================================================================
153
+ // Debug
154
+
155
+ /**
156
+ * History.debug(message,...)
157
+ * Logs the passed arguments if debug enabled
158
+ */
159
+ History.debug = function(){
160
+ if ( (History.options.debug||false) ) {
161
+ History.log.apply(History,arguments);
162
+ }
163
+ };
164
+
165
+ /**
166
+ * History.log(message,...)
167
+ * Logs the passed arguments
168
+ */
169
+ History.log = function(){
170
+ // Prepare
171
+ var
172
+ consoleExists = !(typeof console === 'undefined' || typeof console.log === 'undefined' || typeof console.log.apply === 'undefined'),
173
+ textarea = document.getElementById('log'),
174
+ message,
175
+ i,n,
176
+ args,arg
177
+ ;
178
+
179
+ // Write to Console
180
+ if ( consoleExists ) {
181
+ args = Array.prototype.slice.call(arguments);
182
+ message = args.shift();
183
+ if ( typeof console.debug !== 'undefined' ) {
184
+ console.debug.apply(console,[message,args]);
185
+ }
186
+ else {
187
+ console.log.apply(console,[message,args]);
188
+ }
189
+ }
190
+ else {
191
+ message = ("\n"+arguments[0]+"\n");
192
+ }
193
+
194
+ // Write to log
195
+ for ( i=1,n=arguments.length; i<n; ++i ) {
196
+ arg = arguments[i];
197
+ if ( typeof arg === 'object' && typeof JSON !== 'undefined' ) {
198
+ try {
199
+ arg = JSON.stringify(arg);
200
+ }
201
+ catch ( Exception ) {
202
+ // Recursive Object
203
+ }
204
+ }
205
+ message += "\n"+arg+"\n";
206
+ }
207
+
208
+ // Textarea
209
+ if ( textarea ) {
210
+ textarea.value += message+"\n-----\n";
211
+ textarea.scrollTop = textarea.scrollHeight - textarea.clientHeight;
212
+ }
213
+ // No Textarea, No Console
214
+ else if ( !consoleExists ) {
215
+ alert(message);
216
+ }
217
+
218
+ // Return true
219
+ return true;
220
+ };
221
+
222
+
223
+ // ====================================================================
224
+ // Emulated Status
225
+
226
+ /**
227
+ * History.getInternetExplorerMajorVersion()
228
+ * Get's the major version of Internet Explorer
229
+ * @return {integer}
230
+ * @license Public Domain
231
+ * @author Benjamin Arthur Lupton <contact@balupton.com>
232
+ * @author James Padolsey <https://gist.github.com/527683>
233
+ */
234
+ History.getInternetExplorerMajorVersion = function(){
235
+ var result = History.getInternetExplorerMajorVersion.cached =
236
+ (typeof History.getInternetExplorerMajorVersion.cached !== 'undefined')
237
+ ? History.getInternetExplorerMajorVersion.cached
238
+ : (function(){
239
+ var v = 3,
240
+ div = document.createElement('div'),
241
+ all = div.getElementsByTagName('i');
242
+ while ( (div.innerHTML = '<!--[if gt IE ' + (++v) + ']><i></i><![endif]-->') && all[0] ) {}
243
+ return (v > 4) ? v : false;
244
+ })()
245
+ ;
246
+ return result;
247
+ };
248
+
249
+ /**
250
+ * History.isInternetExplorer()
251
+ * Are we using Internet Explorer?
252
+ * @return {boolean}
253
+ * @license Public Domain
254
+ * @author Benjamin Arthur Lupton <contact@balupton.com>
255
+ */
256
+ History.isInternetExplorer = function(){
257
+ var result =
258
+ History.isInternetExplorer.cached =
259
+ (typeof History.isInternetExplorer.cached !== 'undefined')
260
+ ? History.isInternetExplorer.cached
261
+ : Boolean(History.getInternetExplorerMajorVersion())
262
+ ;
263
+ return result;
264
+ };
265
+
266
+ /**
267
+ * History.emulated
268
+ * Which features require emulating?
269
+ */
270
+ History.emulated = {
271
+ pushState: !Boolean(
272
+ window.history && window.history.pushState && window.history.replaceState
273
+ && !(
274
+ (/ Mobile\/([1-7][a-z]|(8([abcde]|f(1[0-8]))))/i).test(navigator.userAgent) /* disable for versions of iOS before version 4.3 (8F190) */
275
+ || (/AppleWebKit\/5([0-2]|3[0-2])/i).test(navigator.userAgent) /* disable for the mercury iOS browser, or at least older versions of the webkit engine */
276
+ )
277
+ ),
278
+ hashChange: Boolean(
279
+ !(('onhashchange' in window) || ('onhashchange' in document))
280
+ ||
281
+ (History.isInternetExplorer() && History.getInternetExplorerMajorVersion() < 8)
282
+ )
283
+ };
284
+
285
+ /**
286
+ * History.enabled
287
+ * Is History enabled?
288
+ */
289
+ History.enabled = !History.emulated.pushState;
290
+
291
+ /**
292
+ * History.bugs
293
+ * Which bugs are present
294
+ */
295
+ History.bugs = {
296
+ /**
297
+ * Safari 5 and Safari iOS 4 fail to return to the correct state once a hash is replaced by a `replaceState` call
298
+ * https://bugs.webkit.org/show_bug.cgi?id=56249
299
+ */
300
+ setHash: Boolean(!History.emulated.pushState && navigator.vendor === 'Apple Computer, Inc.' && /AppleWebKit\/5([0-2]|3[0-3])/.test(navigator.userAgent)),
301
+
302
+ /**
303
+ * Safari 5 and Safari iOS 4 sometimes fail to apply the state change under busy conditions
304
+ * https://bugs.webkit.org/show_bug.cgi?id=42940
305
+ */
306
+ safariPoll: Boolean(!History.emulated.pushState && navigator.vendor === 'Apple Computer, Inc.' && /AppleWebKit\/5([0-2]|3[0-3])/.test(navigator.userAgent)),
307
+
308
+ /**
309
+ * MSIE 6 and 7 sometimes do not apply a hash even it was told to (requiring a second call to the apply function)
310
+ */
311
+ ieDoubleCheck: Boolean(History.isInternetExplorer() && History.getInternetExplorerMajorVersion() < 8),
312
+
313
+ /**
314
+ * MSIE 6 requires the entire hash to be encoded for the hashes to trigger the onHashChange event
315
+ */
316
+ hashEscape: Boolean(History.isInternetExplorer() && History.getInternetExplorerMajorVersion() < 7)
317
+ };
318
+
319
+ /**
320
+ * History.isEmptyObject(obj)
321
+ * Checks to see if the Object is Empty
322
+ * @param {Object} obj
323
+ * @return {boolean}
324
+ */
325
+ History.isEmptyObject = function(obj) {
326
+ for ( var name in obj ) {
327
+ return false;
328
+ }
329
+ return true;
330
+ };
331
+
332
+ /**
333
+ * History.cloneObject(obj)
334
+ * Clones a object and eliminate all references to the original contexts
335
+ * @param {Object} obj
336
+ * @return {Object}
337
+ */
338
+ History.cloneObject = function(obj) {
339
+ var hash,newObj;
340
+ if ( obj ) {
341
+ hash = JSON.stringify(obj);
342
+ newObj = JSON.parse(hash);
343
+ }
344
+ else {
345
+ newObj = {};
346
+ }
347
+ return newObj;
348
+ };
349
+
350
+
351
+ // ====================================================================
352
+ // URL Helpers
353
+
354
+ /**
355
+ * History.getRootUrl()
356
+ * Turns "http://mysite.com/dir/page.html?asd" into "http://mysite.com"
357
+ * @return {String} rootUrl
358
+ */
359
+ History.getRootUrl = function(){
360
+ // Create
361
+ var rootUrl = document.location.protocol+'//'+(document.location.hostname||document.location.host);
362
+ if ( document.location.port||false ) {
363
+ rootUrl += ':'+document.location.port;
364
+ }
365
+ rootUrl += '/';
366
+
367
+ // Return
368
+ return rootUrl;
369
+ };
370
+
371
+ /**
372
+ * History.getBaseHref()
373
+ * Fetches the `href` attribute of the `<base href="...">` element if it exists
374
+ * @return {String} baseHref
375
+ */
376
+ History.getBaseHref = function(){
377
+ // Create
378
+ var
379
+ baseElements = document.getElementsByTagName('base'),
380
+ baseElement = null,
381
+ baseHref = '';
382
+
383
+ // Test for Base Element
384
+ if ( baseElements.length === 1 ) {
385
+ // Prepare for Base Element
386
+ baseElement = baseElements[0];
387
+ baseHref = baseElement.href.replace(/[^\/]+$/,'');
388
+ }
389
+
390
+ // Adjust trailing slash
391
+ baseHref = baseHref.replace(/\/+$/,'');
392
+ if ( baseHref ) baseHref += '/';
393
+
394
+ // Return
395
+ return baseHref;
396
+ };
397
+
398
+ /**
399
+ * History.getBaseUrl()
400
+ * Fetches the baseHref or basePageUrl or rootUrl (whichever one exists first)
401
+ * @return {String} baseUrl
402
+ */
403
+ History.getBaseUrl = function(){
404
+ // Create
405
+ var baseUrl = History.getBaseHref()||History.getBasePageUrl()||History.getRootUrl();
406
+
407
+ // Return
408
+ return baseUrl;
409
+ };
410
+
411
+ /**
412
+ * History.getPageUrl()
413
+ * Fetches the URL of the current page
414
+ * @return {String} pageUrl
415
+ */
416
+ History.getPageUrl = function(){
417
+ // Fetch
418
+ var
419
+ State = History.getState(false,false),
420
+ stateUrl = (State||{}).url||document.location.href,
421
+ pageUrl;
422
+
423
+ // Create
424
+ pageUrl = stateUrl.replace(/\/+$/,'').replace(/[^\/]+$/,function(part,index,string){
425
+ return (/\!/).test(part) ? part : part+'/';
426
+ });
427
+
428
+ // Return
429
+ return pageUrl;
430
+ };
431
+
432
+ /**
433
+ * History.getBasePageUrl()
434
+ * Fetches the Url of the directory of the current page
435
+ * @return {String} basePageUrl
436
+ */
437
+ History.getBasePageUrl = function(){
438
+ // Create
439
+ var basePageUrl = document.location.href.replace(/[#\?].*/,'').replace(/[^\/]+$/,function(part,index,string){
440
+ return (/[^\/]$/).test(part) ? '' : part;
441
+ }).replace(/\/+$/,'')+'/';
442
+
443
+ // Return
444
+ return basePageUrl;
445
+ };
446
+
447
+ /**
448
+ * History.getFullUrl(url)
449
+ * Ensures that we have an absolute URL and not a relative URL
450
+ * @param {string} url
451
+ * @param {Boolean} allowBaseHref
452
+ * @return {string} fullUrl
453
+ */
454
+ History.getFullUrl = function(url,allowBaseHref){
455
+ // Prepare
456
+ var fullUrl = url, firstChar = url.substring(0,1);
457
+ allowBaseHref = (typeof allowBaseHref === 'undefined') ? true : allowBaseHref;
458
+
459
+ // Check
460
+ if ( /[a-z]+\:\/\//.test(url) ) {
461
+ // Full URL
462
+ }
463
+ else if ( firstChar === '/' ) {
464
+ // Root URL
465
+ fullUrl = History.getRootUrl()+url.replace(/^\/+/,'');
466
+ }
467
+ else if ( firstChar === '#' ) {
468
+ // Anchor URL
469
+ fullUrl = History.getPageUrl().replace(/#!*/,'')+url;
470
+ }
471
+ else if ( firstChar === '?' ) {
472
+ // Query URL
473
+ fullUrl = History.getPageUrl().replace(/[\?#]!*/,'')+url;
474
+ }
475
+ else {
476
+ // Relative URL
477
+ if ( allowBaseHref ) {
478
+ fullUrl = History.getBaseUrl()+url.replace(/^(\!\/)+/,'');
479
+ } else {
480
+ fullUrl = History.getBasePageUrl()+url.replace(/^(\!\/)+/,'');
481
+ }
482
+ // We have an if condition above as we do not want hashes
483
+ // which are relative to the baseHref in our URLs
484
+ // as if the baseHref changes, then all our bookmarks
485
+ // would now point to different locations
486
+ // whereas the basePageUrl will always stay the same
487
+ }
488
+
489
+ // Return
490
+ return fullUrl.replace(/\#$/,'');
491
+ };
492
+
493
+ /**
494
+ * History.getShortUrl(url)
495
+ * Ensures that we have a relative URL and not a absolute URL
496
+ * @param {string} url
497
+ * @return {string} url
498
+ */
499
+ History.getShortUrl = function(url){
500
+ // Prepare
501
+ var shortUrl = url, baseUrl = History.getBaseUrl(), rootUrl = History.getRootUrl();
502
+
503
+ // Trim baseUrl
504
+ if ( History.emulated.pushState ) {
505
+ // We are in a if statement as when pushState is not emulated
506
+ // The actual url these short urls are relative to can change
507
+ // So within the same session, we the url may end up somewhere different
508
+ shortUrl = shortUrl.replace(baseUrl,'');
509
+ }
510
+
511
+ // Trim rootUrl
512
+ shortUrl = shortUrl.replace(rootUrl,'/');
513
+
514
+ // Ensure we can still detect it as a state
515
+ // if ( History.isTraditionalAnchor(shortUrl) ) {
516
+ // shortUrl = './'+shortUrl;
517
+ // // shortUrl = '/'+shortUrl;
518
+ // }
519
+
520
+ shortUrl = '!/'+shortUrl;
521
+
522
+ // Clean It
523
+ shortUrl = shortUrl.replace(/^(\!\/)+/g,'!/').replace(/\#$/,'');
524
+
525
+ // Return
526
+ return shortUrl;
527
+ };
528
+
529
+
530
+ // ====================================================================
531
+ // State Storage
532
+
533
+ /**
534
+ * History.store
535
+ * The store for all session specific data
536
+ */
537
+ History.store = {};
538
+
539
+ /**
540
+ * History.idToState
541
+ * 1-1: State ID to State Object
542
+ */
543
+ History.idToState = History.idToState||{};
544
+
545
+ /**
546
+ * History.stateToId
547
+ * 1-1: State String to State ID
548
+ */
549
+ History.stateToId = History.stateToId||{};
550
+
551
+ /**
552
+ * History.urlToId
553
+ * 1-1: State URL to State ID
554
+ */
555
+ History.urlToId = History.urlToId||{};
556
+
557
+ /**
558
+ * History.storedStates
559
+ * Store the states in an array
560
+ */
561
+ History.storedStates = History.storedStates||[];
562
+
563
+ /**
564
+ * History.savedStates
565
+ * Saved the states in an array
566
+ */
567
+ History.savedStates = History.savedStates||[];
568
+
569
+ /**
570
+ * History.noramlizeStore()
571
+ * Noramlize the store by adding necessary values
572
+ */
573
+ History.normalizeStore = function(){
574
+ History.store.idToState = History.store.idToState||{};
575
+ History.store.urlToId = History.store.urlToId||{};
576
+ History.store.stateToId = History.store.stateToId||{};
577
+ };
578
+
579
+ /**
580
+ * History.getState()
581
+ * Get an object containing the data, title and url of the current state
582
+ * @param {Boolean} friendly
583
+ * @param {Boolean} create
584
+ * @return {Object} State
585
+ */
586
+ History.getState = function(friendly,create){
587
+ // Prepare
588
+ if ( typeof friendly === 'undefined' ) { friendly = true; }
589
+ if ( typeof create === 'undefined' ) { create = true; }
590
+
591
+ // Fetch
592
+ var State = History.getLastSavedState();
593
+
594
+ // Create
595
+ if ( !State && create ) {
596
+ State = History.createStateObject();
597
+ }
598
+
599
+ // Adjust
600
+ if ( friendly ) {
601
+ State = History.cloneObject(State);
602
+ State.url = State.cleanUrl||State.url;
603
+ }
604
+
605
+ // Return
606
+ return State;
607
+ };
608
+
609
+ /**
610
+ * History.getIdByState(State)
611
+ * Gets a ID for a State
612
+ * @param {State} newState
613
+ * @return {String} id
614
+ */
615
+ History.getIdByState = function(newState){
616
+
617
+ // Fetch ID
618
+ var id = History.extractId(newState.url),
619
+ str;
620
+
621
+ if ( !id ) {
622
+ // Find ID via State String
623
+ str = History.getStateString(newState);
624
+ if ( typeof History.stateToId[str] !== 'undefined' ) {
625
+ id = History.stateToId[str];
626
+ }
627
+ else if ( typeof History.store.stateToId[str] !== 'undefined' ) {
628
+ id = History.store.stateToId[str];
629
+ }
630
+ else {
631
+ // Generate a new ID
632
+ while ( true ) {
633
+ id = (new Date()).getTime() + String(Math.random()).replace(/\D/g,'');
634
+ if ( typeof History.idToState[id] === 'undefined' && typeof History.store.idToState[id] === 'undefined' ) {
635
+ break;
636
+ }
637
+ }
638
+
639
+ // Apply the new State to the ID
640
+ History.stateToId[str] = id;
641
+ History.idToState[id] = newState;
642
+ }
643
+ }
644
+
645
+ // Return ID
646
+ return id;
647
+ };
648
+
649
+ /**
650
+ * History.normalizeState(State)
651
+ * Expands a State Object
652
+ * @param {object} State
653
+ * @return {object}
654
+ */
655
+ History.normalizeState = function(oldState){
656
+ // Variables
657
+ var newState, dataNotEmpty;
658
+
659
+ // Prepare
660
+ if ( !oldState || (typeof oldState !== 'object') ) {
661
+ oldState = {};
662
+ }
663
+
664
+ // Check
665
+ if ( typeof oldState.normalized !== 'undefined' ) {
666
+ return oldState;
667
+ }
668
+
669
+ // Adjust
670
+ if ( !oldState.data || (typeof oldState.data !== 'object') ) {
671
+ oldState.data = {};
672
+ }
673
+
674
+ // ----------------------------------------------------------------
675
+
676
+ // Create
677
+ newState = {};
678
+ newState.normalized = true;
679
+ newState.title = oldState.title||'';
680
+ newState.url = History.getFullUrl(History.unescapeString(oldState.url||document.location.href));
681
+ newState.hash = History.getShortUrl(newState.url);
682
+ newState.data = History.cloneObject(oldState.data);
683
+
684
+ // Fetch ID
685
+ newState.id = History.getIdByState(newState);
686
+
687
+ // ----------------------------------------------------------------
688
+
689
+ // Clean the URL
690
+ newState.cleanUrl = newState.url.replace(/\??\&_suid.*/,'');
691
+ newState.url = newState.cleanUrl;
692
+
693
+ // Check to see if we have more than just a url
694
+ dataNotEmpty = !History.isEmptyObject(newState.data);
695
+
696
+ // Apply
697
+ if ( newState.title || dataNotEmpty ) {
698
+ // Add ID to Hash
699
+ newState.hash = History.getShortUrl(newState.url).replace(/\??\&_suid.*/,'');
700
+ if ( !/\?/.test(newState.hash) ) {
701
+ newState.hash += '?';
702
+ }
703
+ newState.hash += '&_suid='+newState.id;
704
+ }
705
+
706
+ // Create the Hashed URL
707
+ newState.hashedUrl = History.getFullUrl(newState.hash);
708
+
709
+ // ----------------------------------------------------------------
710
+
711
+ // Update the URL if we have a duplicate
712
+ if ( (History.emulated.pushState || History.bugs.safariPoll) && History.hasUrlDuplicate(newState) ) {
713
+ newState.url = newState.hashedUrl;
714
+ }
715
+
716
+ // ----------------------------------------------------------------
717
+
718
+ // Return
719
+ return newState;
720
+ };
721
+
722
+ /**
723
+ * History.createStateObject(data,title,url)
724
+ * Creates a object based on the data, title and url state params
725
+ * @param {object} data
726
+ * @param {string} title
727
+ * @param {string} url
728
+ * @return {object}
729
+ */
730
+ History.createStateObject = function(data,title,url){
731
+ // Hashify
732
+ var State = {
733
+ 'data': data,
734
+ 'title': title,
735
+ 'url': url
736
+ };
737
+
738
+ // Expand the State
739
+ State = History.normalizeState(State);
740
+
741
+ // Return object
742
+ return State;
743
+ };
744
+
745
+ /**
746
+ * History.getStateById(id)
747
+ * Get a state by it's UID
748
+ * @param {String} id
749
+ */
750
+ History.getStateById = function(id){
751
+ // Prepare
752
+ id = String(id);
753
+
754
+ // Retrieve
755
+ var State = History.idToState[id] || History.store.idToState[id] || undefined;
756
+
757
+ // Return State
758
+ return State;
759
+ };
760
+
761
+ /**
762
+ * Get a State's String
763
+ * @param {State} passedState
764
+ */
765
+ History.getStateString = function(passedState){
766
+ // Prepare
767
+ var State, cleanedState, str;
768
+
769
+ // Fetch
770
+ State = History.normalizeState(passedState);
771
+
772
+ // Clean
773
+ cleanedState = {
774
+ data: State.data,
775
+ title: passedState.title,
776
+ url: passedState.url
777
+ };
778
+
779
+ // Fetch
780
+ str = JSON.stringify(cleanedState);
781
+
782
+ // Return
783
+ return str;
784
+ };
785
+
786
+ /**
787
+ * Get a State's ID
788
+ * @param {State} passedState
789
+ * @return {String} id
790
+ */
791
+ History.getStateId = function(passedState){
792
+ // Prepare
793
+ var State, id;
794
+
795
+ // Fetch
796
+ State = History.normalizeState(passedState);
797
+
798
+ // Fetch
799
+ id = State.id;
800
+
801
+ // Return
802
+ return id;
803
+ };
804
+
805
+ /**
806
+ * History.getHashByState(State)
807
+ * Creates a Hash for the State Object
808
+ * @param {State} passedState
809
+ * @return {String} hash
810
+ */
811
+ History.getHashByState = function(passedState){
812
+ // Prepare
813
+ var State, hash;
814
+
815
+ // Fetch
816
+ State = History.normalizeState(passedState);
817
+
818
+ // Hash
819
+ hash = State.hash;
820
+
821
+ // Return
822
+ return hash;
823
+ };
824
+
825
+ /**
826
+ * History.extractId(url_or_hash)
827
+ * Get a State ID by it's URL or Hash
828
+ * @param {string} url_or_hash
829
+ * @return {string} id
830
+ */
831
+ History.extractId = function ( url_or_hash ) {
832
+ // Prepare
833
+ var id,parts,url;
834
+
835
+ // Extract
836
+ parts = /(.*)\&_suid=([0-9]+)$/.exec(url_or_hash);
837
+ url = parts ? (parts[1]||url_or_hash) : url_or_hash;
838
+ id = parts ? String(parts[2]||'') : '';
839
+
840
+ // Return
841
+ return id||false;
842
+ };
843
+
844
+ /**
845
+ * History.isTraditionalAnchor
846
+ * Checks to see if the url is a traditional anchor or not
847
+ * @param {String} url_or_hash
848
+ * @return {Boolean}
849
+ */
850
+ History.isTraditionalAnchor = function(url_or_hash){
851
+ // Check
852
+ var isTraditional = !(/[\/\?\.]/.test(url_or_hash));
853
+
854
+ // Return
855
+ return isTraditional;
856
+ };
857
+
858
+ /**
859
+ * History.extractState
860
+ * Get a State by it's URL or Hash
861
+ * @param {String} url_or_hash
862
+ * @return {State|null}
863
+ */
864
+ History.extractState = function(url_or_hash,create){
865
+ // Prepare
866
+ var State = null, id, url;
867
+ create = create||false;
868
+
869
+ // Fetch SUID
870
+ id = History.extractId(url_or_hash);
871
+ if ( id ) {
872
+ State = History.getStateById(id);
873
+ }
874
+
875
+ // Fetch SUID returned no State
876
+ if ( !State ) {
877
+ // Fetch URL
878
+ url = History.getFullUrl(url_or_hash);
879
+
880
+ // Check URL
881
+ id = History.getIdByUrl(url)||false;
882
+ if ( id ) {
883
+ State = History.getStateById(id);
884
+ }
885
+
886
+ // Create State
887
+ if ( !State && create && !History.isTraditionalAnchor(url_or_hash) ) {
888
+ State = History.createStateObject(null,null,url);
889
+ }
890
+ }
891
+
892
+ // Return
893
+ return State;
894
+ };
895
+
896
+ /**
897
+ * History.getIdByUrl()
898
+ * Get a State ID by a State URL
899
+ */
900
+ History.getIdByUrl = function(url){
901
+ // Fetch
902
+ var id = History.urlToId[url] || History.store.urlToId[url] || undefined;
903
+
904
+ // Return
905
+ return id;
906
+ };
907
+
908
+ /**
909
+ * History.getLastSavedState()
910
+ * Get an object containing the data, title and url of the current state
911
+ * @return {Object} State
912
+ */
913
+ History.getLastSavedState = function(){
914
+ return History.savedStates[History.savedStates.length-1]||undefined;
915
+ };
916
+
917
+ /**
918
+ * History.getLastStoredState()
919
+ * Get an object containing the data, title and url of the current state
920
+ * @return {Object} State
921
+ */
922
+ History.getLastStoredState = function(){
923
+ return History.storedStates[History.storedStates.length-1]||undefined;
924
+ };
925
+
926
+ /**
927
+ * History.hasUrlDuplicate
928
+ * Checks if a Url will have a url conflict
929
+ * @param {Object} newState
930
+ * @return {Boolean} hasDuplicate
931
+ */
932
+ History.hasUrlDuplicate = function(newState) {
933
+ // Prepare
934
+ var hasDuplicate = false,
935
+ oldState;
936
+
937
+ // Fetch
938
+ oldState = History.extractState(newState.url);
939
+
940
+ // Check
941
+ hasDuplicate = oldState && oldState.id !== newState.id;
942
+
943
+ // Return
944
+ return hasDuplicate;
945
+ };
946
+
947
+ /**
948
+ * History.storeState
949
+ * Store a State
950
+ * @param {Object} newState
951
+ * @return {Object} newState
952
+ */
953
+ History.storeState = function(newState){
954
+ // Store the State
955
+ History.urlToId[newState.url] = newState.id;
956
+
957
+ // Push the State
958
+ History.storedStates.push(History.cloneObject(newState));
959
+
960
+ // Return newState
961
+ return newState;
962
+ };
963
+
964
+ /**
965
+ * History.isLastSavedState(newState)
966
+ * Tests to see if the state is the last state
967
+ * @param {Object} newState
968
+ * @return {boolean} isLast
969
+ */
970
+ History.isLastSavedState = function(newState){
971
+ // Prepare
972
+ var isLast = false,
973
+ newId, oldState, oldId;
974
+
975
+ // Check
976
+ if ( History.savedStates.length ) {
977
+ newId = newState.id;
978
+ oldState = History.getLastSavedState();
979
+ oldId = oldState.id;
980
+
981
+ // Check
982
+ isLast = (newId === oldId);
983
+ }
984
+
985
+ // Return
986
+ return isLast;
987
+ };
988
+
989
+ /**
990
+ * History.saveState
991
+ * Push a State
992
+ * @param {Object} newState
993
+ * @return {boolean} changed
994
+ */
995
+ History.saveState = function(newState){
996
+ // Check Hash
997
+ if ( History.isLastSavedState(newState) ) {
998
+ return false;
999
+ }
1000
+
1001
+ // Push the State
1002
+ History.savedStates.push(History.cloneObject(newState));
1003
+
1004
+ // Return true
1005
+ return true;
1006
+ };
1007
+
1008
+ /**
1009
+ * History.getStateByIndex()
1010
+ * Gets a state by the index
1011
+ * @param {integer} index
1012
+ * @return {Object}
1013
+ */
1014
+ History.getStateByIndex = function(index){
1015
+ // Prepare
1016
+ var State = null;
1017
+
1018
+ // Handle
1019
+ if ( typeof index === 'undefined' ) {
1020
+ // Get the last inserted
1021
+ State = History.savedStates[History.savedStates.length-1];
1022
+ }
1023
+ else if ( index < 0 ) {
1024
+ // Get from the end
1025
+ State = History.savedStates[History.savedStates.length+index];
1026
+ }
1027
+ else {
1028
+ // Get from the beginning
1029
+ State = History.savedStates[index];
1030
+ }
1031
+
1032
+ // Return State
1033
+ return State;
1034
+ };
1035
+
1036
+
1037
+ // ====================================================================
1038
+ // Hash Helpers
1039
+
1040
+ /**
1041
+ * History.getHash()
1042
+ * Gets the current document hash
1043
+ * @return {string}
1044
+ */
1045
+ History.getHash = function(){
1046
+ var hash = History.unescapeHash(document.location.hash);
1047
+ return hash;
1048
+ };
1049
+
1050
+ /**
1051
+ * History.unescapeString()
1052
+ * Unescape a string
1053
+ * @param {String} str
1054
+ * @return {string}
1055
+ */
1056
+ History.unescapeString = function(str){
1057
+ // Prepare
1058
+ var result = str,
1059
+ tmp;
1060
+
1061
+ // Unescape hash
1062
+ while ( true ) {
1063
+ tmp = window.decodeURI(result);
1064
+ if ( tmp === result ) {
1065
+ break;
1066
+ }
1067
+ result = tmp;
1068
+ }
1069
+
1070
+ // Return result
1071
+ return result;
1072
+ };
1073
+
1074
+ /**
1075
+ * History.unescapeHash()
1076
+ * normalize and Unescape a Hash
1077
+ * @param {String} hash
1078
+ * @return {string}
1079
+ */
1080
+ History.unescapeHash = function(hash){
1081
+ // Prepare
1082
+ var result = History.normalizeHash(hash);
1083
+
1084
+ // Unescape hash
1085
+ result = History.unescapeString(result);
1086
+
1087
+ // Return result
1088
+ return result;
1089
+ };
1090
+
1091
+ /**
1092
+ * History.normalizeHash()
1093
+ * normalize a hash across browsers
1094
+ * @return {string}
1095
+ */
1096
+ History.normalizeHash = function(hash){
1097
+ // Prepare
1098
+ var result = hash.replace(/[^#]*#/,'').replace(/#!*/, '');
1099
+
1100
+ // Return result
1101
+ return result;
1102
+ };
1103
+
1104
+ /**
1105
+ * History.setHash(hash)
1106
+ * Sets the document hash
1107
+ * @param {string} hash
1108
+ * @return {History}
1109
+ */
1110
+ History.setHash = function(hash,queue){
1111
+ // Prepare
1112
+ var adjustedHash, State, pageUrl;
1113
+
1114
+ // Handle Queueing
1115
+ if ( queue !== false && History.busy() ) {
1116
+ // Wait + Push to Queue
1117
+ //History.debug('History.setHash: we must wait', arguments);
1118
+ History.pushQueue({
1119
+ scope: History,
1120
+ callback: History.setHash,
1121
+ args: arguments,
1122
+ queue: queue
1123
+ });
1124
+ return false;
1125
+ }
1126
+
1127
+ // Log
1128
+ //History.debug('History.setHash: called',hash);
1129
+
1130
+ // Prepare
1131
+ adjustedHash = History.escapeHash(hash);
1132
+
1133
+ // Make Busy + Continue
1134
+ History.busy(true);
1135
+
1136
+ // Check if hash is a state
1137
+ State = History.extractState(hash,true);
1138
+ if ( State && !History.emulated.pushState ) {
1139
+ // Hash is a state so skip the setHash
1140
+ //History.debug('History.setHash: Hash is a state so skipping the hash set with a direct pushState call',arguments);
1141
+
1142
+ // PushState
1143
+ History.pushState(State.data,State.title,State.url,false);
1144
+ }
1145
+ else if ( document.location.hash !== adjustedHash ) {
1146
+ // Hash is a proper hash, so apply it
1147
+
1148
+ // Handle browser bugs
1149
+ if ( History.bugs.setHash ) {
1150
+ // Fix Safari Bug https://bugs.webkit.org/show_bug.cgi?id=56249
1151
+
1152
+ // Fetch the base page
1153
+ pageUrl = History.getPageUrl();
1154
+
1155
+ // Safari hash apply
1156
+ History.pushState(null,null,pageUrl+'#'+adjustedHash,false);
1157
+ }
1158
+ else {
1159
+ // Normal hash apply
1160
+ document.location.hash = adjustedHash;
1161
+ }
1162
+ }
1163
+
1164
+ // Chain
1165
+ return History;
1166
+ };
1167
+
1168
+ /**
1169
+ * History.escape()
1170
+ * normalize and Escape a Hash
1171
+ * @return {string}
1172
+ */
1173
+ History.escapeHash = function(hash){
1174
+ // Prepare
1175
+ var result = History.normalizeHash(hash);
1176
+
1177
+ // Escape hash
1178
+ result = window.encodeURI(result);
1179
+
1180
+ // IE6 Escape Bug
1181
+ if ( !History.bugs.hashEscape ) {
1182
+ // Restore common parts
1183
+ result = result
1184
+ .replace(/\%21/g,'!')
1185
+ .replace(/\%26/g,'&')
1186
+ .replace(/\%3D/g,'=')
1187
+ .replace(/\%3F/g,'?');
1188
+ }
1189
+
1190
+ // Return result
1191
+ return result;
1192
+ };
1193
+
1194
+ /**
1195
+ * History.getHashByUrl(url)
1196
+ * Extracts the Hash from a URL
1197
+ * @param {string} url
1198
+ * @return {string} url
1199
+ */
1200
+ History.getHashByUrl = function(url){
1201
+ // Extract the hash
1202
+ var hash = String(url)
1203
+ .replace(/([^#]*)#?([^#]*)#?(.*)/, '$2')
1204
+ ;
1205
+
1206
+ // Unescape hash
1207
+ hash = History.unescapeHash(hash);
1208
+
1209
+ // Return hash
1210
+ return hash;
1211
+ };
1212
+
1213
+ /**
1214
+ * History.setTitle(title)
1215
+ * Applies the title to the document
1216
+ * @param {State} newState
1217
+ * @return {Boolean}
1218
+ */
1219
+ History.setTitle = function(newState){
1220
+ // Prepare
1221
+ var title = newState.title,
1222
+ firstState;
1223
+
1224
+ // Initial
1225
+ if ( !title ) {
1226
+ firstState = History.getStateByIndex(0);
1227
+ if ( firstState && firstState.url === newState.url ) {
1228
+ title = firstState.title||History.options.initialTitle;
1229
+ }
1230
+ }
1231
+
1232
+ // Apply
1233
+ try {
1234
+ document.getElementsByTagName('title')[0].innerHTML = title.replace('<','&lt;').replace('>','&gt;').replace(' & ',' &amp; ');
1235
+ }
1236
+ catch ( Exception ) { }
1237
+ document.title = title;
1238
+
1239
+ // Chain
1240
+ return History;
1241
+ };
1242
+
1243
+
1244
+ // ====================================================================
1245
+ // Queueing
1246
+
1247
+ /**
1248
+ * History.queues
1249
+ * The list of queues to use
1250
+ * First In, First Out
1251
+ */
1252
+ History.queues = [];
1253
+
1254
+ /**
1255
+ * History.busy(value)
1256
+ * @param {boolean} value [optional]
1257
+ * @return {boolean} busy
1258
+ */
1259
+ History.busy = function(value){
1260
+ // Apply
1261
+ if ( typeof value !== 'undefined' ) {
1262
+ //History.debug('History.busy: changing ['+(History.busy.flag||false)+'] to ['+(value||false)+']', History.queues.length);
1263
+ History.busy.flag = value;
1264
+ }
1265
+ // Default
1266
+ else if ( typeof History.busy.flag === 'undefined' ) {
1267
+ History.busy.flag = false;
1268
+ }
1269
+
1270
+ // Queue
1271
+ if ( !History.busy.flag ) {
1272
+ // Execute the next item in the queue
1273
+ clearTimeout(History.busy.timeout);
1274
+ var fireNext = function(){
1275
+ var i, queue, item;
1276
+ if ( History.busy.flag ) return;
1277
+ for ( i=History.queues.length-1; i >= 0; --i ) {
1278
+ queue = History.queues[i];
1279
+ if ( queue.length === 0 ) continue;
1280
+ item = queue.shift();
1281
+ History.fireQueueItem(item);
1282
+ History.busy.timeout = setTimeout(fireNext,History.options.busyDelay);
1283
+ }
1284
+ };
1285
+ History.busy.timeout = setTimeout(fireNext,History.options.busyDelay);
1286
+ }
1287
+
1288
+ // Return
1289
+ return History.busy.flag;
1290
+ };
1291
+
1292
+ /**
1293
+ * History.busy.flag
1294
+ */
1295
+ History.busy.flag = false;
1296
+
1297
+ /**
1298
+ * History.fireQueueItem(item)
1299
+ * Fire a Queue Item
1300
+ * @param {Object} item
1301
+ * @return {Mixed} result
1302
+ */
1303
+ History.fireQueueItem = function(item){
1304
+ return item.callback.apply(item.scope||History,item.args||[]);
1305
+ };
1306
+
1307
+ /**
1308
+ * History.pushQueue(callback,args)
1309
+ * Add an item to the queue
1310
+ * @param {Object} item [scope,callback,args,queue]
1311
+ */
1312
+ History.pushQueue = function(item){
1313
+ // Prepare the queue
1314
+ History.queues[item.queue||0] = History.queues[item.queue||0]||[];
1315
+
1316
+ // Add to the queue
1317
+ History.queues[item.queue||0].push(item);
1318
+
1319
+ // Chain
1320
+ return History;
1321
+ };
1322
+
1323
+ /**
1324
+ * History.queue (item,queue), (func,queue), (func), (item)
1325
+ * Either firs the item now if not busy, or adds it to the queue
1326
+ */
1327
+ History.queue = function(item,queue){
1328
+ // Prepare
1329
+ if ( typeof item === 'function' ) {
1330
+ item = {
1331
+ callback: item
1332
+ };
1333
+ }
1334
+ if ( typeof queue !== 'undefined' ) {
1335
+ item.queue = queue;
1336
+ }
1337
+
1338
+ // Handle
1339
+ if ( History.busy() ) {
1340
+ History.pushQueue(item);
1341
+ } else {
1342
+ History.fireQueueItem(item);
1343
+ }
1344
+
1345
+ // Chain
1346
+ return History;
1347
+ };
1348
+
1349
+ /**
1350
+ * History.clearQueue()
1351
+ * Clears the Queue
1352
+ */
1353
+ History.clearQueue = function(){
1354
+ History.busy.flag = false;
1355
+ History.queues = [];
1356
+ return History;
1357
+ };
1358
+
1359
+
1360
+ // ====================================================================
1361
+ // IE Bug Fix
1362
+
1363
+ /**
1364
+ * History.stateChanged
1365
+ * States whether or not the state has changed since the last double check was initialised
1366
+ */
1367
+ History.stateChanged = false;
1368
+
1369
+ /**
1370
+ * History.doubleChecker
1371
+ * Contains the timeout used for the double checks
1372
+ */
1373
+ History.doubleChecker = false;
1374
+
1375
+ /**
1376
+ * History.doubleCheckComplete()
1377
+ * Complete a double check
1378
+ * @return {History}
1379
+ */
1380
+ History.doubleCheckComplete = function(){
1381
+ // Update
1382
+ History.stateChanged = true;
1383
+
1384
+ // Clear
1385
+ History.doubleCheckClear();
1386
+
1387
+ // Chain
1388
+ return History;
1389
+ };
1390
+
1391
+ /**
1392
+ * History.doubleCheckClear()
1393
+ * Clear a double check
1394
+ * @return {History}
1395
+ */
1396
+ History.doubleCheckClear = function(){
1397
+ // Clear
1398
+ if ( History.doubleChecker ) {
1399
+ clearTimeout(History.doubleChecker);
1400
+ History.doubleChecker = false;
1401
+ }
1402
+
1403
+ // Chain
1404
+ return History;
1405
+ };
1406
+
1407
+ /**
1408
+ * History.doubleCheck()
1409
+ * Create a double check
1410
+ * @return {History}
1411
+ */
1412
+ History.doubleCheck = function(tryAgain){
1413
+ // Reset
1414
+ History.stateChanged = false;
1415
+ History.doubleCheckClear();
1416
+
1417
+ // Fix IE6,IE7 bug where calling history.back or history.forward does not actually change the hash (whereas doing it manually does)
1418
+ // Fix Safari 5 bug where sometimes the state does not change: https://bugs.webkit.org/show_bug.cgi?id=42940
1419
+ if ( History.bugs.ieDoubleCheck ) {
1420
+ // Apply Check
1421
+ History.doubleChecker = setTimeout(
1422
+ function(){
1423
+ History.doubleCheckClear();
1424
+ if ( !History.stateChanged ) {
1425
+ //History.debug('History.doubleCheck: State has not yet changed, trying again', arguments);
1426
+ // Re-Attempt
1427
+ tryAgain();
1428
+ }
1429
+ return true;
1430
+ },
1431
+ History.options.doubleCheckInterval
1432
+ );
1433
+ }
1434
+
1435
+ // Chain
1436
+ return History;
1437
+ };
1438
+
1439
+
1440
+ // ====================================================================
1441
+ // Safari Bug Fix
1442
+
1443
+ /**
1444
+ * History.safariStatePoll()
1445
+ * Poll the current state
1446
+ * @return {History}
1447
+ */
1448
+ History.safariStatePoll = function(){
1449
+ // Poll the URL
1450
+
1451
+ // Get the Last State which has the new URL
1452
+ var
1453
+ urlState = History.extractState(document.location.href),
1454
+ newState;
1455
+
1456
+ // Check for a difference
1457
+ if ( !History.isLastSavedState(urlState) ) {
1458
+ newState = urlState;
1459
+ }
1460
+ else {
1461
+ return;
1462
+ }
1463
+
1464
+ // Check if we have a state with that url
1465
+ // If not create it
1466
+ if ( !newState ) {
1467
+ //History.debug('History.safariStatePoll: new');
1468
+ newState = History.createStateObject();
1469
+ }
1470
+
1471
+ // Apply the New State
1472
+ //History.debug('History.safariStatePoll: trigger');
1473
+ History.Adapter.trigger(window,'popstate');
1474
+
1475
+ // Chain
1476
+ return History;
1477
+ };
1478
+
1479
+
1480
+ // ====================================================================
1481
+ // State Aliases
1482
+
1483
+ /**
1484
+ * History.back(queue)
1485
+ * Send the browser history back one item
1486
+ * @param {Integer} queue [optional]
1487
+ */
1488
+ History.back = function(queue){
1489
+ //History.debug('History.back: called', arguments);
1490
+
1491
+ // Handle Queueing
1492
+ if ( queue !== false && History.busy() ) {
1493
+ // Wait + Push to Queue
1494
+ //History.debug('History.back: we must wait', arguments);
1495
+ History.pushQueue({
1496
+ scope: History,
1497
+ callback: History.back,
1498
+ args: arguments,
1499
+ queue: queue
1500
+ });
1501
+ return false;
1502
+ }
1503
+
1504
+ // Make Busy + Continue
1505
+ History.busy(true);
1506
+
1507
+ // Fix certain browser bugs that prevent the state from changing
1508
+ History.doubleCheck(function(){
1509
+ History.back(false);
1510
+ });
1511
+
1512
+ // Go back
1513
+ history.go(-1);
1514
+
1515
+ // End back closure
1516
+ return true;
1517
+ };
1518
+
1519
+ /**
1520
+ * History.forward(queue)
1521
+ * Send the browser history forward one item
1522
+ * @param {Integer} queue [optional]
1523
+ */
1524
+ History.forward = function(queue){
1525
+ //History.debug('History.forward: called', arguments);
1526
+
1527
+ // Handle Queueing
1528
+ if ( queue !== false && History.busy() ) {
1529
+ // Wait + Push to Queue
1530
+ //History.debug('History.forward: we must wait', arguments);
1531
+ History.pushQueue({
1532
+ scope: History,
1533
+ callback: History.forward,
1534
+ args: arguments,
1535
+ queue: queue
1536
+ });
1537
+ return false;
1538
+ }
1539
+
1540
+ // Make Busy + Continue
1541
+ History.busy(true);
1542
+
1543
+ // Fix certain browser bugs that prevent the state from changing
1544
+ History.doubleCheck(function(){
1545
+ History.forward(false);
1546
+ });
1547
+
1548
+ // Go forward
1549
+ history.go(1);
1550
+
1551
+ // End forward closure
1552
+ return true;
1553
+ };
1554
+
1555
+ /**
1556
+ * History.go(index,queue)
1557
+ * Send the browser history back or forward index times
1558
+ * @param {Integer} queue [optional]
1559
+ */
1560
+ History.go = function(index,queue){
1561
+ //History.debug('History.go: called', arguments);
1562
+
1563
+ // Prepare
1564
+ var i;
1565
+
1566
+ // Handle
1567
+ if ( index > 0 ) {
1568
+ // Forward
1569
+ for ( i=1; i<=index; ++i ) {
1570
+ History.forward(queue);
1571
+ }
1572
+ }
1573
+ else if ( index < 0 ) {
1574
+ // Backward
1575
+ for ( i=-1; i>=index; --i ) {
1576
+ History.back(queue);
1577
+ }
1578
+ }
1579
+ else {
1580
+ throw new Error('History.go: History.go requires a positive or negative integer passed.');
1581
+ }
1582
+
1583
+ // Chain
1584
+ return History;
1585
+ };
1586
+
1587
+
1588
+ // ====================================================================
1589
+ // HTML5 State Support
1590
+
1591
+ // Non-Native pushState Implementation
1592
+ if ( History.emulated.pushState ) {
1593
+ /*
1594
+ * Provide Skeleton for HTML4 Browsers
1595
+ */
1596
+
1597
+ // Prepare
1598
+ var emptyFunction = function(){};
1599
+ History.pushState = History.pushState||emptyFunction;
1600
+ History.replaceState = History.replaceState||emptyFunction;
1601
+ } // History.emulated.pushState
1602
+
1603
+ // Native pushState Implementation
1604
+ else {
1605
+ /*
1606
+ * Use native HTML5 History API Implementation
1607
+ */
1608
+
1609
+ /**
1610
+ * History.onPopState(event,extra)
1611
+ * Refresh the Current State
1612
+ */
1613
+ History.onPopState = function(event,extra){
1614
+ // Prepare
1615
+ var stateId = false, newState = false, currentHash, currentState;
1616
+
1617
+ // Reset the double check
1618
+ History.doubleCheckComplete();
1619
+
1620
+ // Check for a Hash, and handle apporiatly
1621
+ currentHash = History.getHash();
1622
+ if ( currentHash ) {
1623
+ // Expand Hash
1624
+ currentState = History.extractState(currentHash||document.location.href,true);
1625
+ if ( currentState ) {
1626
+ // We were able to parse it, it must be a State!
1627
+ // Let's forward to replaceState
1628
+ //History.debug('History.onPopState: state anchor', currentHash, currentState);
1629
+ History.replaceState(currentState.data, currentState.title, currentState.url, false);
1630
+ }
1631
+ else {
1632
+ // Traditional Anchor
1633
+ //History.debug('History.onPopState: traditional anchor', currentHash);
1634
+ History.Adapter.trigger(window,'anchorchange');
1635
+ History.busy(false);
1636
+ }
1637
+
1638
+ // We don't care for hashes
1639
+ History.expectedStateId = false;
1640
+ return false;
1641
+ }
1642
+
1643
+ // Ensure
1644
+ stateId = History.Adapter.extractEventData('state',event,extra) || false;
1645
+
1646
+ // Fetch State
1647
+ if ( stateId ) {
1648
+ // Vanilla: Back/forward button was used
1649
+ newState = History.getStateById(stateId);
1650
+ }
1651
+ else if ( History.expectedStateId ) {
1652
+ // Vanilla: A new state was pushed, and popstate was called manually
1653
+ newState = History.getStateById(History.expectedStateId);
1654
+ }
1655
+ else {
1656
+ // Initial State
1657
+ newState = History.extractState(document.location.href);
1658
+ }
1659
+
1660
+ // The State did not exist in our store
1661
+ if ( !newState ) {
1662
+ // Regenerate the State
1663
+ newState = History.createStateObject(null,null,document.location.href);
1664
+ }
1665
+
1666
+ // Clean
1667
+ History.expectedStateId = false;
1668
+
1669
+ // Check if we are the same state
1670
+ if ( History.isLastSavedState(newState) ) {
1671
+ // There has been no change (just the page's hash has finally propagated)
1672
+ //History.debug('History.onPopState: no change', newState, History.savedStates);
1673
+ History.busy(false);
1674
+ return false;
1675
+ }
1676
+
1677
+ // Store the State
1678
+ History.storeState(newState);
1679
+ History.saveState(newState);
1680
+
1681
+ // Force update of the title
1682
+ History.setTitle(newState);
1683
+
1684
+ // Fire Our Event
1685
+ History.Adapter.trigger(window,'statechange');
1686
+ History.busy(false);
1687
+
1688
+ // Return true
1689
+ return true;
1690
+ };
1691
+ History.Adapter.bind(window,'popstate',History.onPopState);
1692
+
1693
+ /**
1694
+ * History.pushState(data,title,url)
1695
+ * Add a new State to the history object, become it, and trigger onpopstate
1696
+ * We have to trigger for HTML4 compatibility
1697
+ * @param {object} data
1698
+ * @param {string} title
1699
+ * @param {string} url
1700
+ * @return {true}
1701
+ */
1702
+ History.pushState = function(data,title,url,queue){
1703
+ //History.debug('History.pushState: called', arguments);
1704
+
1705
+ // Check the State
1706
+ if ( History.getHashByUrl(url) && History.emulated.pushState ) {
1707
+ throw new Error('History.js does not support states with fragement-identifiers (hashes/anchors).');
1708
+ }
1709
+
1710
+ // Handle Queueing
1711
+ if ( queue !== false && History.busy() ) {
1712
+ // Wait + Push to Queue
1713
+ //History.debug('History.pushState: we must wait', arguments);
1714
+ History.pushQueue({
1715
+ scope: History,
1716
+ callback: History.pushState,
1717
+ args: arguments,
1718
+ queue: queue
1719
+ });
1720
+ return false;
1721
+ }
1722
+
1723
+ // Make Busy + Continue
1724
+ History.busy(true);
1725
+
1726
+ // Create the newState
1727
+ var newState = History.createStateObject(data,title,url);
1728
+
1729
+ // Check it
1730
+ if ( History.isLastSavedState(newState) ) {
1731
+ // Won't be a change
1732
+ History.busy(false);
1733
+ }
1734
+ else {
1735
+ // Store the newState
1736
+ History.storeState(newState);
1737
+ History.expectedStateId = newState.id;
1738
+
1739
+ // Push the newState
1740
+ history.pushState(newState.id,newState.title,newState.url);
1741
+
1742
+ // Fire HTML5 Event
1743
+ History.Adapter.trigger(window,'popstate');
1744
+ }
1745
+
1746
+ // End pushState closure
1747
+ return true;
1748
+ };
1749
+
1750
+ /**
1751
+ * History.replaceState(data,title,url)
1752
+ * Replace the State and trigger onpopstate
1753
+ * We have to trigger for HTML4 compatibility
1754
+ * @param {object} data
1755
+ * @param {string} title
1756
+ * @param {string} url
1757
+ * @return {true}
1758
+ */
1759
+ History.replaceState = function(data,title,url,queue){
1760
+ //History.debug('History.replaceState: called', arguments);
1761
+
1762
+ // Check the State
1763
+ if ( History.getHashByUrl(url) && History.emulated.pushState ) {
1764
+ throw new Error('History.js does not support states with fragement-identifiers (hashes/anchors).');
1765
+ }
1766
+
1767
+ // Handle Queueing
1768
+ if ( queue !== false && History.busy() ) {
1769
+ // Wait + Push to Queue
1770
+ //History.debug('History.replaceState: we must wait', arguments);
1771
+ History.pushQueue({
1772
+ scope: History,
1773
+ callback: History.replaceState,
1774
+ args: arguments,
1775
+ queue: queue
1776
+ });
1777
+ return false;
1778
+ }
1779
+
1780
+ // Make Busy + Continue
1781
+ History.busy(true);
1782
+
1783
+ // Create the newState
1784
+ var newState = History.createStateObject(data,title,url);
1785
+
1786
+ // Check it
1787
+ if ( History.isLastSavedState(newState) ) {
1788
+ // Won't be a change
1789
+ History.busy(false);
1790
+ }
1791
+ else {
1792
+ // Store the newState
1793
+ History.storeState(newState);
1794
+ History.expectedStateId = newState.id;
1795
+
1796
+ // Push the newState
1797
+ history.replaceState(newState.id,newState.title,newState.url);
1798
+
1799
+ // Fire HTML5 Event
1800
+ History.Adapter.trigger(window,'popstate');
1801
+ }
1802
+
1803
+ // End replaceState closure
1804
+ return true;
1805
+ };
1806
+
1807
+ } // !History.emulated.pushState
1808
+
1809
+
1810
+ // ====================================================================
1811
+ // Initialise
1812
+
1813
+ /**
1814
+ * Load the Store
1815
+ */
1816
+ if ( sessionStorage ) {
1817
+ // Fetch
1818
+ try {
1819
+ History.store = JSON.parse(sessionStorage.getItem('History.store'))||{};
1820
+ }
1821
+ catch ( err ) {
1822
+ History.store = {};
1823
+ }
1824
+
1825
+ // Normalize
1826
+ History.normalizeStore();
1827
+ }
1828
+ else {
1829
+ // Default Load
1830
+ History.store = {};
1831
+ History.normalizeStore();
1832
+ }
1833
+
1834
+ /**
1835
+ * Clear Intervals on exit to prevent memory leaks
1836
+ */
1837
+ History.Adapter.bind(window,"beforeunload",History.clearAllIntervals);
1838
+ History.Adapter.bind(window,"unload",History.clearAllIntervals);
1839
+
1840
+ /**
1841
+ * Create the initial State
1842
+ */
1843
+ History.saveState(History.storeState(History.extractState(document.location.href,true)));
1844
+
1845
+ /**
1846
+ * Bind for Saving Store
1847
+ */
1848
+ if ( sessionStorage ) {
1849
+ // When the page is closed
1850
+ History.onUnload = function(){
1851
+ // Prepare
1852
+ var currentStore, item;
1853
+
1854
+ // Fetch
1855
+ try {
1856
+ currentStore = JSON.parse(sessionStorage.getItem('History.store'))||{};
1857
+ }
1858
+ catch ( err ) {
1859
+ currentStore = {};
1860
+ }
1861
+
1862
+ // Ensure
1863
+ currentStore.idToState = currentStore.idToState || {};
1864
+ currentStore.urlToId = currentStore.urlToId || {};
1865
+ currentStore.stateToId = currentStore.stateToId || {};
1866
+
1867
+ // Sync
1868
+ for ( item in History.idToState ) {
1869
+ if ( !History.idToState.hasOwnProperty(item) ) {
1870
+ continue;
1871
+ }
1872
+ currentStore.idToState[item] = History.idToState[item];
1873
+ }
1874
+ for ( item in History.urlToId ) {
1875
+ if ( !History.urlToId.hasOwnProperty(item) ) {
1876
+ continue;
1877
+ }
1878
+ currentStore.urlToId[item] = History.urlToId[item];
1879
+ }
1880
+ for ( item in History.stateToId ) {
1881
+ if ( !History.stateToId.hasOwnProperty(item) ) {
1882
+ continue;
1883
+ }
1884
+ currentStore.stateToId[item] = History.stateToId[item];
1885
+ }
1886
+
1887
+ // Update
1888
+ History.store = currentStore;
1889
+ History.normalizeStore();
1890
+
1891
+ // Store
1892
+ sessionStorage.setItem('History.store',JSON.stringify(currentStore));
1893
+ };
1894
+
1895
+ // For Internet Explorer
1896
+ History.intervalList.push(setInterval(History.onUnload,History.options.storeInterval));
1897
+
1898
+ // For Other Browsers
1899
+ History.Adapter.bind(window,'beforeunload',History.onUnload);
1900
+ History.Adapter.bind(window,'unload',History.onUnload);
1901
+
1902
+ // Both are enabled for consistency
1903
+ }
1904
+
1905
+ // Non-Native pushState Implementation
1906
+ if ( !History.emulated.pushState ) {
1907
+ // Be aware, the following is only for native pushState implementations
1908
+ // If you are wanting to include something for all browsers
1909
+ // Then include it above this if block
1910
+
1911
+ /**
1912
+ * Setup Safari Fix
1913
+ */
1914
+ if ( History.bugs.safariPoll ) {
1915
+ History.intervalList.push(setInterval(History.safariStatePoll, History.options.safariPollInterval));
1916
+ }
1917
+
1918
+ /**
1919
+ * Ensure Cross Browser Compatibility
1920
+ */
1921
+ if ( navigator.vendor === 'Apple Computer, Inc.' || (navigator.appCodeName||'') === 'Mozilla' ) {
1922
+ /**
1923
+ * Fix Safari HashChange Issue
1924
+ */
1925
+
1926
+ // Setup Alias
1927
+ History.Adapter.bind(window,'hashchange',function(){
1928
+ History.Adapter.trigger(window,'popstate');
1929
+ });
1930
+
1931
+ // Initialise Alias
1932
+ if ( History.getHash() ) {
1933
+ History.Adapter.onDomLoad(function(){
1934
+ History.Adapter.trigger(window,'hashchange');
1935
+ });
1936
+ }
1937
+ }
1938
+
1939
+ } // !History.emulated.pushState
1940
+
1941
+
1942
+ }; // History.initCore
1943
+
1944
+ // Try and Initialise History
1945
+ History.init();
1946
+
1947
+ })(window);
1948
+ /**
1949
+ * History.js HTML4 Support
1950
+ * Depends on the HTML5 Support
1951
+ * @author Benjamin Arthur Lupton <contact@balupton.com>
1952
+ * @copyright 2010-2011 Benjamin Arthur Lupton <contact@balupton.com>
1953
+ * @license New BSD License <http://creativecommons.org/licenses/BSD/>
1954
+ */
1955
+
1956
+
1957
+ (function(window,undefined){
1958
+ "use strict";
1959
+
1960
+ // ========================================================================
1961
+ // Initialise
1962
+
1963
+ // Localise Globals
1964
+ var
1965
+ document = window.document, // Make sure we are using the correct document
1966
+ setTimeout = window.setTimeout||setTimeout,
1967
+ clearTimeout = window.clearTimeout||clearTimeout,
1968
+ setInterval = window.setInterval||setInterval,
1969
+ History = window.History = window.History||{}; // Public History Object
1970
+
1971
+ // Check Existence
1972
+ if ( typeof History.initHtml4 !== 'undefined' ) {
1973
+ throw new Error('History.js HTML4 Support has already been loaded...');
1974
+ }
1975
+
1976
+
1977
+ // ========================================================================
1978
+ // Initialise HTML4 Support
1979
+
1980
+ // Initialise HTML4 Support
1981
+ History.initHtml4 = function(){
1982
+ // Initialise
1983
+ if ( typeof History.initHtml4.initialized !== 'undefined' ) {
1984
+ // Already Loaded
1985
+ return false;
1986
+ }
1987
+ else {
1988
+ History.initHtml4.initialized = true;
1989
+ }
1990
+
1991
+
1992
+ // ====================================================================
1993
+ // Properties
1994
+
1995
+ /**
1996
+ * History.enabled
1997
+ * Is History enabled?
1998
+ */
1999
+ History.enabled = true;
2000
+
2001
+
2002
+ // ====================================================================
2003
+ // Hash Storage
2004
+
2005
+ /**
2006
+ * History.savedHashes
2007
+ * Store the hashes in an array
2008
+ */
2009
+ History.savedHashes = [];
2010
+
2011
+ /**
2012
+ * History.isLastHash(newHash)
2013
+ * Checks if the hash is the last hash
2014
+ * @param {string} newHash
2015
+ * @return {boolean} true
2016
+ */
2017
+ History.isLastHash = function(newHash){
2018
+ // Prepare
2019
+ var oldHash = History.getHashByIndex(),
2020
+ isLast;
2021
+
2022
+ // Check
2023
+ isLast = newHash === oldHash;
2024
+
2025
+ // Return isLast
2026
+ return isLast;
2027
+ };
2028
+
2029
+ /**
2030
+ * History.saveHash(newHash)
2031
+ * Push a Hash
2032
+ * @param {string} newHash
2033
+ * @return {boolean} true
2034
+ */
2035
+ History.saveHash = function(newHash){
2036
+ // Check Hash
2037
+ if ( History.isLastHash(newHash) ) {
2038
+ return false;
2039
+ }
2040
+
2041
+ // Push the Hash
2042
+ History.savedHashes.push(newHash);
2043
+
2044
+ // Return true
2045
+ return true;
2046
+ };
2047
+
2048
+ /**
2049
+ * History.getHashByIndex()
2050
+ * Gets a hash by the index
2051
+ * @param {integer} index
2052
+ * @return {string}
2053
+ */
2054
+ History.getHashByIndex = function(index){
2055
+ // Prepare
2056
+ var hash = null;
2057
+
2058
+ // Handle
2059
+ if ( typeof index === 'undefined' ) {
2060
+ // Get the last inserted
2061
+ hash = History.savedHashes[History.savedHashes.length-1];
2062
+ }
2063
+ else if ( index < 0 ) {
2064
+ // Get from the end
2065
+ hash = History.savedHashes[History.savedHashes.length+index];
2066
+ }
2067
+ else {
2068
+ // Get from the beginning
2069
+ hash = History.savedHashes[index];
2070
+ }
2071
+
2072
+ // Return hash
2073
+ return hash;
2074
+ };
2075
+
2076
+
2077
+ // ====================================================================
2078
+ // Discarded States
2079
+
2080
+ /**
2081
+ * History.discardedHashes
2082
+ * A hashed array of discarded hashes
2083
+ */
2084
+ History.discardedHashes = {};
2085
+
2086
+ /**
2087
+ * History.discardedStates
2088
+ * A hashed array of discarded states
2089
+ */
2090
+ History.discardedStates = {};
2091
+
2092
+ /**
2093
+ * History.discardState(State)
2094
+ * Discards the state by ignoring it through History
2095
+ * @param {object} State
2096
+ * @return {true}
2097
+ */
2098
+ History.discardState = function(discardedState,forwardState,backState){
2099
+ //History.debug('History.discardState', arguments);
2100
+ // Prepare
2101
+ var discardedStateHash = History.getHashByState(discardedState),
2102
+ discardObject;
2103
+
2104
+ // Create Discard Object
2105
+ discardObject = {
2106
+ 'discardedState': discardedState,
2107
+ 'backState': backState,
2108
+ 'forwardState': forwardState
2109
+ };
2110
+
2111
+ // Add to DiscardedStates
2112
+ History.discardedStates[discardedStateHash] = discardObject;
2113
+
2114
+ // Return true
2115
+ return true;
2116
+ };
2117
+
2118
+ /**
2119
+ * History.discardHash(hash)
2120
+ * Discards the hash by ignoring it through History
2121
+ * @param {string} hash
2122
+ * @return {true}
2123
+ */
2124
+ History.discardHash = function(discardedHash,forwardState,backState){
2125
+ //History.debug('History.discardState', arguments);
2126
+ // Create Discard Object
2127
+ var discardObject = {
2128
+ 'discardedHash': discardedHash,
2129
+ 'backState': backState,
2130
+ 'forwardState': forwardState
2131
+ };
2132
+
2133
+ // Add to discardedHash
2134
+ History.discardedHashes[discardedHash] = discardObject;
2135
+
2136
+ // Return true
2137
+ return true;
2138
+ };
2139
+
2140
+ /**
2141
+ * History.discardState(State)
2142
+ * Checks to see if the state is discarded
2143
+ * @param {object} State
2144
+ * @return {bool}
2145
+ */
2146
+ History.discardedState = function(State){
2147
+ // Prepare
2148
+ var StateHash = History.getHashByState(State),
2149
+ discarded;
2150
+
2151
+ // Check
2152
+ discarded = History.discardedStates[StateHash]||false;
2153
+
2154
+ // Return true
2155
+ return discarded;
2156
+ };
2157
+
2158
+ /**
2159
+ * History.discardedHash(hash)
2160
+ * Checks to see if the state is discarded
2161
+ * @param {string} State
2162
+ * @return {bool}
2163
+ */
2164
+ History.discardedHash = function(hash){
2165
+ // Check
2166
+ var discarded = History.discardedHashes[hash]||false;
2167
+
2168
+ // Return true
2169
+ return discarded;
2170
+ };
2171
+
2172
+ /**
2173
+ * History.recycleState(State)
2174
+ * Allows a discarded state to be used again
2175
+ * @param {object} data
2176
+ * @param {string} title
2177
+ * @param {string} url
2178
+ * @return {true}
2179
+ */
2180
+ History.recycleState = function(State){
2181
+ //History.debug('History.recycleState', arguments);
2182
+ // Prepare
2183
+ var StateHash = History.getHashByState(State);
2184
+
2185
+ // Remove from DiscardedStates
2186
+ if ( History.discardedState(State) ) {
2187
+ delete History.discardedStates[StateHash];
2188
+ }
2189
+
2190
+ // Return true
2191
+ return true;
2192
+ };
2193
+
2194
+
2195
+ // ====================================================================
2196
+ // HTML4 HashChange Support
2197
+
2198
+ if ( History.emulated.hashChange ) {
2199
+ /*
2200
+ * We must emulate the HTML4 HashChange Support by manually checking for hash changes
2201
+ */
2202
+
2203
+ /**
2204
+ * History.hashChangeInit()
2205
+ * Init the HashChange Emulation
2206
+ */
2207
+ History.hashChangeInit = function(){
2208
+ // Define our Checker Function
2209
+ History.checkerFunction = null;
2210
+
2211
+ // Define some variables that will help in our checker function
2212
+ var lastDocumentHash = '',
2213
+ iframeId, iframe,
2214
+ lastIframeHash, checkerRunning;
2215
+
2216
+ // Handle depending on the browser
2217
+ if ( History.isInternetExplorer() ) {
2218
+ // IE6 and IE7
2219
+ // We need to use an iframe to emulate the back and forward buttons
2220
+
2221
+ // Create iFrame
2222
+ iframeId = 'historyjs-iframe';
2223
+ iframe = document.createElement('iframe');
2224
+
2225
+ // Adjust iFarme
2226
+ iframe.setAttribute('id', iframeId);
2227
+ iframe.style.display = 'none';
2228
+
2229
+ // Append iFrame
2230
+ document.body.appendChild(iframe);
2231
+
2232
+ // Create initial history entry
2233
+ iframe.contentWindow.document.open();
2234
+ iframe.contentWindow.document.close();
2235
+
2236
+ // Define some variables that will help in our checker function
2237
+ lastIframeHash = '';
2238
+ checkerRunning = false;
2239
+
2240
+ // Define the checker function
2241
+ History.checkerFunction = function(){
2242
+ // Check Running
2243
+ if ( checkerRunning ) {
2244
+ return false;
2245
+ }
2246
+
2247
+ // Update Running
2248
+ checkerRunning = true;
2249
+
2250
+ // Fetch
2251
+ var documentHash = History.getHash()||'',
2252
+ iframeHash = History.unescapeHash(iframe.contentWindow.document.location.hash)||'';
2253
+
2254
+ // The Document Hash has changed (application caused)
2255
+ if ( documentHash !== lastDocumentHash ) {
2256
+ // Equalise
2257
+ lastDocumentHash = documentHash;
2258
+
2259
+ // Create a history entry in the iframe
2260
+ if ( iframeHash !== documentHash ) {
2261
+ //History.debug('hashchange.checker: iframe hash change', 'documentHash (new):', documentHash, 'iframeHash (old):', iframeHash);
2262
+
2263
+ // Equalise
2264
+ lastIframeHash = iframeHash = documentHash;
2265
+
2266
+ // Create History Entry
2267
+ iframe.contentWindow.document.open();
2268
+ iframe.contentWindow.document.close();
2269
+
2270
+ // Update the iframe's hash
2271
+ iframe.contentWindow.document.location.hash = History.escapeHash(documentHash);
2272
+ }
2273
+
2274
+ // Trigger Hashchange Event
2275
+ History.Adapter.trigger(window,'hashchange');
2276
+ }
2277
+
2278
+ // The iFrame Hash has changed (back button caused)
2279
+ else if ( iframeHash !== lastIframeHash ) {
2280
+ //History.debug('hashchange.checker: iframe hash out of sync', 'iframeHash (new):', iframeHash, 'documentHash (old):', documentHash);
2281
+
2282
+ // Equalise
2283
+ lastIframeHash = iframeHash;
2284
+
2285
+ // Update the Hash
2286
+ History.setHash(iframeHash,false);
2287
+ }
2288
+
2289
+ // Reset Running
2290
+ checkerRunning = false;
2291
+
2292
+ // Return true
2293
+ return true;
2294
+ };
2295
+ }
2296
+ else {
2297
+ // We are not IE
2298
+ // Firefox 1 or 2, Opera
2299
+
2300
+ // Define the checker function
2301
+ History.checkerFunction = function(){
2302
+ // Prepare
2303
+ var documentHash = History.getHash();
2304
+
2305
+ // The Document Hash has changed (application caused)
2306
+ if ( documentHash !== lastDocumentHash ) {
2307
+ // Equalise
2308
+ lastDocumentHash = documentHash;
2309
+
2310
+ // Trigger Hashchange Event
2311
+ History.Adapter.trigger(window,'hashchange');
2312
+ }
2313
+
2314
+ // Return true
2315
+ return true;
2316
+ };
2317
+ }
2318
+
2319
+ // Apply the checker function
2320
+ History.intervalList.push(setInterval(History.checkerFunction, History.options.hashChangeInterval));
2321
+
2322
+ // Done
2323
+ return true;
2324
+ }; // History.hashChangeInit
2325
+
2326
+ // Bind hashChangeInit
2327
+ History.Adapter.onDomLoad(History.hashChangeInit);
2328
+
2329
+ } // History.emulated.hashChange
2330
+
2331
+
2332
+ // ====================================================================
2333
+ // HTML5 State Support
2334
+
2335
+ // Non-Native pushState Implementation
2336
+ if ( History.emulated.pushState ) {
2337
+ /*
2338
+ * We must emulate the HTML5 State Management by using HTML4 HashChange
2339
+ */
2340
+
2341
+ /**
2342
+ * History.onHashChange(event)
2343
+ * Trigger HTML5's window.onpopstate via HTML4 HashChange Support
2344
+ */
2345
+ History.onHashChange = function(event){
2346
+ //History.debug('History.onHashChange', arguments);
2347
+
2348
+ // Prepare
2349
+ var currentUrl = ((event && event.newURL) || document.location.href),
2350
+ currentHash = History.getHashByUrl(currentUrl),
2351
+ currentState = null,
2352
+ currentStateHash = null,
2353
+ currentStateHashExits = null,
2354
+ discardObject;
2355
+
2356
+ // Check if we are the same state
2357
+ if ( History.isLastHash(currentHash) ) {
2358
+ // There has been no change (just the page's hash has finally propagated)
2359
+ //History.debug('History.onHashChange: no change');
2360
+ History.busy(false);
2361
+ return false;
2362
+ }
2363
+
2364
+ // Reset the double check
2365
+ History.doubleCheckComplete();
2366
+
2367
+ // Store our location for use in detecting back/forward direction
2368
+ History.saveHash(currentHash);
2369
+
2370
+ // Expand Hash
2371
+ if ( currentHash && History.isTraditionalAnchor(currentHash) ) {
2372
+ //History.debug('History.onHashChange: traditional anchor', currentHash);
2373
+ // Traditional Anchor Hash
2374
+ History.Adapter.trigger(window,'anchorchange');
2375
+ History.busy(false);
2376
+ return false;
2377
+ }
2378
+
2379
+ // Create State
2380
+ currentState = History.extractState(History.getFullUrl(currentHash||document.location.href,false),true);
2381
+
2382
+ // Check if we are the same state
2383
+ if ( History.isLastSavedState(currentState) ) {
2384
+ //History.debug('History.onHashChange: no change');
2385
+ // There has been no change (just the page's hash has finally propagated)
2386
+ History.busy(false);
2387
+ return false;
2388
+ }
2389
+
2390
+ // Create the state Hash
2391
+ currentStateHash = History.getHashByState(currentState);
2392
+
2393
+ // Check if we are DiscardedState
2394
+ discardObject = History.discardedState(currentState);
2395
+ if ( discardObject ) {
2396
+ // Ignore this state as it has been discarded and go back to the state before it
2397
+ if ( History.getHashByIndex(-2) === History.getHashByState(discardObject.forwardState) ) {
2398
+ // We are going backwards
2399
+ //History.debug('History.onHashChange: go backwards');
2400
+ History.back(false);
2401
+ } else {
2402
+ // We are going forwards
2403
+ //History.debug('History.onHashChange: go forwards');
2404
+ History.forward(false);
2405
+ }
2406
+ return false;
2407
+ }
2408
+
2409
+ // Push the new HTML5 State
2410
+ //History.debug('History.onHashChange: success hashchange');
2411
+ History.pushState(currentState.data,currentState.title,currentState.url,false);
2412
+
2413
+ // End onHashChange closure
2414
+ return true;
2415
+ };
2416
+ History.Adapter.bind(window,'hashchange',History.onHashChange);
2417
+
2418
+ /**
2419
+ * History.pushState(data,title,url)
2420
+ * Add a new State to the history object, become it, and trigger onpopstate
2421
+ * We have to trigger for HTML4 compatibility
2422
+ * @param {object} data
2423
+ * @param {string} title
2424
+ * @param {string} url
2425
+ * @return {true}
2426
+ */
2427
+ History.pushState = function(data,title,url,queue){
2428
+ //History.debug('History.pushState: called', arguments);
2429
+
2430
+ // Check the State
2431
+ if ( History.getHashByUrl(url) ) {
2432
+ throw new Error('History.js does not support states with fragement-identifiers (hashes/anchors).');
2433
+ }
2434
+
2435
+ // Handle Queueing
2436
+ if ( queue !== false && History.busy() ) {
2437
+ // Wait + Push to Queue
2438
+ //History.debug('History.pushState: we must wait', arguments);
2439
+ History.pushQueue({
2440
+ scope: History,
2441
+ callback: History.pushState,
2442
+ args: arguments,
2443
+ queue: queue
2444
+ });
2445
+ return false;
2446
+ }
2447
+
2448
+ // Make Busy
2449
+ History.busy(true);
2450
+
2451
+ // Fetch the State Object
2452
+ var newState = History.createStateObject(data,title,url),
2453
+ newStateHash = History.getHashByState(newState),
2454
+ oldState = History.getState(false),
2455
+ oldStateHash = History.getHashByState(oldState),
2456
+ html4Hash = History.getHash();
2457
+
2458
+ // Store the newState
2459
+ History.storeState(newState);
2460
+ History.expectedStateId = newState.id;
2461
+
2462
+ // Recycle the State
2463
+ History.recycleState(newState);
2464
+
2465
+ // Force update of the title
2466
+ // History.setTitle(newState);
2467
+
2468
+ // Check if we are the same State
2469
+ if ( newStateHash === oldStateHash ) {
2470
+ //History.debug('History.pushState: no change', newStateHash);
2471
+ History.busy(false);
2472
+ return false;
2473
+ }
2474
+
2475
+ // Update HTML4 Hash
2476
+ if ( newStateHash !== html4Hash && newStateHash !== History.getShortUrl(document.location.href) ) {
2477
+ //History.debug('History.pushState: update hash', newStateHash, html4Hash);
2478
+ History.setHash(newStateHash,false);
2479
+ return false;
2480
+ }
2481
+
2482
+ // Update HTML5 State
2483
+ History.saveState(newState);
2484
+
2485
+ // Fire HTML5 Event
2486
+ //History.debug('History.pushState: trigger popstate');
2487
+ History.Adapter.trigger(window,'statechange');
2488
+ History.busy(false);
2489
+
2490
+ // End pushState closure
2491
+ return true;
2492
+ };
2493
+
2494
+ /**
2495
+ * History.replaceState(data,title,url)
2496
+ * Replace the State and trigger onpopstate
2497
+ * We have to trigger for HTML4 compatibility
2498
+ * @param {object} data
2499
+ * @param {string} title
2500
+ * @param {string} url
2501
+ * @return {true}
2502
+ */
2503
+ History.replaceState = function(data,title,url,queue){
2504
+ //History.debug('History.replaceState: called', arguments);
2505
+
2506
+ // Check the State
2507
+ if ( History.getHashByUrl(url) ) {
2508
+ throw new Error('History.js does not support states with fragement-identifiers (hashes/anchors).');
2509
+ }
2510
+
2511
+ // Handle Queueing
2512
+ if ( queue !== false && History.busy() ) {
2513
+ // Wait + Push to Queue
2514
+ //History.debug('History.replaceState: we must wait', arguments);
2515
+ History.pushQueue({
2516
+ scope: History,
2517
+ callback: History.replaceState,
2518
+ args: arguments,
2519
+ queue: queue
2520
+ });
2521
+ return false;
2522
+ }
2523
+
2524
+ // Make Busy
2525
+ History.busy(true);
2526
+
2527
+ // Fetch the State Objects
2528
+ var newState = History.createStateObject(data,title,url),
2529
+ oldState = History.getState(false),
2530
+ previousState = History.getStateByIndex(-2);
2531
+
2532
+ // Discard Old State
2533
+ History.discardState(oldState,newState,previousState);
2534
+
2535
+ // Alias to PushState
2536
+ History.pushState(newState.data,newState.title,newState.url,false);
2537
+
2538
+ // End replaceState closure
2539
+ return true;
2540
+ };
2541
+
2542
+ } // History.emulated.pushState
2543
+
2544
+
2545
+
2546
+ // ====================================================================
2547
+ // Initialise
2548
+
2549
+ // Non-Native pushState Implementation
2550
+ if ( History.emulated.pushState ) {
2551
+ /**
2552
+ * Ensure initial state is handled correctly
2553
+ */
2554
+ if ( History.getHash() && !History.emulated.hashChange ) {
2555
+ History.Adapter.onDomLoad(function(){
2556
+ History.Adapter.trigger(window,'hashchange');
2557
+ });
2558
+ }
2559
+
2560
+ } // History.emulated.pushState
2561
+
2562
+ }; // History.initHtml4
2563
+
2564
+ // Try and Initialise History
2565
+ if ( typeof History.init !== 'undefined' ) {
2566
+ History.init();
2567
+ }
2568
+
2569
+ })(window);
2570
+ /**
2571
+ * History.js jQuery Adapter
2572
+ * @author Benjamin Arthur Lupton <contact@balupton.com>
2573
+ * @copyright 2010-2011 Benjamin Arthur Lupton <contact@balupton.com>
2574
+ * @license New BSD License <http://creativecommons.org/licenses/BSD/>
2575
+ */
2576
+
2577
+ // Closure
2578
+ (function(window,undefined){
2579
+ "use strict";
2580
+
2581
+ // Localise Globals
2582
+ var
2583
+ History = window.History = window.History||{},
2584
+ jQuery = window.jQuery;
2585
+
2586
+ // Check Existence
2587
+ if ( typeof History.Adapter !== 'undefined' ) {
2588
+ throw new Error('History.js Adapter has already been loaded...');
2589
+ }
2590
+
2591
+ // Add the Adapter
2592
+ History.Adapter = {
2593
+ /**
2594
+ * History.Adapter.bind(el,event,callback)
2595
+ * @param {Element|string} el
2596
+ * @param {string} event - custom and standard events
2597
+ * @param {function} callback
2598
+ * @return {void}
2599
+ */
2600
+ bind: function(el,event,callback){
2601
+ jQuery(el).bind(event,callback);
2602
+ },
2603
+
2604
+ /**
2605
+ * History.Adapter.trigger(el,event)
2606
+ * @param {Element|string} el
2607
+ * @param {string} event - custom and standard events
2608
+ * @param {Object=} extra - a object of extra event data (optional)
2609
+ * @return {void}
2610
+ */
2611
+ trigger: function(el,event,extra){
2612
+ jQuery(el).trigger(event,extra);
2613
+ },
2614
+
2615
+ /**
2616
+ * History.Adapter.extractEventData(key,event,extra)
2617
+ * @param {string} key - key for the event data to extract
2618
+ * @param {string} event - custom and standard events
2619
+ * @param {Object=} extra - a object of extra event data (optional)
2620
+ * @return {mixed}
2621
+ */
2622
+ extractEventData: function(key,event,extra){
2623
+ // jQuery Native then jQuery Custom
2624
+ var result = (event && event.originalEvent && event.originalEvent[key]) || (extra && extra[key]) || undefined;
2625
+
2626
+ // Return
2627
+ return result;
2628
+ },
2629
+
2630
+ /**
2631
+ * History.Adapter.onDomLoad(callback)
2632
+ * @param {function} callback
2633
+ * @return {void}
2634
+ */
2635
+ onDomLoad: function(callback) {
2636
+ jQuery(callback);
2637
+ }
2638
+ };
2639
+
2640
+ // Try and Initialise History
2641
+ if ( typeof History.init !== 'undefined' ) {
2642
+ History.init();
2643
+ }
2644
+
2645
+ })(window);
2646
+ (function() {
2647
+ var Wiselinks;
2648
+
2649
+ String.prototype.ends_with = function(suffix) {
2650
+ return this.indexOf(suffix, this.length - suffix.length) !== -1;
2651
+ };
2652
+
2653
+ Wiselinks = (function() {
2654
+
2655
+ function Wiselinks($target, options) {
2656
+ var self;
2657
+ this.$target = $target != null ? $target : $('body');
2658
+ this.options = options != null ? options : {};
2659
+ if (window.jQuery == null) {
2660
+ throw "Load jQuery to use Wiselinks";
2661
+ }
2662
+ self = this;
2663
+ this.options = jQuery.extend(self._defaults(), this.options);
2664
+ if (self.enabled()) {
2665
+ this.assets_digest = $("meta[name='assets-digest']").attr("content");
2666
+ if (History.emulated.pushState && this.options.html4 === true) {
2667
+ if (window.location.href.indexOf('#!') === -1 && window.location.pathname !== '/') {
2668
+ window.location.href = "" + window.location.protocol + "//" + window.location.host + "/#!" + window.location.pathname;
2669
+ }
2670
+ if (window.location.hash.indexOf('#!') !== -1) {
2671
+ self._call(window.location.hash.substring(2));
2672
+ }
2673
+ }
2674
+ History.Adapter.bind(window, "statechange", function(event, data) {
2675
+ var state;
2676
+ if (!History.ready) {
2677
+ return false;
2678
+ }
2679
+ state = History.getState();
2680
+ return self._call(state.url, state.data.target, state.data.render);
2681
+ });
2682
+ $(document).on("submit", "form[data-push], form[data-replace]", function(event) {
2683
+ self._process_form($(this));
2684
+ event.preventDefault();
2685
+ return false;
2686
+ });
2687
+ $(document).on("click", "a[data-push], a[data-replace]", function(event) {
2688
+ if (self._cross_origin_link(event.target) || self._non_standard_click(event)) {
2689
+ return true;
2690
+ }
2691
+ self._process_link($(this));
2692
+ event.preventDefault();
2693
+ return false;
2694
+ });
2695
+ }
2696
+ }
2697
+
2698
+ Wiselinks.prototype.enabled = function() {
2699
+ return !History.emulated.pushState || this.options.html4 === true;
2700
+ };
2701
+
2702
+ Wiselinks.prototype.load = function(url, target, render) {
2703
+ if (render == null) {
2704
+ render = 'template';
2705
+ }
2706
+ History.ready = true;
2707
+ return History.pushState({
2708
+ timestamp: new Date().getTime(),
2709
+ render: render,
2710
+ target: target
2711
+ }, document.title, url);
2712
+ };
2713
+
2714
+ Wiselinks.prototype.reload = function() {
2715
+ History.ready = true;
2716
+ return History.replaceState({
2717
+ timestamp: new Date().getTime(),
2718
+ render: 'template'
2719
+ }, document.title, History.getState().url);
2720
+ };
2721
+
2722
+ Wiselinks.prototype._defaults = function() {
2723
+ return {
2724
+ html4: true
2725
+ };
2726
+ };
2727
+
2728
+ Wiselinks.prototype._call = function(url, target, render) {
2729
+ var $document, $target, self;
2730
+ if (render == null) {
2731
+ render = 'template';
2732
+ }
2733
+ self = this;
2734
+ $target = target != null ? $(target) : self.$target;
2735
+ $document = $(document).trigger('page:loading', [url, $target.selector, render]);
2736
+ return $.ajax({
2737
+ url: url,
2738
+ headers: {
2739
+ 'X-Render': render
2740
+ },
2741
+ complete: function(xhr, status) {
2742
+ return $document.trigger('page:complete', [xhr, status]);
2743
+ },
2744
+ success: function(data, status, xhr) {
2745
+ if (self._assets_changed(xhr.getResponseHeader('X-Assets-Digest'))) {
2746
+ return window.location.reload(true);
2747
+ } else {
2748
+ self._set_title(xhr);
2749
+ $target.html(data);
2750
+ return $document.trigger('page:success', [data, status]);
2751
+ }
2752
+ },
2753
+ error: function(xhr, status, error) {
2754
+ return $document.trigger('page:error', [status, error]);
2755
+ },
2756
+ dataType: "html"
2757
+ });
2758
+ };
2759
+
2760
+ Wiselinks.prototype._process_form = function($form) {
2761
+ var $disable, item, key, name, params, self, serialized, type, url, _i, _len, _ref;
2762
+ self = this;
2763
+ $disable = $form.find(':input[value=""]');
2764
+ $disable.attr('disabled', true);
2765
+ params = {};
2766
+ _ref = $form.serializeArray();
2767
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
2768
+ item = _ref[_i];
2769
+ if (item.name !== 'utf8') {
2770
+ name = item.name.ends_with('[]') ? item.name.substr(0, item.name.length - 2) : item.name;
2771
+ if (params[name] != null) {
2772
+ params[name] = params[name] + ("," + item.value);
2773
+ } else {
2774
+ params[name] = item.value;
2775
+ }
2776
+ }
2777
+ }
2778
+ serialized = [];
2779
+ for (key in params) {
2780
+ serialized.push("" + key + "=" + params[key]);
2781
+ }
2782
+ serialized = serialized.join('&').replace(/%|!/g, '');
2783
+ url = $form.attr("action");
2784
+ if (serialized.length > 0) {
2785
+ url += "?" + serialized;
2786
+ }
2787
+ $disable.attr('disabled', false);
2788
+ type = $form.attr("data-push") === 'partial' ? 'partial' : 'template';
2789
+ return self.load(url, $form.attr("data-target"), type);
2790
+ };
2791
+
2792
+ Wiselinks.prototype._process_link = function($link) {
2793
+ var self, type;
2794
+ self = this;
2795
+ type = $link.attr("data-push") === 'partial' ? 'partial' : 'template';
2796
+ return self.load($link.attr("href"), $link.attr("data-target"), type);
2797
+ };
2798
+
2799
+ Wiselinks.prototype._cross_origin_link = function(link) {
2800
+ return (location.protocol !== link.protocol) || (location.host.split(':')[0] !== link.host.split(':')[0]);
2801
+ };
2802
+
2803
+ Wiselinks.prototype._non_standard_click = function(event) {
2804
+ return event.metaKey || event.ctrlKey || event.shiftKey || event.altKey;
2805
+ };
2806
+
2807
+ Wiselinks.prototype._assets_changed = function(digest) {
2808
+ return (this.assets_digest != null) && this.assets_digest !== digest;
2809
+ };
2810
+
2811
+ Wiselinks.prototype._set_title = function(xhr) {
2812
+ var value;
2813
+ value = xhr.getResponseHeader('X-Title');
2814
+ if (value != null) {
2815
+ return document.title = decodeURI(value);
2816
+ }
2817
+ };
2818
+
2819
+ return Wiselinks;
2820
+
2821
+ })();
2822
+
2823
+ window.Wiselinks = Wiselinks;
2824
+
2825
+ }).call(this);
2826
+ (function() {
2827
+
2828
+
2829
+
2830
+ }).call(this);