historyjs-rails 1.0.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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);