historyjs-rails 1.0.0 → 1.0.1

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.
@@ -1,621 +1,621 @@
1
- /**
2
- * History.js HTML4 Support
3
- * Depends on the HTML5 Support
4
- * @author Benjamin Arthur Lupton <contact@balupton.com>
5
- * @copyright 2010-2011 Benjamin Arthur Lupton <contact@balupton.com>
6
- * @license New BSD License <http://creativecommons.org/licenses/BSD/>
7
- */
8
-
9
- (function(window,undefined){
10
- "use strict";
11
-
12
- // ========================================================================
13
- // Initialise
14
-
15
- // Localise Globals
16
- var
17
- document = window.document, // Make sure we are using the correct document
18
- setTimeout = window.setTimeout||setTimeout,
19
- clearTimeout = window.clearTimeout||clearTimeout,
20
- setInterval = window.setInterval||setInterval,
21
- History = window.History = window.History||{}; // Public History Object
22
-
23
- // Check Existence
24
- if ( typeof History.initHtml4 !== 'undefined' ) {
25
- throw new Error('History.js HTML4 Support has already been loaded...');
26
- }
27
-
28
-
29
- // ========================================================================
30
- // Initialise HTML4 Support
31
-
32
- // Initialise HTML4 Support
33
- History.initHtml4 = function(){
34
- // Initialise
35
- if ( typeof History.initHtml4.initialized !== 'undefined' ) {
36
- // Already Loaded
37
- return false;
38
- }
39
- else {
40
- History.initHtml4.initialized = true;
41
- }
42
-
43
-
44
- // ====================================================================
45
- // Properties
46
-
47
- /**
48
- * History.enabled
49
- * Is History enabled?
50
- */
51
- History.enabled = true;
52
-
53
-
54
- // ====================================================================
55
- // Hash Storage
56
-
57
- /**
58
- * History.savedHashes
59
- * Store the hashes in an array
60
- */
61
- History.savedHashes = [];
62
-
63
- /**
64
- * History.isLastHash(newHash)
65
- * Checks if the hash is the last hash
66
- * @param {string} newHash
67
- * @return {boolean} true
68
- */
69
- History.isLastHash = function(newHash){
70
- // Prepare
71
- var oldHash = History.getHashByIndex(),
72
- isLast;
73
-
74
- // Check
75
- isLast = newHash === oldHash;
76
-
77
- // Return isLast
78
- return isLast;
79
- };
80
-
81
- /**
82
- * History.saveHash(newHash)
83
- * Push a Hash
84
- * @param {string} newHash
85
- * @return {boolean} true
86
- */
87
- History.saveHash = function(newHash){
88
- // Check Hash
89
- if ( History.isLastHash(newHash) ) {
90
- return false;
91
- }
92
-
93
- // Push the Hash
94
- History.savedHashes.push(newHash);
95
-
96
- // Return true
97
- return true;
98
- };
99
-
100
- /**
101
- * History.getHashByIndex()
102
- * Gets a hash by the index
103
- * @param {integer} index
104
- * @return {string}
105
- */
106
- History.getHashByIndex = function(index){
107
- // Prepare
108
- var hash = null;
109
-
110
- // Handle
111
- if ( typeof index === 'undefined' ) {
112
- // Get the last inserted
113
- hash = History.savedHashes[History.savedHashes.length-1];
114
- }
115
- else if ( index < 0 ) {
116
- // Get from the end
117
- hash = History.savedHashes[History.savedHashes.length+index];
118
- }
119
- else {
120
- // Get from the beginning
121
- hash = History.savedHashes[index];
122
- }
123
-
124
- // Return hash
125
- return hash;
126
- };
127
-
128
-
129
- // ====================================================================
130
- // Discarded States
131
-
132
- /**
133
- * History.discardedHashes
134
- * A hashed array of discarded hashes
135
- */
136
- History.discardedHashes = {};
137
-
138
- /**
139
- * History.discardedStates
140
- * A hashed array of discarded states
141
- */
142
- History.discardedStates = {};
143
-
144
- /**
145
- * History.discardState(State)
146
- * Discards the state by ignoring it through History
147
- * @param {object} State
148
- * @return {true}
149
- */
150
- History.discardState = function(discardedState,forwardState,backState){
151
- //History.debug('History.discardState', arguments);
152
- // Prepare
153
- var discardedStateHash = History.getHashByState(discardedState),
154
- discardObject;
155
-
156
- // Create Discard Object
157
- discardObject = {
158
- 'discardedState': discardedState,
159
- 'backState': backState,
160
- 'forwardState': forwardState
161
- };
162
-
163
- // Add to DiscardedStates
164
- History.discardedStates[discardedStateHash] = discardObject;
165
-
166
- // Return true
167
- return true;
168
- };
169
-
170
- /**
171
- * History.discardHash(hash)
172
- * Discards the hash by ignoring it through History
173
- * @param {string} hash
174
- * @return {true}
175
- */
176
- History.discardHash = function(discardedHash,forwardState,backState){
177
- //History.debug('History.discardState', arguments);
178
- // Create Discard Object
179
- var discardObject = {
180
- 'discardedHash': discardedHash,
181
- 'backState': backState,
182
- 'forwardState': forwardState
183
- };
184
-
185
- // Add to discardedHash
186
- History.discardedHashes[discardedHash] = discardObject;
187
-
188
- // Return true
189
- return true;
190
- };
191
-
192
- /**
193
- * History.discardState(State)
194
- * Checks to see if the state is discarded
195
- * @param {object} State
196
- * @return {bool}
197
- */
198
- History.discardedState = function(State){
199
- // Prepare
200
- var StateHash = History.getHashByState(State),
201
- discarded;
202
-
203
- // Check
204
- discarded = History.discardedStates[StateHash]||false;
205
-
206
- // Return true
207
- return discarded;
208
- };
209
-
210
- /**
211
- * History.discardedHash(hash)
212
- * Checks to see if the state is discarded
213
- * @param {string} State
214
- * @return {bool}
215
- */
216
- History.discardedHash = function(hash){
217
- // Check
218
- var discarded = History.discardedHashes[hash]||false;
219
-
220
- // Return true
221
- return discarded;
222
- };
223
-
224
- /**
225
- * History.recycleState(State)
226
- * Allows a discarded state to be used again
227
- * @param {object} data
228
- * @param {string} title
229
- * @param {string} url
230
- * @return {true}
231
- */
232
- History.recycleState = function(State){
233
- //History.debug('History.recycleState', arguments);
234
- // Prepare
235
- var StateHash = History.getHashByState(State);
236
-
237
- // Remove from DiscardedStates
238
- if ( History.discardedState(State) ) {
239
- delete History.discardedStates[StateHash];
240
- }
241
-
242
- // Return true
243
- return true;
244
- };
245
-
246
-
247
- // ====================================================================
248
- // HTML4 HashChange Support
249
-
250
- if ( History.emulated.hashChange ) {
251
- /*
252
- * We must emulate the HTML4 HashChange Support by manually checking for hash changes
253
- */
254
-
255
- /**
256
- * History.hashChangeInit()
257
- * Init the HashChange Emulation
258
- */
259
- History.hashChangeInit = function(){
260
- // Define our Checker Function
261
- History.checkerFunction = null;
262
-
263
- // Define some variables that will help in our checker function
264
- var lastDocumentHash = '',
265
- iframeId, iframe,
266
- lastIframeHash, checkerRunning;
267
-
268
- // Handle depending on the browser
269
- if ( History.isInternetExplorer() ) {
270
- // IE6 and IE7
271
- // We need to use an iframe to emulate the back and forward buttons
272
-
273
- // Create iFrame
274
- iframeId = 'historyjs-iframe';
275
- iframe = document.createElement('iframe');
276
-
277
- // Adjust iFarme
278
- iframe.setAttribute('id', iframeId);
279
- iframe.style.display = 'none';
280
-
281
- // Append iFrame
282
- document.body.appendChild(iframe);
283
-
284
- // Create initial history entry
285
- iframe.contentWindow.document.open();
286
- iframe.contentWindow.document.close();
287
-
288
- // Define some variables that will help in our checker function
289
- lastIframeHash = '';
290
- checkerRunning = false;
291
-
292
- // Define the checker function
293
- History.checkerFunction = function(){
294
- // Check Running
295
- if ( checkerRunning ) {
296
- return false;
297
- }
298
-
299
- // Update Running
300
- checkerRunning = true;
301
-
302
- // Fetch
303
- var documentHash = History.getHash()||'',
304
- iframeHash = History.unescapeHash(iframe.contentWindow.document.location.hash)||'';
305
-
306
- // The Document Hash has changed (application caused)
307
- if ( documentHash !== lastDocumentHash ) {
308
- // Equalise
309
- lastDocumentHash = documentHash;
310
-
311
- // Create a history entry in the iframe
312
- if ( iframeHash !== documentHash ) {
313
- //History.debug('hashchange.checker: iframe hash change', 'documentHash (new):', documentHash, 'iframeHash (old):', iframeHash);
314
-
315
- // Equalise
316
- lastIframeHash = iframeHash = documentHash;
317
-
318
- // Create History Entry
319
- iframe.contentWindow.document.open();
320
- iframe.contentWindow.document.close();
321
-
322
- // Update the iframe's hash
323
- iframe.contentWindow.document.location.hash = History.escapeHash(documentHash);
324
- }
325
-
326
- // Trigger Hashchange Event
327
- History.Adapter.trigger(window,'hashchange');
328
- }
329
-
330
- // The iFrame Hash has changed (back button caused)
331
- else if ( iframeHash !== lastIframeHash ) {
332
- //History.debug('hashchange.checker: iframe hash out of sync', 'iframeHash (new):', iframeHash, 'documentHash (old):', documentHash);
333
-
334
- // Equalise
335
- lastIframeHash = iframeHash;
336
-
337
- // Update the Hash
338
- History.setHash(iframeHash,false);
339
- }
340
-
341
- // Reset Running
342
- checkerRunning = false;
343
-
344
- // Return true
345
- return true;
346
- };
347
- }
348
- else {
349
- // We are not IE
350
- // Firefox 1 or 2, Opera
351
-
352
- // Define the checker function
353
- History.checkerFunction = function(){
354
- // Prepare
355
- var documentHash = History.getHash();
356
-
357
- // The Document Hash has changed (application caused)
358
- if ( documentHash !== lastDocumentHash ) {
359
- // Equalise
360
- lastDocumentHash = documentHash;
361
-
362
- // Trigger Hashchange Event
363
- History.Adapter.trigger(window,'hashchange');
364
- }
365
-
366
- // Return true
367
- return true;
368
- };
369
- }
370
-
371
- // Apply the checker function
372
- History.intervalList.push(setInterval(History.checkerFunction, History.options.hashChangeInterval));
373
-
374
- // Done
375
- return true;
376
- }; // History.hashChangeInit
377
-
378
- // Bind hashChangeInit
379
- History.Adapter.onDomLoad(History.hashChangeInit);
380
-
381
- } // History.emulated.hashChange
382
-
383
-
384
- // ====================================================================
385
- // HTML5 State Support
386
-
387
- // Non-Native pushState Implementation
388
- if ( History.emulated.pushState ) {
389
- /*
390
- * We must emulate the HTML5 State Management by using HTML4 HashChange
391
- */
392
-
393
- /**
394
- * History.onHashChange(event)
395
- * Trigger HTML5's window.onpopstate via HTML4 HashChange Support
396
- */
397
- History.onHashChange = function(event){
398
- //History.debug('History.onHashChange', arguments);
399
-
400
- // Prepare
401
- var currentUrl = ((event && event.newURL) || document.location.href),
402
- currentHash = History.getHashByUrl(currentUrl),
403
- currentState = null,
404
- currentStateHash = null,
405
- currentStateHashExits = null,
406
- discardObject;
407
-
408
- // Check if we are the same state
409
- if ( History.isLastHash(currentHash) ) {
410
- // There has been no change (just the page's hash has finally propagated)
411
- //History.debug('History.onHashChange: no change');
412
- History.busy(false);
413
- return false;
414
- }
415
-
416
- // Reset the double check
417
- History.doubleCheckComplete();
418
-
419
- // Store our location for use in detecting back/forward direction
420
- History.saveHash(currentHash);
421
-
422
- // Expand Hash
423
- if ( currentHash && History.isTraditionalAnchor(currentHash) ) {
424
- //History.debug('History.onHashChange: traditional anchor', currentHash);
425
- // Traditional Anchor Hash
426
- History.Adapter.trigger(window,'anchorchange');
427
- History.busy(false);
428
- return false;
429
- }
430
-
431
- // Create State
432
- currentState = History.extractState(History.getFullUrl(currentHash||document.location.href,false),true);
433
-
434
- // Check if we are the same state
435
- if ( History.isLastSavedState(currentState) ) {
436
- //History.debug('History.onHashChange: no change');
437
- // There has been no change (just the page's hash has finally propagated)
438
- History.busy(false);
439
- return false;
440
- }
441
-
442
- // Create the state Hash
443
- currentStateHash = History.getHashByState(currentState);
444
-
445
- // Check if we are DiscardedState
446
- discardObject = History.discardedState(currentState);
447
- if ( discardObject ) {
448
- // Ignore this state as it has been discarded and go back to the state before it
449
- if ( History.getHashByIndex(-2) === History.getHashByState(discardObject.forwardState) ) {
450
- // We are going backwards
451
- //History.debug('History.onHashChange: go backwards');
452
- History.back(false);
453
- } else {
454
- // We are going forwards
455
- //History.debug('History.onHashChange: go forwards');
456
- History.forward(false);
457
- }
458
- return false;
459
- }
460
-
461
- // Push the new HTML5 State
462
- //History.debug('History.onHashChange: success hashchange');
463
- History.pushState(currentState.data,currentState.title,currentState.url,false);
464
-
465
- // End onHashChange closure
466
- return true;
467
- };
468
- History.Adapter.bind(window,'hashchange',History.onHashChange);
469
-
470
- /**
471
- * History.pushState(data,title,url)
472
- * Add a new State to the history object, become it, and trigger onpopstate
473
- * We have to trigger for HTML4 compatibility
474
- * @param {object} data
475
- * @param {string} title
476
- * @param {string} url
477
- * @return {true}
478
- */
479
- History.pushState = function(data,title,url,queue){
480
- //History.debug('History.pushState: called', arguments);
481
-
482
- // Check the State
483
- if ( History.getHashByUrl(url) ) {
484
- throw new Error('History.js does not support states with fragement-identifiers (hashes/anchors).');
485
- }
486
-
487
- // Handle Queueing
488
- if ( queue !== false && History.busy() ) {
489
- // Wait + Push to Queue
490
- //History.debug('History.pushState: we must wait', arguments);
491
- History.pushQueue({
492
- scope: History,
493
- callback: History.pushState,
494
- args: arguments,
495
- queue: queue
496
- });
497
- return false;
498
- }
499
-
500
- // Make Busy
501
- History.busy(true);
502
-
503
- // Fetch the State Object
504
- var newState = History.createStateObject(data,title,url),
505
- newStateHash = History.getHashByState(newState),
506
- oldState = History.getState(false),
507
- oldStateHash = History.getHashByState(oldState),
508
- html4Hash = History.getHash();
509
-
510
- // Store the newState
511
- History.storeState(newState);
512
- History.expectedStateId = newState.id;
513
-
514
- // Recycle the State
515
- History.recycleState(newState);
516
-
517
- // Force update of the title
518
- History.setTitle(newState);
519
-
520
- // Check if we are the same State
521
- if ( newStateHash === oldStateHash ) {
522
- //History.debug('History.pushState: no change', newStateHash);
523
- History.busy(false);
524
- return false;
525
- }
526
-
527
- // Update HTML4 Hash
528
- if ( newStateHash !== html4Hash && newStateHash !== History.getShortUrl(document.location.href) ) {
529
- //History.debug('History.pushState: update hash', newStateHash, html4Hash);
530
- History.setHash(newStateHash,false);
531
- return false;
532
- }
533
-
534
- // Update HTML5 State
535
- History.saveState(newState);
536
-
537
- // Fire HTML5 Event
538
- //History.debug('History.pushState: trigger popstate');
539
- History.Adapter.trigger(window,'statechange');
540
- History.busy(false);
541
-
542
- // End pushState closure
543
- return true;
544
- };
545
-
546
- /**
547
- * History.replaceState(data,title,url)
548
- * Replace the State and trigger onpopstate
549
- * We have to trigger for HTML4 compatibility
550
- * @param {object} data
551
- * @param {string} title
552
- * @param {string} url
553
- * @return {true}
554
- */
555
- History.replaceState = function(data,title,url,queue){
556
- //History.debug('History.replaceState: called', arguments);
557
-
558
- // Check the State
559
- if ( History.getHashByUrl(url) ) {
560
- throw new Error('History.js does not support states with fragement-identifiers (hashes/anchors).');
561
- }
562
-
563
- // Handle Queueing
564
- if ( queue !== false && History.busy() ) {
565
- // Wait + Push to Queue
566
- //History.debug('History.replaceState: we must wait', arguments);
567
- History.pushQueue({
568
- scope: History,
569
- callback: History.replaceState,
570
- args: arguments,
571
- queue: queue
572
- });
573
- return false;
574
- }
575
-
576
- // Make Busy
577
- History.busy(true);
578
-
579
- // Fetch the State Objects
580
- var newState = History.createStateObject(data,title,url),
581
- oldState = History.getState(false),
582
- previousState = History.getStateByIndex(-2);
583
-
584
- // Discard Old State
585
- History.discardState(oldState,newState,previousState);
586
-
587
- // Alias to PushState
588
- History.pushState(newState.data,newState.title,newState.url,false);
589
-
590
- // End replaceState closure
591
- return true;
592
- };
593
-
594
- } // History.emulated.pushState
595
-
596
-
597
-
598
- // ====================================================================
599
- // Initialise
600
-
601
- // Non-Native pushState Implementation
602
- if ( History.emulated.pushState ) {
603
- /**
604
- * Ensure initial state is handled correctly
605
- */
606
- if ( History.getHash() && !History.emulated.hashChange ) {
607
- History.Adapter.onDomLoad(function(){
608
- History.Adapter.trigger(window,'hashchange');
609
- });
610
- }
611
-
612
- } // History.emulated.pushState
613
-
614
- }; // History.initHtml4
615
-
616
- // Try and Initialise History
617
- if ( typeof History.init !== 'undefined' ) {
618
- History.init();
619
- }
620
-
621
- })(window);
1
+ /**
2
+ * History.js HTML4 Support
3
+ * Depends on the HTML5 Support
4
+ * @author Benjamin Arthur Lupton <contact@balupton.com>
5
+ * @copyright 2010-2011 Benjamin Arthur Lupton <contact@balupton.com>
6
+ * @license New BSD License <http://creativecommons.org/licenses/BSD/>
7
+ */
8
+
9
+ (function(window,undefined){
10
+ "use strict";
11
+
12
+ // ========================================================================
13
+ // Initialise
14
+
15
+ // Localise Globals
16
+ var
17
+ document = window.document, // Make sure we are using the correct document
18
+ setTimeout = window.setTimeout||setTimeout,
19
+ clearTimeout = window.clearTimeout||clearTimeout,
20
+ setInterval = window.setInterval||setInterval,
21
+ History = window.History = window.History||{}; // Public History Object
22
+
23
+ // Check Existence
24
+ if ( typeof History.initHtml4 !== 'undefined' ) {
25
+ throw new Error('History.js HTML4 Support has already been loaded...');
26
+ }
27
+
28
+
29
+ // ========================================================================
30
+ // Initialise HTML4 Support
31
+
32
+ // Initialise HTML4 Support
33
+ History.initHtml4 = function(){
34
+ // Initialise
35
+ if ( typeof History.initHtml4.initialized !== 'undefined' ) {
36
+ // Already Loaded
37
+ return false;
38
+ }
39
+ else {
40
+ History.initHtml4.initialized = true;
41
+ }
42
+
43
+
44
+ // ====================================================================
45
+ // Properties
46
+
47
+ /**
48
+ * History.enabled
49
+ * Is History enabled?
50
+ */
51
+ History.enabled = true;
52
+
53
+
54
+ // ====================================================================
55
+ // Hash Storage
56
+
57
+ /**
58
+ * History.savedHashes
59
+ * Store the hashes in an array
60
+ */
61
+ History.savedHashes = [];
62
+
63
+ /**
64
+ * History.isLastHash(newHash)
65
+ * Checks if the hash is the last hash
66
+ * @param {string} newHash
67
+ * @return {boolean} true
68
+ */
69
+ History.isLastHash = function(newHash){
70
+ // Prepare
71
+ var oldHash = History.getHashByIndex(),
72
+ isLast;
73
+
74
+ // Check
75
+ isLast = newHash === oldHash;
76
+
77
+ // Return isLast
78
+ return isLast;
79
+ };
80
+
81
+ /**
82
+ * History.saveHash(newHash)
83
+ * Push a Hash
84
+ * @param {string} newHash
85
+ * @return {boolean} true
86
+ */
87
+ History.saveHash = function(newHash){
88
+ // Check Hash
89
+ if ( History.isLastHash(newHash) ) {
90
+ return false;
91
+ }
92
+
93
+ // Push the Hash
94
+ History.savedHashes.push(newHash);
95
+
96
+ // Return true
97
+ return true;
98
+ };
99
+
100
+ /**
101
+ * History.getHashByIndex()
102
+ * Gets a hash by the index
103
+ * @param {integer} index
104
+ * @return {string}
105
+ */
106
+ History.getHashByIndex = function(index){
107
+ // Prepare
108
+ var hash = null;
109
+
110
+ // Handle
111
+ if ( typeof index === 'undefined' ) {
112
+ // Get the last inserted
113
+ hash = History.savedHashes[History.savedHashes.length-1];
114
+ }
115
+ else if ( index < 0 ) {
116
+ // Get from the end
117
+ hash = History.savedHashes[History.savedHashes.length+index];
118
+ }
119
+ else {
120
+ // Get from the beginning
121
+ hash = History.savedHashes[index];
122
+ }
123
+
124
+ // Return hash
125
+ return hash;
126
+ };
127
+
128
+
129
+ // ====================================================================
130
+ // Discarded States
131
+
132
+ /**
133
+ * History.discardedHashes
134
+ * A hashed array of discarded hashes
135
+ */
136
+ History.discardedHashes = {};
137
+
138
+ /**
139
+ * History.discardedStates
140
+ * A hashed array of discarded states
141
+ */
142
+ History.discardedStates = {};
143
+
144
+ /**
145
+ * History.discardState(State)
146
+ * Discards the state by ignoring it through History
147
+ * @param {object} State
148
+ * @return {true}
149
+ */
150
+ History.discardState = function(discardedState,forwardState,backState){
151
+ //History.debug('History.discardState', arguments);
152
+ // Prepare
153
+ var discardedStateHash = History.getHashByState(discardedState),
154
+ discardObject;
155
+
156
+ // Create Discard Object
157
+ discardObject = {
158
+ 'discardedState': discardedState,
159
+ 'backState': backState,
160
+ 'forwardState': forwardState
161
+ };
162
+
163
+ // Add to DiscardedStates
164
+ History.discardedStates[discardedStateHash] = discardObject;
165
+
166
+ // Return true
167
+ return true;
168
+ };
169
+
170
+ /**
171
+ * History.discardHash(hash)
172
+ * Discards the hash by ignoring it through History
173
+ * @param {string} hash
174
+ * @return {true}
175
+ */
176
+ History.discardHash = function(discardedHash,forwardState,backState){
177
+ //History.debug('History.discardState', arguments);
178
+ // Create Discard Object
179
+ var discardObject = {
180
+ 'discardedHash': discardedHash,
181
+ 'backState': backState,
182
+ 'forwardState': forwardState
183
+ };
184
+
185
+ // Add to discardedHash
186
+ History.discardedHashes[discardedHash] = discardObject;
187
+
188
+ // Return true
189
+ return true;
190
+ };
191
+
192
+ /**
193
+ * History.discardState(State)
194
+ * Checks to see if the state is discarded
195
+ * @param {object} State
196
+ * @return {bool}
197
+ */
198
+ History.discardedState = function(State){
199
+ // Prepare
200
+ var StateHash = History.getHashByState(State),
201
+ discarded;
202
+
203
+ // Check
204
+ discarded = History.discardedStates[StateHash]||false;
205
+
206
+ // Return true
207
+ return discarded;
208
+ };
209
+
210
+ /**
211
+ * History.discardedHash(hash)
212
+ * Checks to see if the state is discarded
213
+ * @param {string} State
214
+ * @return {bool}
215
+ */
216
+ History.discardedHash = function(hash){
217
+ // Check
218
+ var discarded = History.discardedHashes[hash]||false;
219
+
220
+ // Return true
221
+ return discarded;
222
+ };
223
+
224
+ /**
225
+ * History.recycleState(State)
226
+ * Allows a discarded state to be used again
227
+ * @param {object} data
228
+ * @param {string} title
229
+ * @param {string} url
230
+ * @return {true}
231
+ */
232
+ History.recycleState = function(State){
233
+ //History.debug('History.recycleState', arguments);
234
+ // Prepare
235
+ var StateHash = History.getHashByState(State);
236
+
237
+ // Remove from DiscardedStates
238
+ if ( History.discardedState(State) ) {
239
+ delete History.discardedStates[StateHash];
240
+ }
241
+
242
+ // Return true
243
+ return true;
244
+ };
245
+
246
+
247
+ // ====================================================================
248
+ // HTML4 HashChange Support
249
+
250
+ if ( History.emulated.hashChange ) {
251
+ /*
252
+ * We must emulate the HTML4 HashChange Support by manually checking for hash changes
253
+ */
254
+
255
+ /**
256
+ * History.hashChangeInit()
257
+ * Init the HashChange Emulation
258
+ */
259
+ History.hashChangeInit = function(){
260
+ // Define our Checker Function
261
+ History.checkerFunction = null;
262
+
263
+ // Define some variables that will help in our checker function
264
+ var lastDocumentHash = '',
265
+ iframeId, iframe,
266
+ lastIframeHash, checkerRunning;
267
+
268
+ // Handle depending on the browser
269
+ if ( History.isInternetExplorer() ) {
270
+ // IE6 and IE7
271
+ // We need to use an iframe to emulate the back and forward buttons
272
+
273
+ // Create iFrame
274
+ iframeId = 'historyjs-iframe';
275
+ iframe = document.createElement('iframe');
276
+
277
+ // Adjust iFarme
278
+ iframe.setAttribute('id', iframeId);
279
+ iframe.style.display = 'none';
280
+
281
+ // Append iFrame
282
+ document.body.appendChild(iframe);
283
+
284
+ // Create initial history entry
285
+ iframe.contentWindow.document.open();
286
+ iframe.contentWindow.document.close();
287
+
288
+ // Define some variables that will help in our checker function
289
+ lastIframeHash = '';
290
+ checkerRunning = false;
291
+
292
+ // Define the checker function
293
+ History.checkerFunction = function(){
294
+ // Check Running
295
+ if ( checkerRunning ) {
296
+ return false;
297
+ }
298
+
299
+ // Update Running
300
+ checkerRunning = true;
301
+
302
+ // Fetch
303
+ var documentHash = History.getHash()||'',
304
+ iframeHash = History.unescapeHash(iframe.contentWindow.document.location.hash)||'';
305
+
306
+ // The Document Hash has changed (application caused)
307
+ if ( documentHash !== lastDocumentHash ) {
308
+ // Equalise
309
+ lastDocumentHash = documentHash;
310
+
311
+ // Create a history entry in the iframe
312
+ if ( iframeHash !== documentHash ) {
313
+ //History.debug('hashchange.checker: iframe hash change', 'documentHash (new):', documentHash, 'iframeHash (old):', iframeHash);
314
+
315
+ // Equalise
316
+ lastIframeHash = iframeHash = documentHash;
317
+
318
+ // Create History Entry
319
+ iframe.contentWindow.document.open();
320
+ iframe.contentWindow.document.close();
321
+
322
+ // Update the iframe's hash
323
+ iframe.contentWindow.document.location.hash = History.escapeHash(documentHash);
324
+ }
325
+
326
+ // Trigger Hashchange Event
327
+ History.Adapter.trigger(window,'hashchange');
328
+ }
329
+
330
+ // The iFrame Hash has changed (back button caused)
331
+ else if ( iframeHash !== lastIframeHash ) {
332
+ //History.debug('hashchange.checker: iframe hash out of sync', 'iframeHash (new):', iframeHash, 'documentHash (old):', documentHash);
333
+
334
+ // Equalise
335
+ lastIframeHash = iframeHash;
336
+
337
+ // Update the Hash
338
+ History.setHash(iframeHash,false);
339
+ }
340
+
341
+ // Reset Running
342
+ checkerRunning = false;
343
+
344
+ // Return true
345
+ return true;
346
+ };
347
+ }
348
+ else {
349
+ // We are not IE
350
+ // Firefox 1 or 2, Opera
351
+
352
+ // Define the checker function
353
+ History.checkerFunction = function(){
354
+ // Prepare
355
+ var documentHash = History.getHash();
356
+
357
+ // The Document Hash has changed (application caused)
358
+ if ( documentHash !== lastDocumentHash ) {
359
+ // Equalise
360
+ lastDocumentHash = documentHash;
361
+
362
+ // Trigger Hashchange Event
363
+ History.Adapter.trigger(window,'hashchange');
364
+ }
365
+
366
+ // Return true
367
+ return true;
368
+ };
369
+ }
370
+
371
+ // Apply the checker function
372
+ History.intervalList.push(setInterval(History.checkerFunction, History.options.hashChangeInterval));
373
+
374
+ // Done
375
+ return true;
376
+ }; // History.hashChangeInit
377
+
378
+ // Bind hashChangeInit
379
+ History.Adapter.onDomLoad(History.hashChangeInit);
380
+
381
+ } // History.emulated.hashChange
382
+
383
+
384
+ // ====================================================================
385
+ // HTML5 State Support
386
+
387
+ // Non-Native pushState Implementation
388
+ if ( History.emulated.pushState ) {
389
+ /*
390
+ * We must emulate the HTML5 State Management by using HTML4 HashChange
391
+ */
392
+
393
+ /**
394
+ * History.onHashChange(event)
395
+ * Trigger HTML5's window.onpopstate via HTML4 HashChange Support
396
+ */
397
+ History.onHashChange = function(event){
398
+ //History.debug('History.onHashChange', arguments);
399
+
400
+ // Prepare
401
+ var currentUrl = ((event && event.newURL) || document.location.href),
402
+ currentHash = History.getHashByUrl(currentUrl),
403
+ currentState = null,
404
+ currentStateHash = null,
405
+ currentStateHashExits = null,
406
+ discardObject;
407
+
408
+ // Check if we are the same state
409
+ if ( History.isLastHash(currentHash) ) {
410
+ // There has been no change (just the page's hash has finally propagated)
411
+ //History.debug('History.onHashChange: no change');
412
+ History.busy(false);
413
+ return false;
414
+ }
415
+
416
+ // Reset the double check
417
+ History.doubleCheckComplete();
418
+
419
+ // Store our location for use in detecting back/forward direction
420
+ History.saveHash(currentHash);
421
+
422
+ // Expand Hash
423
+ if ( currentHash && History.isTraditionalAnchor(currentHash) ) {
424
+ //History.debug('History.onHashChange: traditional anchor', currentHash);
425
+ // Traditional Anchor Hash
426
+ History.Adapter.trigger(window,'anchorchange');
427
+ History.busy(false);
428
+ return false;
429
+ }
430
+
431
+ // Create State
432
+ currentState = History.extractState(History.getFullUrl(currentHash||document.location.href,false),true);
433
+
434
+ // Check if we are the same state
435
+ if ( History.isLastSavedState(currentState) ) {
436
+ //History.debug('History.onHashChange: no change');
437
+ // There has been no change (just the page's hash has finally propagated)
438
+ History.busy(false);
439
+ return false;
440
+ }
441
+
442
+ // Create the state Hash
443
+ currentStateHash = History.getHashByState(currentState);
444
+
445
+ // Check if we are DiscardedState
446
+ discardObject = History.discardedState(currentState);
447
+ if ( discardObject ) {
448
+ // Ignore this state as it has been discarded and go back to the state before it
449
+ if ( History.getHashByIndex(-2) === History.getHashByState(discardObject.forwardState) ) {
450
+ // We are going backwards
451
+ //History.debug('History.onHashChange: go backwards');
452
+ History.back(false);
453
+ } else {
454
+ // We are going forwards
455
+ //History.debug('History.onHashChange: go forwards');
456
+ History.forward(false);
457
+ }
458
+ return false;
459
+ }
460
+
461
+ // Push the new HTML5 State
462
+ //History.debug('History.onHashChange: success hashchange');
463
+ History.pushState(currentState.data,currentState.title,currentState.url,false);
464
+
465
+ // End onHashChange closure
466
+ return true;
467
+ };
468
+ History.Adapter.bind(window,'hashchange',History.onHashChange);
469
+
470
+ /**
471
+ * History.pushState(data,title,url)
472
+ * Add a new State to the history object, become it, and trigger onpopstate
473
+ * We have to trigger for HTML4 compatibility
474
+ * @param {object} data
475
+ * @param {string} title
476
+ * @param {string} url
477
+ * @return {true}
478
+ */
479
+ History.pushState = function(data,title,url,queue){
480
+ //History.debug('History.pushState: called', arguments);
481
+
482
+ // Check the State
483
+ if ( History.getHashByUrl(url) ) {
484
+ throw new Error('History.js does not support states with fragement-identifiers (hashes/anchors).');
485
+ }
486
+
487
+ // Handle Queueing
488
+ if ( queue !== false && History.busy() ) {
489
+ // Wait + Push to Queue
490
+ //History.debug('History.pushState: we must wait', arguments);
491
+ History.pushQueue({
492
+ scope: History,
493
+ callback: History.pushState,
494
+ args: arguments,
495
+ queue: queue
496
+ });
497
+ return false;
498
+ }
499
+
500
+ // Make Busy
501
+ History.busy(true);
502
+
503
+ // Fetch the State Object
504
+ var newState = History.createStateObject(data,title,url),
505
+ newStateHash = History.getHashByState(newState),
506
+ oldState = History.getState(false),
507
+ oldStateHash = History.getHashByState(oldState),
508
+ html4Hash = History.getHash();
509
+
510
+ // Store the newState
511
+ History.storeState(newState);
512
+ History.expectedStateId = newState.id;
513
+
514
+ // Recycle the State
515
+ History.recycleState(newState);
516
+
517
+ // Force update of the title
518
+ History.setTitle(newState);
519
+
520
+ // Check if we are the same State
521
+ if ( newStateHash === oldStateHash ) {
522
+ //History.debug('History.pushState: no change', newStateHash);
523
+ History.busy(false);
524
+ return false;
525
+ }
526
+
527
+ // Update HTML4 Hash
528
+ if ( newStateHash !== html4Hash && newStateHash !== History.getShortUrl(document.location.href) ) {
529
+ //History.debug('History.pushState: update hash', newStateHash, html4Hash);
530
+ History.setHash(newStateHash,false);
531
+ return false;
532
+ }
533
+
534
+ // Update HTML5 State
535
+ History.saveState(newState);
536
+
537
+ // Fire HTML5 Event
538
+ //History.debug('History.pushState: trigger popstate');
539
+ History.Adapter.trigger(window,'statechange');
540
+ History.busy(false);
541
+
542
+ // End pushState closure
543
+ return true;
544
+ };
545
+
546
+ /**
547
+ * History.replaceState(data,title,url)
548
+ * Replace the State and trigger onpopstate
549
+ * We have to trigger for HTML4 compatibility
550
+ * @param {object} data
551
+ * @param {string} title
552
+ * @param {string} url
553
+ * @return {true}
554
+ */
555
+ History.replaceState = function(data,title,url,queue){
556
+ //History.debug('History.replaceState: called', arguments);
557
+
558
+ // Check the State
559
+ if ( History.getHashByUrl(url) ) {
560
+ throw new Error('History.js does not support states with fragement-identifiers (hashes/anchors).');
561
+ }
562
+
563
+ // Handle Queueing
564
+ if ( queue !== false && History.busy() ) {
565
+ // Wait + Push to Queue
566
+ //History.debug('History.replaceState: we must wait', arguments);
567
+ History.pushQueue({
568
+ scope: History,
569
+ callback: History.replaceState,
570
+ args: arguments,
571
+ queue: queue
572
+ });
573
+ return false;
574
+ }
575
+
576
+ // Make Busy
577
+ History.busy(true);
578
+
579
+ // Fetch the State Objects
580
+ var newState = History.createStateObject(data,title,url),
581
+ oldState = History.getState(false),
582
+ previousState = History.getStateByIndex(-2);
583
+
584
+ // Discard Old State
585
+ History.discardState(oldState,newState,previousState);
586
+
587
+ // Alias to PushState
588
+ History.pushState(newState.data,newState.title,newState.url,false);
589
+
590
+ // End replaceState closure
591
+ return true;
592
+ };
593
+
594
+ } // History.emulated.pushState
595
+
596
+
597
+
598
+ // ====================================================================
599
+ // Initialise
600
+
601
+ // Non-Native pushState Implementation
602
+ if ( History.emulated.pushState ) {
603
+ /**
604
+ * Ensure initial state is handled correctly
605
+ */
606
+ if ( History.getHash() && !History.emulated.hashChange ) {
607
+ History.Adapter.onDomLoad(function(){
608
+ History.Adapter.trigger(window,'hashchange');
609
+ });
610
+ }
611
+
612
+ } // History.emulated.pushState
613
+
614
+ }; // History.initHtml4
615
+
616
+ // Try and Initialise History
617
+ if ( typeof History.init !== 'undefined' ) {
618
+ History.init();
619
+ }
620
+
621
+ })(window);