wiselinks 0.7.3 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,1946 +0,0 @@
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
- (function(window,undefined){
9
- "use strict";
10
-
11
- // ========================================================================
12
- // Initialise
13
-
14
- // Localise Globals
15
- var
16
- console = window.console||undefined, // Prevent a JSLint complain
17
- document = window.document, // Make sure we are using the correct document
18
- navigator = window.navigator, // Make sure we are using the correct navigator
19
- sessionStorage = window.sessionStorage||false, // sessionStorage
20
- setTimeout = window.setTimeout,
21
- clearTimeout = window.clearTimeout,
22
- setInterval = window.setInterval,
23
- clearInterval = window.clearInterval,
24
- JSON = window.JSON,
25
- alert = window.alert,
26
- History = window.History = window.History||{}, // Public History Object
27
- history = window.history; // Old History Object
28
-
29
- // MooTools Compatibility
30
- JSON.stringify = JSON.stringify||JSON.encode;
31
- JSON.parse = JSON.parse||JSON.decode;
32
-
33
- // Check Existence
34
- if ( typeof History.init !== 'undefined' ) {
35
- throw new Error('History.js Core has already been loaded...');
36
- }
37
-
38
- // Initialise History
39
- History.init = function(){
40
- // Check Load Status of Adapter
41
- if ( typeof History.Adapter === 'undefined' ) {
42
- return false;
43
- }
44
-
45
- // Check Load Status of Core
46
- if ( typeof History.initCore !== 'undefined' ) {
47
- History.initCore();
48
- }
49
-
50
- // Check Load Status of HTML4 Support
51
- if ( typeof History.initHtml4 !== 'undefined' ) {
52
- History.initHtml4();
53
- }
54
-
55
- // Return true
56
- return true;
57
- };
58
-
59
-
60
- // ========================================================================
61
- // Initialise Core
62
-
63
- // Initialise Core
64
- History.initCore = function(){
65
- // Initialise
66
- if ( typeof History.initCore.initialized !== 'undefined' ) {
67
- // Already Loaded
68
- return false;
69
- }
70
- else {
71
- History.initCore.initialized = true;
72
- }
73
-
74
-
75
- // ====================================================================
76
- // Options
77
-
78
- /**
79
- * History.options
80
- * Configurable options
81
- */
82
- History.options = History.options||{};
83
-
84
- /**
85
- * History.options.hashChangeInterval
86
- * How long should the interval be before hashchange checks
87
- */
88
- History.options.hashChangeInterval = History.options.hashChangeInterval || 100;
89
-
90
- /**
91
- * History.options.safariPollInterval
92
- * How long should the interval be before safari poll checks
93
- */
94
- History.options.safariPollInterval = History.options.safariPollInterval || 500;
95
-
96
- /**
97
- * History.options.doubleCheckInterval
98
- * How long should the interval be before we perform a double check
99
- */
100
- History.options.doubleCheckInterval = History.options.doubleCheckInterval || 500;
101
-
102
- /**
103
- * History.options.storeInterval
104
- * How long should we wait between store calls
105
- */
106
- History.options.storeInterval = History.options.storeInterval || 1000;
107
-
108
- /**
109
- * History.options.busyDelay
110
- * How long should we wait between busy events
111
- */
112
- History.options.busyDelay = History.options.busyDelay || 250;
113
-
114
- /**
115
- * History.options.debug
116
- * If true will enable debug messages to be logged
117
- */
118
- History.options.debug = History.options.debug || false;
119
-
120
- /**
121
- * History.options.initialTitle
122
- * What is the title of the initial state
123
- */
124
- History.options.initialTitle = History.options.initialTitle || document.title;
125
-
126
-
127
- // ====================================================================
128
- // Interval record
129
-
130
- /**
131
- * History.intervalList
132
- * List of intervals set, to be cleared when document is unloaded.
133
- */
134
- History.intervalList = [];
135
-
136
- /**
137
- * History.clearAllIntervals
138
- * Clears all setInterval instances.
139
- */
140
- History.clearAllIntervals = function(){
141
- var i, il = History.intervalList;
142
- if (typeof il !== "undefined" && il !== null) {
143
- for (i = 0; i < il.length; i++) {
144
- clearInterval(il[i]);
145
- }
146
- History.intervalList = null;
147
- }
148
- };
149
-
150
-
151
- // ====================================================================
152
- // Debug
153
-
154
- /**
155
- * History.debug(message,...)
156
- * Logs the passed arguments if debug enabled
157
- */
158
- History.debug = function(){
159
- if ( (History.options.debug||false) ) {
160
- History.log.apply(History,arguments);
161
- }
162
- };
163
-
164
- /**
165
- * History.log(message,...)
166
- * Logs the passed arguments
167
- */
168
- History.log = function(){
169
- // Prepare
170
- var
171
- consoleExists = !(typeof console === 'undefined' || typeof console.log === 'undefined' || typeof console.log.apply === 'undefined'),
172
- textarea = document.getElementById('log'),
173
- message,
174
- i,n,
175
- args,arg
176
- ;
177
-
178
- // Write to Console
179
- if ( consoleExists ) {
180
- args = Array.prototype.slice.call(arguments);
181
- message = args.shift();
182
- if ( typeof console.debug !== 'undefined' ) {
183
- console.debug.apply(console,[message,args]);
184
- }
185
- else {
186
- console.log.apply(console,[message,args]);
187
- }
188
- }
189
- else {
190
- message = ("\n"+arguments[0]+"\n");
191
- }
192
-
193
- // Write to log
194
- for ( i=1,n=arguments.length; i<n; ++i ) {
195
- arg = arguments[i];
196
- if ( typeof arg === 'object' && typeof JSON !== 'undefined' ) {
197
- try {
198
- arg = JSON.stringify(arg);
199
- }
200
- catch ( Exception ) {
201
- // Recursive Object
202
- }
203
- }
204
- message += "\n"+arg+"\n";
205
- }
206
-
207
- // Textarea
208
- if ( textarea ) {
209
- textarea.value += message+"\n-----\n";
210
- textarea.scrollTop = textarea.scrollHeight - textarea.clientHeight;
211
- }
212
- // No Textarea, No Console
213
- else if ( !consoleExists ) {
214
- alert(message);
215
- }
216
-
217
- // Return true
218
- return true;
219
- };
220
-
221
-
222
- // ====================================================================
223
- // Emulated Status
224
-
225
- /**
226
- * History.getInternetExplorerMajorVersion()
227
- * Get's the major version of Internet Explorer
228
- * @return {integer}
229
- * @license Public Domain
230
- * @author Benjamin Arthur Lupton <contact@balupton.com>
231
- * @author James Padolsey <https://gist.github.com/527683>
232
- */
233
- History.getInternetExplorerMajorVersion = function(){
234
- var result = History.getInternetExplorerMajorVersion.cached =
235
- (typeof History.getInternetExplorerMajorVersion.cached !== 'undefined')
236
- ? History.getInternetExplorerMajorVersion.cached
237
- : (function(){
238
- var v = 3,
239
- div = document.createElement('div'),
240
- all = div.getElementsByTagName('i');
241
- while ( (div.innerHTML = '<!--[if gt IE ' + (++v) + ']><i></i><![endif]-->') && all[0] ) {}
242
- return (v > 4) ? v : false;
243
- })()
244
- ;
245
- return result;
246
- };
247
-
248
- /**
249
- * History.isInternetExplorer()
250
- * Are we using Internet Explorer?
251
- * @return {boolean}
252
- * @license Public Domain
253
- * @author Benjamin Arthur Lupton <contact@balupton.com>
254
- */
255
- History.isInternetExplorer = function(){
256
- var result =
257
- History.isInternetExplorer.cached =
258
- (typeof History.isInternetExplorer.cached !== 'undefined')
259
- ? History.isInternetExplorer.cached
260
- : Boolean(History.getInternetExplorerMajorVersion())
261
- ;
262
- return result;
263
- };
264
-
265
- /**
266
- * History.emulated
267
- * Which features require emulating?
268
- */
269
- History.emulated = {
270
- pushState: !Boolean(
271
- window.history && window.history.pushState && window.history.replaceState
272
- && !(
273
- (/ 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) */
274
- || (/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 */
275
- )
276
- ),
277
- hashChange: Boolean(
278
- !(('onhashchange' in window) || ('onhashchange' in document))
279
- ||
280
- (History.isInternetExplorer() && History.getInternetExplorerMajorVersion() < 8)
281
- )
282
- };
283
-
284
- /**
285
- * History.enabled
286
- * Is History enabled?
287
- */
288
- History.enabled = !History.emulated.pushState;
289
-
290
- /**
291
- * History.bugs
292
- * Which bugs are present
293
- */
294
- History.bugs = {
295
- /**
296
- * Safari 5 and Safari iOS 4 fail to return to the correct state once a hash is replaced by a `replaceState` call
297
- * https://bugs.webkit.org/show_bug.cgi?id=56249
298
- */
299
- setHash: Boolean(!History.emulated.pushState && navigator.vendor === 'Apple Computer, Inc.' && /AppleWebKit\/5([0-2]|3[0-3])/.test(navigator.userAgent)),
300
-
301
- /**
302
- * Safari 5 and Safari iOS 4 sometimes fail to apply the state change under busy conditions
303
- * https://bugs.webkit.org/show_bug.cgi?id=42940
304
- */
305
- safariPoll: Boolean(!History.emulated.pushState && navigator.vendor === 'Apple Computer, Inc.' && /AppleWebKit\/5([0-2]|3[0-3])/.test(navigator.userAgent)),
306
-
307
- /**
308
- * MSIE 6 and 7 sometimes do not apply a hash even it was told to (requiring a second call to the apply function)
309
- */
310
- ieDoubleCheck: Boolean(History.isInternetExplorer() && History.getInternetExplorerMajorVersion() < 8),
311
-
312
- /**
313
- * MSIE 6 requires the entire hash to be encoded for the hashes to trigger the onHashChange event
314
- */
315
- hashEscape: Boolean(History.isInternetExplorer() && History.getInternetExplorerMajorVersion() < 7)
316
- };
317
-
318
- /**
319
- * History.isEmptyObject(obj)
320
- * Checks to see if the Object is Empty
321
- * @param {Object} obj
322
- * @return {boolean}
323
- */
324
- History.isEmptyObject = function(obj) {
325
- for ( var name in obj ) {
326
- return false;
327
- }
328
- return true;
329
- };
330
-
331
- /**
332
- * History.cloneObject(obj)
333
- * Clones a object and eliminate all references to the original contexts
334
- * @param {Object} obj
335
- * @return {Object}
336
- */
337
- History.cloneObject = function(obj) {
338
- var hash,newObj;
339
- if ( obj ) {
340
- hash = JSON.stringify(obj);
341
- newObj = JSON.parse(hash);
342
- }
343
- else {
344
- newObj = {};
345
- }
346
- return newObj;
347
- };
348
-
349
-
350
- // ====================================================================
351
- // URL Helpers
352
-
353
- /**
354
- * History.getRootUrl()
355
- * Turns "http://mysite.com/dir/page.html?asd" into "http://mysite.com"
356
- * @return {String} rootUrl
357
- */
358
- History.getRootUrl = function(){
359
- // Create
360
- var rootUrl = document.location.protocol+'//'+(document.location.hostname||document.location.host);
361
- if ( document.location.port||false ) {
362
- rootUrl += ':'+document.location.port;
363
- }
364
- rootUrl += '/';
365
-
366
- // Return
367
- return rootUrl;
368
- };
369
-
370
- /**
371
- * History.getBaseHref()
372
- * Fetches the `href` attribute of the `<base href="...">` element if it exists
373
- * @return {String} baseHref
374
- */
375
- History.getBaseHref = function(){
376
- // Create
377
- var
378
- baseElements = document.getElementsByTagName('base'),
379
- baseElement = null,
380
- baseHref = '';
381
-
382
- // Test for Base Element
383
- if ( baseElements.length === 1 ) {
384
- // Prepare for Base Element
385
- baseElement = baseElements[0];
386
- baseHref = baseElement.href.replace(/[^\/]+$/,'');
387
- }
388
-
389
- // Adjust trailing slash
390
- baseHref = baseHref.replace(/\/+$/,'');
391
- if ( baseHref ) baseHref += '/';
392
-
393
- // Return
394
- return baseHref;
395
- };
396
-
397
- /**
398
- * History.getBaseUrl()
399
- * Fetches the baseHref or basePageUrl or rootUrl (whichever one exists first)
400
- * @return {String} baseUrl
401
- */
402
- History.getBaseUrl = function(){
403
- // Create
404
- var baseUrl = History.getBaseHref()||History.getBasePageUrl()||History.getRootUrl();
405
-
406
- // Return
407
- return baseUrl;
408
- };
409
-
410
- /**
411
- * History.getPageUrl()
412
- * Fetches the URL of the current page
413
- * @return {String} pageUrl
414
- */
415
- History.getPageUrl = function(){
416
- // Fetch
417
- var
418
- State = History.getState(false,false),
419
- stateUrl = (State||{}).url||document.location.href,
420
- pageUrl;
421
-
422
- // Create
423
- pageUrl = stateUrl.replace(/\/+$/,'').replace(/[^\/]+$/,function(part,index,string){
424
- return (/\!/).test(part) ? part : part+'/';
425
- });
426
-
427
- // Return
428
- return pageUrl;
429
- };
430
-
431
- /**
432
- * History.getBasePageUrl()
433
- * Fetches the Url of the directory of the current page
434
- * @return {String} basePageUrl
435
- */
436
- History.getBasePageUrl = function(){
437
- // Create
438
- var basePageUrl = document.location.href.replace(/[#\?].*/,'').replace(/[^\/]+$/,function(part,index,string){
439
- return (/[^\/]$/).test(part) ? '' : part;
440
- }).replace(/\/+$/,'')+'/';
441
-
442
- // Return
443
- return basePageUrl;
444
- };
445
-
446
- /**
447
- * History.getFullUrl(url)
448
- * Ensures that we have an absolute URL and not a relative URL
449
- * @param {string} url
450
- * @param {Boolean} allowBaseHref
451
- * @return {string} fullUrl
452
- */
453
- History.getFullUrl = function(url,allowBaseHref){
454
- // Prepare
455
- var fullUrl = url, firstChar = url.substring(0,1);
456
- allowBaseHref = (typeof allowBaseHref === 'undefined') ? true : allowBaseHref;
457
-
458
- // Check
459
- if ( /[a-z]+\:\/\//.test(url) ) {
460
- // Full URL
461
- }
462
- else if ( firstChar === '/' ) {
463
- // Root URL
464
- fullUrl = History.getRootUrl()+url.replace(/^\/+/,'');
465
- }
466
- else if ( firstChar === '#' ) {
467
- // Anchor URL
468
- fullUrl = History.getPageUrl().replace(/#!*/,'')+url;
469
- }
470
- else if ( firstChar === '?' ) {
471
- // Query URL
472
- fullUrl = History.getPageUrl().replace(/[\?#]!*/,'')+url;
473
- }
474
- else {
475
- // Relative URL
476
- if ( allowBaseHref ) {
477
- fullUrl = History.getBaseUrl()+url.replace(/^(\!\/)+/,'');
478
- } else {
479
- fullUrl = History.getBasePageUrl()+url.replace(/^(\!\/)+/,'');
480
- }
481
- // We have an if condition above as we do not want hashes
482
- // which are relative to the baseHref in our URLs
483
- // as if the baseHref changes, then all our bookmarks
484
- // would now point to different locations
485
- // whereas the basePageUrl will always stay the same
486
- }
487
-
488
- // Return
489
- return fullUrl.replace(/\#$/,'');
490
- };
491
-
492
- /**
493
- * History.getShortUrl(url)
494
- * Ensures that we have a relative URL and not a absolute URL
495
- * @param {string} url
496
- * @return {string} url
497
- */
498
- History.getShortUrl = function(url){
499
- // Prepare
500
- var shortUrl = url, baseUrl = History.getBaseUrl(), rootUrl = History.getRootUrl();
501
-
502
- // Trim baseUrl
503
- if ( History.emulated.pushState ) {
504
- // We are in a if statement as when pushState is not emulated
505
- // The actual url these short urls are relative to can change
506
- // So within the same session, we the url may end up somewhere different
507
- shortUrl = shortUrl.replace(baseUrl,'');
508
- }
509
-
510
- // Trim rootUrl
511
- shortUrl = shortUrl.replace(rootUrl,'/');
512
-
513
- // Ensure we can still detect it as a state
514
- // if ( History.isTraditionalAnchor(shortUrl) ) {
515
- // shortUrl = './'+shortUrl;
516
- // // shortUrl = '/'+shortUrl;
517
- // }
518
-
519
- shortUrl = '!/'+shortUrl;
520
-
521
- // Clean It
522
- shortUrl = shortUrl.replace(/^(\!\/)+/g,'!/').replace(/\#$/,'');
523
-
524
- // Return
525
- return shortUrl;
526
- };
527
-
528
-
529
- // ====================================================================
530
- // State Storage
531
-
532
- /**
533
- * History.store
534
- * The store for all session specific data
535
- */
536
- History.store = {};
537
-
538
- /**
539
- * History.idToState
540
- * 1-1: State ID to State Object
541
- */
542
- History.idToState = History.idToState||{};
543
-
544
- /**
545
- * History.stateToId
546
- * 1-1: State String to State ID
547
- */
548
- History.stateToId = History.stateToId||{};
549
-
550
- /**
551
- * History.urlToId
552
- * 1-1: State URL to State ID
553
- */
554
- History.urlToId = History.urlToId||{};
555
-
556
- /**
557
- * History.storedStates
558
- * Store the states in an array
559
- */
560
- History.storedStates = History.storedStates||[];
561
-
562
- /**
563
- * History.savedStates
564
- * Saved the states in an array
565
- */
566
- History.savedStates = History.savedStates||[];
567
-
568
- /**
569
- * History.noramlizeStore()
570
- * Noramlize the store by adding necessary values
571
- */
572
- History.normalizeStore = function(){
573
- History.store.idToState = History.store.idToState||{};
574
- History.store.urlToId = History.store.urlToId||{};
575
- History.store.stateToId = History.store.stateToId||{};
576
- };
577
-
578
- /**
579
- * History.getState()
580
- * Get an object containing the data, title and url of the current state
581
- * @param {Boolean} friendly
582
- * @param {Boolean} create
583
- * @return {Object} State
584
- */
585
- History.getState = function(friendly,create){
586
- // Prepare
587
- if ( typeof friendly === 'undefined' ) { friendly = true; }
588
- if ( typeof create === 'undefined' ) { create = true; }
589
-
590
- // Fetch
591
- var State = History.getLastSavedState();
592
-
593
- // Create
594
- if ( !State && create ) {
595
- State = History.createStateObject();
596
- }
597
-
598
- // Adjust
599
- if ( friendly ) {
600
- State = History.cloneObject(State);
601
- State.url = State.cleanUrl||State.url;
602
- }
603
-
604
- // Return
605
- return State;
606
- };
607
-
608
- /**
609
- * History.getIdByState(State)
610
- * Gets a ID for a State
611
- * @param {State} newState
612
- * @return {String} id
613
- */
614
- History.getIdByState = function(newState){
615
-
616
- // Fetch ID
617
- var id = History.extractId(newState.url),
618
- str;
619
-
620
- if ( !id ) {
621
- // Find ID via State String
622
- str = History.getStateString(newState);
623
- if ( typeof History.stateToId[str] !== 'undefined' ) {
624
- id = History.stateToId[str];
625
- }
626
- else if ( typeof History.store.stateToId[str] !== 'undefined' ) {
627
- id = History.store.stateToId[str];
628
- }
629
- else {
630
- // Generate a new ID
631
- while ( true ) {
632
- id = (new Date()).getTime() + String(Math.random()).replace(/\D/g,'');
633
- if ( typeof History.idToState[id] === 'undefined' && typeof History.store.idToState[id] === 'undefined' ) {
634
- break;
635
- }
636
- }
637
-
638
- // Apply the new State to the ID
639
- History.stateToId[str] = id;
640
- History.idToState[id] = newState;
641
- }
642
- }
643
-
644
- // Return ID
645
- return id;
646
- };
647
-
648
- /**
649
- * History.normalizeState(State)
650
- * Expands a State Object
651
- * @param {object} State
652
- * @return {object}
653
- */
654
- History.normalizeState = function(oldState){
655
- // Variables
656
- var newState, dataNotEmpty;
657
-
658
- // Prepare
659
- if ( !oldState || (typeof oldState !== 'object') ) {
660
- oldState = {};
661
- }
662
-
663
- // Check
664
- if ( typeof oldState.normalized !== 'undefined' ) {
665
- return oldState;
666
- }
667
-
668
- // Adjust
669
- if ( !oldState.data || (typeof oldState.data !== 'object') ) {
670
- oldState.data = {};
671
- }
672
-
673
- // ----------------------------------------------------------------
674
-
675
- // Create
676
- newState = {};
677
- newState.normalized = true;
678
- newState.title = oldState.title||'';
679
- newState.url = History.getFullUrl(History.unescapeString(oldState.url||document.location.href));
680
- newState.hash = History.getShortUrl(newState.url);
681
- newState.data = History.cloneObject(oldState.data);
682
-
683
- // Fetch ID
684
- newState.id = History.getIdByState(newState);
685
-
686
- // ----------------------------------------------------------------
687
-
688
- // Clean the URL
689
- newState.cleanUrl = newState.url.replace(/\??\&_suid.*/,'');
690
- newState.url = newState.cleanUrl;
691
-
692
- // Check to see if we have more than just a url
693
- dataNotEmpty = !History.isEmptyObject(newState.data);
694
-
695
- // Apply
696
- if ( newState.title || dataNotEmpty ) {
697
- // Add ID to Hash
698
- newState.hash = History.getShortUrl(newState.url).replace(/\??\&_suid.*/,'');
699
- if ( !/\?/.test(newState.hash) ) {
700
- newState.hash += '?';
701
- }
702
- newState.hash += '&_suid='+newState.id;
703
- }
704
-
705
- // Create the Hashed URL
706
- newState.hashedUrl = History.getFullUrl(newState.hash);
707
-
708
- // ----------------------------------------------------------------
709
-
710
- // Update the URL if we have a duplicate
711
- if ( (History.emulated.pushState || History.bugs.safariPoll) && History.hasUrlDuplicate(newState) ) {
712
- newState.url = newState.hashedUrl;
713
- }
714
-
715
- // ----------------------------------------------------------------
716
-
717
- // Return
718
- return newState;
719
- };
720
-
721
- /**
722
- * History.createStateObject(data,title,url)
723
- * Creates a object based on the data, title and url state params
724
- * @param {object} data
725
- * @param {string} title
726
- * @param {string} url
727
- * @return {object}
728
- */
729
- History.createStateObject = function(data,title,url){
730
- // Hashify
731
- var State = {
732
- 'data': data,
733
- 'title': title,
734
- 'url': url
735
- };
736
-
737
- // Expand the State
738
- State = History.normalizeState(State);
739
-
740
- // Return object
741
- return State;
742
- };
743
-
744
- /**
745
- * History.getStateById(id)
746
- * Get a state by it's UID
747
- * @param {String} id
748
- */
749
- History.getStateById = function(id){
750
- // Prepare
751
- id = String(id);
752
-
753
- // Retrieve
754
- var State = History.idToState[id] || History.store.idToState[id] || undefined;
755
-
756
- // Return State
757
- return State;
758
- };
759
-
760
- /**
761
- * Get a State's String
762
- * @param {State} passedState
763
- */
764
- History.getStateString = function(passedState){
765
- // Prepare
766
- var State, cleanedState, str;
767
-
768
- // Fetch
769
- State = History.normalizeState(passedState);
770
-
771
- // Clean
772
- cleanedState = {
773
- data: State.data,
774
- title: passedState.title,
775
- url: passedState.url
776
- };
777
-
778
- // Fetch
779
- str = JSON.stringify(cleanedState);
780
-
781
- // Return
782
- return str;
783
- };
784
-
785
- /**
786
- * Get a State's ID
787
- * @param {State} passedState
788
- * @return {String} id
789
- */
790
- History.getStateId = function(passedState){
791
- // Prepare
792
- var State, id;
793
-
794
- // Fetch
795
- State = History.normalizeState(passedState);
796
-
797
- // Fetch
798
- id = State.id;
799
-
800
- // Return
801
- return id;
802
- };
803
-
804
- /**
805
- * History.getHashByState(State)
806
- * Creates a Hash for the State Object
807
- * @param {State} passedState
808
- * @return {String} hash
809
- */
810
- History.getHashByState = function(passedState){
811
- // Prepare
812
- var State, hash;
813
-
814
- // Fetch
815
- State = History.normalizeState(passedState);
816
-
817
- // Hash
818
- hash = State.hash;
819
-
820
- // Return
821
- return hash;
822
- };
823
-
824
- /**
825
- * History.extractId(url_or_hash)
826
- * Get a State ID by it's URL or Hash
827
- * @param {string} url_or_hash
828
- * @return {string} id
829
- */
830
- History.extractId = function ( url_or_hash ) {
831
- // Prepare
832
- var id,parts,url;
833
-
834
- // Extract
835
- parts = /(.*)\&_suid=([0-9]+)$/.exec(url_or_hash);
836
- url = parts ? (parts[1]||url_or_hash) : url_or_hash;
837
- id = parts ? String(parts[2]||'') : '';
838
-
839
- // Return
840
- return id||false;
841
- };
842
-
843
- /**
844
- * History.isTraditionalAnchor
845
- * Checks to see if the url is a traditional anchor or not
846
- * @param {String} url_or_hash
847
- * @return {Boolean}
848
- */
849
- History.isTraditionalAnchor = function(url_or_hash){
850
- // Check
851
- var isTraditional = !(/[\/\?\.]/.test(url_or_hash));
852
-
853
- // Return
854
- return isTraditional;
855
- };
856
-
857
- /**
858
- * History.extractState
859
- * Get a State by it's URL or Hash
860
- * @param {String} url_or_hash
861
- * @return {State|null}
862
- */
863
- History.extractState = function(url_or_hash,create){
864
- // Prepare
865
- var State = null, id, url;
866
- create = create||false;
867
-
868
- // Fetch SUID
869
- id = History.extractId(url_or_hash);
870
- if ( id ) {
871
- State = History.getStateById(id);
872
- }
873
-
874
- // Fetch SUID returned no State
875
- if ( !State ) {
876
- // Fetch URL
877
- url = History.getFullUrl(url_or_hash);
878
-
879
- // Check URL
880
- id = History.getIdByUrl(url)||false;
881
- if ( id ) {
882
- State = History.getStateById(id);
883
- }
884
-
885
- // Create State
886
- if ( !State && create && !History.isTraditionalAnchor(url_or_hash) ) {
887
- State = History.createStateObject(null,null,url);
888
- }
889
- }
890
-
891
- // Return
892
- return State;
893
- };
894
-
895
- /**
896
- * History.getIdByUrl()
897
- * Get a State ID by a State URL
898
- */
899
- History.getIdByUrl = function(url){
900
- // Fetch
901
- var id = History.urlToId[url] || History.store.urlToId[url] || undefined;
902
-
903
- // Return
904
- return id;
905
- };
906
-
907
- /**
908
- * History.getLastSavedState()
909
- * Get an object containing the data, title and url of the current state
910
- * @return {Object} State
911
- */
912
- History.getLastSavedState = function(){
913
- return History.savedStates[History.savedStates.length-1]||undefined;
914
- };
915
-
916
- /**
917
- * History.getLastStoredState()
918
- * Get an object containing the data, title and url of the current state
919
- * @return {Object} State
920
- */
921
- History.getLastStoredState = function(){
922
- return History.storedStates[History.storedStates.length-1]||undefined;
923
- };
924
-
925
- /**
926
- * History.hasUrlDuplicate
927
- * Checks if a Url will have a url conflict
928
- * @param {Object} newState
929
- * @return {Boolean} hasDuplicate
930
- */
931
- History.hasUrlDuplicate = function(newState) {
932
- // Prepare
933
- var hasDuplicate = false,
934
- oldState;
935
-
936
- // Fetch
937
- oldState = History.extractState(newState.url);
938
-
939
- // Check
940
- hasDuplicate = oldState && oldState.id !== newState.id;
941
-
942
- // Return
943
- return hasDuplicate;
944
- };
945
-
946
- /**
947
- * History.storeState
948
- * Store a State
949
- * @param {Object} newState
950
- * @return {Object} newState
951
- */
952
- History.storeState = function(newState){
953
- // Store the State
954
- History.urlToId[newState.url] = newState.id;
955
-
956
- // Push the State
957
- History.storedStates.push(History.cloneObject(newState));
958
-
959
- // Return newState
960
- return newState;
961
- };
962
-
963
- /**
964
- * History.isLastSavedState(newState)
965
- * Tests to see if the state is the last state
966
- * @param {Object} newState
967
- * @return {boolean} isLast
968
- */
969
- History.isLastSavedState = function(newState){
970
- // Prepare
971
- var isLast = false,
972
- newId, oldState, oldId;
973
-
974
- // Check
975
- if ( History.savedStates.length ) {
976
- newId = newState.id;
977
- oldState = History.getLastSavedState();
978
- oldId = oldState.id;
979
-
980
- // Check
981
- isLast = (newId === oldId);
982
- }
983
-
984
- // Return
985
- return isLast;
986
- };
987
-
988
- /**
989
- * History.saveState
990
- * Push a State
991
- * @param {Object} newState
992
- * @return {boolean} changed
993
- */
994
- History.saveState = function(newState){
995
- // Check Hash
996
- if ( History.isLastSavedState(newState) ) {
997
- return false;
998
- }
999
-
1000
- // Push the State
1001
- History.savedStates.push(History.cloneObject(newState));
1002
-
1003
- // Return true
1004
- return true;
1005
- };
1006
-
1007
- /**
1008
- * History.getStateByIndex()
1009
- * Gets a state by the index
1010
- * @param {integer} index
1011
- * @return {Object}
1012
- */
1013
- History.getStateByIndex = function(index){
1014
- // Prepare
1015
- var State = null;
1016
-
1017
- // Handle
1018
- if ( typeof index === 'undefined' ) {
1019
- // Get the last inserted
1020
- State = History.savedStates[History.savedStates.length-1];
1021
- }
1022
- else if ( index < 0 ) {
1023
- // Get from the end
1024
- State = History.savedStates[History.savedStates.length+index];
1025
- }
1026
- else {
1027
- // Get from the beginning
1028
- State = History.savedStates[index];
1029
- }
1030
-
1031
- // Return State
1032
- return State;
1033
- };
1034
-
1035
-
1036
- // ====================================================================
1037
- // Hash Helpers
1038
-
1039
- /**
1040
- * History.getHash()
1041
- * Gets the current document hash
1042
- * @return {string}
1043
- */
1044
- History.getHash = function(){
1045
- var hash = History.unescapeHash(document.location.hash);
1046
- return hash;
1047
- };
1048
-
1049
- /**
1050
- * History.unescapeString()
1051
- * Unescape a string
1052
- * @param {String} str
1053
- * @return {string}
1054
- */
1055
- History.unescapeString = function(str){
1056
- // Prepare
1057
- var result = str,
1058
- tmp;
1059
-
1060
- // Unescape hash
1061
- while ( true ) {
1062
- tmp = window.decodeURI(result);
1063
- if ( tmp === result ) {
1064
- break;
1065
- }
1066
- result = tmp;
1067
- }
1068
-
1069
- // Return result
1070
- return result;
1071
- };
1072
-
1073
- /**
1074
- * History.unescapeHash()
1075
- * normalize and Unescape a Hash
1076
- * @param {String} hash
1077
- * @return {string}
1078
- */
1079
- History.unescapeHash = function(hash){
1080
- // Prepare
1081
- var result = History.normalizeHash(hash);
1082
-
1083
- // Unescape hash
1084
- result = History.unescapeString(result);
1085
-
1086
- // Return result
1087
- return result;
1088
- };
1089
-
1090
- /**
1091
- * History.normalizeHash()
1092
- * normalize a hash across browsers
1093
- * @return {string}
1094
- */
1095
- History.normalizeHash = function(hash){
1096
- // Prepare
1097
- var result = hash.replace(/[^#]*#/,'').replace(/#!*/, '');
1098
-
1099
- // Return result
1100
- return result;
1101
- };
1102
-
1103
- /**
1104
- * History.setHash(hash)
1105
- * Sets the document hash
1106
- * @param {string} hash
1107
- * @return {History}
1108
- */
1109
- History.setHash = function(hash,queue){
1110
- // Prepare
1111
- var adjustedHash, State, pageUrl;
1112
-
1113
- // Handle Queueing
1114
- if ( queue !== false && History.busy() ) {
1115
- // Wait + Push to Queue
1116
- //History.debug('History.setHash: we must wait', arguments);
1117
- History.pushQueue({
1118
- scope: History,
1119
- callback: History.setHash,
1120
- args: arguments,
1121
- queue: queue
1122
- });
1123
- return false;
1124
- }
1125
-
1126
- // Log
1127
- //History.debug('History.setHash: called',hash);
1128
-
1129
- // Prepare
1130
- adjustedHash = History.escapeHash(hash);
1131
-
1132
- // Make Busy + Continue
1133
- History.busy(true);
1134
-
1135
- // Check if hash is a state
1136
- State = History.extractState(hash,true);
1137
- if ( State && !History.emulated.pushState ) {
1138
- // Hash is a state so skip the setHash
1139
- //History.debug('History.setHash: Hash is a state so skipping the hash set with a direct pushState call',arguments);
1140
-
1141
- // PushState
1142
- History.pushState(State.data,State.title,State.url,false);
1143
- }
1144
- else if ( document.location.hash !== adjustedHash ) {
1145
- // Hash is a proper hash, so apply it
1146
-
1147
- // Handle browser bugs
1148
- if ( History.bugs.setHash ) {
1149
- // Fix Safari Bug https://bugs.webkit.org/show_bug.cgi?id=56249
1150
-
1151
- // Fetch the base page
1152
- pageUrl = History.getPageUrl();
1153
-
1154
- // Safari hash apply
1155
- History.pushState(null,null,pageUrl+'#'+adjustedHash,false);
1156
- }
1157
- else {
1158
- // Normal hash apply
1159
- document.location.hash = adjustedHash;
1160
- }
1161
- }
1162
-
1163
- // Chain
1164
- return History;
1165
- };
1166
-
1167
- /**
1168
- * History.escape()
1169
- * normalize and Escape a Hash
1170
- * @return {string}
1171
- */
1172
- History.escapeHash = function(hash){
1173
- // Prepare
1174
- var result = History.normalizeHash(hash);
1175
-
1176
- // Escape hash
1177
- result = window.encodeURI(result);
1178
-
1179
- // IE6 Escape Bug
1180
- if ( !History.bugs.hashEscape ) {
1181
- // Restore common parts
1182
- result = result
1183
- .replace(/\%21/g,'!')
1184
- .replace(/\%26/g,'&')
1185
- .replace(/\%3D/g,'=')
1186
- .replace(/\%3F/g,'?');
1187
- }
1188
-
1189
- // Return result
1190
- return result;
1191
- };
1192
-
1193
- /**
1194
- * History.getHashByUrl(url)
1195
- * Extracts the Hash from a URL
1196
- * @param {string} url
1197
- * @return {string} url
1198
- */
1199
- History.getHashByUrl = function(url){
1200
- // Extract the hash
1201
- var hash = String(url)
1202
- .replace(/([^#]*)#?([^#]*)#?(.*)/, '$2')
1203
- ;
1204
-
1205
- // Unescape hash
1206
- hash = History.unescapeHash(hash);
1207
-
1208
- // Return hash
1209
- return hash;
1210
- };
1211
-
1212
- /**
1213
- * History.setTitle(title)
1214
- * Applies the title to the document
1215
- * @param {State} newState
1216
- * @return {Boolean}
1217
- */
1218
- History.setTitle = function(newState){
1219
- // Prepare
1220
- var title = newState.title,
1221
- firstState;
1222
-
1223
- // Initial
1224
- if ( !title ) {
1225
- firstState = History.getStateByIndex(0);
1226
- if ( firstState && firstState.url === newState.url ) {
1227
- title = firstState.title||History.options.initialTitle;
1228
- }
1229
- }
1230
-
1231
- // Apply
1232
- try {
1233
- document.getElementsByTagName('title')[0].innerHTML = title.replace('<','&lt;').replace('>','&gt;').replace(' & ',' &amp; ');
1234
- }
1235
- catch ( Exception ) { }
1236
- document.title = title;
1237
-
1238
- // Chain
1239
- return History;
1240
- };
1241
-
1242
-
1243
- // ====================================================================
1244
- // Queueing
1245
-
1246
- /**
1247
- * History.queues
1248
- * The list of queues to use
1249
- * First In, First Out
1250
- */
1251
- History.queues = [];
1252
-
1253
- /**
1254
- * History.busy(value)
1255
- * @param {boolean} value [optional]
1256
- * @return {boolean} busy
1257
- */
1258
- History.busy = function(value){
1259
- // Apply
1260
- if ( typeof value !== 'undefined' ) {
1261
- //History.debug('History.busy: changing ['+(History.busy.flag||false)+'] to ['+(value||false)+']', History.queues.length);
1262
- History.busy.flag = value;
1263
- }
1264
- // Default
1265
- else if ( typeof History.busy.flag === 'undefined' ) {
1266
- History.busy.flag = false;
1267
- }
1268
-
1269
- // Queue
1270
- if ( !History.busy.flag ) {
1271
- // Execute the next item in the queue
1272
- clearTimeout(History.busy.timeout);
1273
- var fireNext = function(){
1274
- var i, queue, item;
1275
- if ( History.busy.flag ) return;
1276
- for ( i=History.queues.length-1; i >= 0; --i ) {
1277
- queue = History.queues[i];
1278
- if ( queue.length === 0 ) continue;
1279
- item = queue.shift();
1280
- History.fireQueueItem(item);
1281
- History.busy.timeout = setTimeout(fireNext,History.options.busyDelay);
1282
- }
1283
- };
1284
- History.busy.timeout = setTimeout(fireNext,History.options.busyDelay);
1285
- }
1286
-
1287
- // Return
1288
- return History.busy.flag;
1289
- };
1290
-
1291
- /**
1292
- * History.busy.flag
1293
- */
1294
- History.busy.flag = false;
1295
-
1296
- /**
1297
- * History.fireQueueItem(item)
1298
- * Fire a Queue Item
1299
- * @param {Object} item
1300
- * @return {Mixed} result
1301
- */
1302
- History.fireQueueItem = function(item){
1303
- return item.callback.apply(item.scope||History,item.args||[]);
1304
- };
1305
-
1306
- /**
1307
- * History.pushQueue(callback,args)
1308
- * Add an item to the queue
1309
- * @param {Object} item [scope,callback,args,queue]
1310
- */
1311
- History.pushQueue = function(item){
1312
- // Prepare the queue
1313
- History.queues[item.queue||0] = History.queues[item.queue||0]||[];
1314
-
1315
- // Add to the queue
1316
- History.queues[item.queue||0].push(item);
1317
-
1318
- // Chain
1319
- return History;
1320
- };
1321
-
1322
- /**
1323
- * History.queue (item,queue), (func,queue), (func), (item)
1324
- * Either firs the item now if not busy, or adds it to the queue
1325
- */
1326
- History.queue = function(item,queue){
1327
- // Prepare
1328
- if ( typeof item === 'function' ) {
1329
- item = {
1330
- callback: item
1331
- };
1332
- }
1333
- if ( typeof queue !== 'undefined' ) {
1334
- item.queue = queue;
1335
- }
1336
-
1337
- // Handle
1338
- if ( History.busy() ) {
1339
- History.pushQueue(item);
1340
- } else {
1341
- History.fireQueueItem(item);
1342
- }
1343
-
1344
- // Chain
1345
- return History;
1346
- };
1347
-
1348
- /**
1349
- * History.clearQueue()
1350
- * Clears the Queue
1351
- */
1352
- History.clearQueue = function(){
1353
- History.busy.flag = false;
1354
- History.queues = [];
1355
- return History;
1356
- };
1357
-
1358
-
1359
- // ====================================================================
1360
- // IE Bug Fix
1361
-
1362
- /**
1363
- * History.stateChanged
1364
- * States whether or not the state has changed since the last double check was initialised
1365
- */
1366
- History.stateChanged = false;
1367
-
1368
- /**
1369
- * History.doubleChecker
1370
- * Contains the timeout used for the double checks
1371
- */
1372
- History.doubleChecker = false;
1373
-
1374
- /**
1375
- * History.doubleCheckComplete()
1376
- * Complete a double check
1377
- * @return {History}
1378
- */
1379
- History.doubleCheckComplete = function(){
1380
- // Update
1381
- History.stateChanged = true;
1382
-
1383
- // Clear
1384
- History.doubleCheckClear();
1385
-
1386
- // Chain
1387
- return History;
1388
- };
1389
-
1390
- /**
1391
- * History.doubleCheckClear()
1392
- * Clear a double check
1393
- * @return {History}
1394
- */
1395
- History.doubleCheckClear = function(){
1396
- // Clear
1397
- if ( History.doubleChecker ) {
1398
- clearTimeout(History.doubleChecker);
1399
- History.doubleChecker = false;
1400
- }
1401
-
1402
- // Chain
1403
- return History;
1404
- };
1405
-
1406
- /**
1407
- * History.doubleCheck()
1408
- * Create a double check
1409
- * @return {History}
1410
- */
1411
- History.doubleCheck = function(tryAgain){
1412
- // Reset
1413
- History.stateChanged = false;
1414
- History.doubleCheckClear();
1415
-
1416
- // Fix IE6,IE7 bug where calling history.back or history.forward does not actually change the hash (whereas doing it manually does)
1417
- // Fix Safari 5 bug where sometimes the state does not change: https://bugs.webkit.org/show_bug.cgi?id=42940
1418
- if ( History.bugs.ieDoubleCheck ) {
1419
- // Apply Check
1420
- History.doubleChecker = setTimeout(
1421
- function(){
1422
- History.doubleCheckClear();
1423
- if ( !History.stateChanged ) {
1424
- //History.debug('History.doubleCheck: State has not yet changed, trying again', arguments);
1425
- // Re-Attempt
1426
- tryAgain();
1427
- }
1428
- return true;
1429
- },
1430
- History.options.doubleCheckInterval
1431
- );
1432
- }
1433
-
1434
- // Chain
1435
- return History;
1436
- };
1437
-
1438
-
1439
- // ====================================================================
1440
- // Safari Bug Fix
1441
-
1442
- /**
1443
- * History.safariStatePoll()
1444
- * Poll the current state
1445
- * @return {History}
1446
- */
1447
- History.safariStatePoll = function(){
1448
- // Poll the URL
1449
-
1450
- // Get the Last State which has the new URL
1451
- var
1452
- urlState = History.extractState(document.location.href),
1453
- newState;
1454
-
1455
- // Check for a difference
1456
- if ( !History.isLastSavedState(urlState) ) {
1457
- newState = urlState;
1458
- }
1459
- else {
1460
- return;
1461
- }
1462
-
1463
- // Check if we have a state with that url
1464
- // If not create it
1465
- if ( !newState ) {
1466
- //History.debug('History.safariStatePoll: new');
1467
- newState = History.createStateObject();
1468
- }
1469
-
1470
- // Apply the New State
1471
- //History.debug('History.safariStatePoll: trigger');
1472
- History.Adapter.trigger(window,'popstate');
1473
-
1474
- // Chain
1475
- return History;
1476
- };
1477
-
1478
-
1479
- // ====================================================================
1480
- // State Aliases
1481
-
1482
- /**
1483
- * History.back(queue)
1484
- * Send the browser history back one item
1485
- * @param {Integer} queue [optional]
1486
- */
1487
- History.back = function(queue){
1488
- //History.debug('History.back: called', arguments);
1489
-
1490
- // Handle Queueing
1491
- if ( queue !== false && History.busy() ) {
1492
- // Wait + Push to Queue
1493
- //History.debug('History.back: we must wait', arguments);
1494
- History.pushQueue({
1495
- scope: History,
1496
- callback: History.back,
1497
- args: arguments,
1498
- queue: queue
1499
- });
1500
- return false;
1501
- }
1502
-
1503
- // Make Busy + Continue
1504
- History.busy(true);
1505
-
1506
- // Fix certain browser bugs that prevent the state from changing
1507
- History.doubleCheck(function(){
1508
- History.back(false);
1509
- });
1510
-
1511
- // Go back
1512
- history.go(-1);
1513
-
1514
- // End back closure
1515
- return true;
1516
- };
1517
-
1518
- /**
1519
- * History.forward(queue)
1520
- * Send the browser history forward one item
1521
- * @param {Integer} queue [optional]
1522
- */
1523
- History.forward = function(queue){
1524
- //History.debug('History.forward: called', arguments);
1525
-
1526
- // Handle Queueing
1527
- if ( queue !== false && History.busy() ) {
1528
- // Wait + Push to Queue
1529
- //History.debug('History.forward: we must wait', arguments);
1530
- History.pushQueue({
1531
- scope: History,
1532
- callback: History.forward,
1533
- args: arguments,
1534
- queue: queue
1535
- });
1536
- return false;
1537
- }
1538
-
1539
- // Make Busy + Continue
1540
- History.busy(true);
1541
-
1542
- // Fix certain browser bugs that prevent the state from changing
1543
- History.doubleCheck(function(){
1544
- History.forward(false);
1545
- });
1546
-
1547
- // Go forward
1548
- history.go(1);
1549
-
1550
- // End forward closure
1551
- return true;
1552
- };
1553
-
1554
- /**
1555
- * History.go(index,queue)
1556
- * Send the browser history back or forward index times
1557
- * @param {Integer} queue [optional]
1558
- */
1559
- History.go = function(index,queue){
1560
- //History.debug('History.go: called', arguments);
1561
-
1562
- // Prepare
1563
- var i;
1564
-
1565
- // Handle
1566
- if ( index > 0 ) {
1567
- // Forward
1568
- for ( i=1; i<=index; ++i ) {
1569
- History.forward(queue);
1570
- }
1571
- }
1572
- else if ( index < 0 ) {
1573
- // Backward
1574
- for ( i=-1; i>=index; --i ) {
1575
- History.back(queue);
1576
- }
1577
- }
1578
- else {
1579
- throw new Error('History.go: History.go requires a positive or negative integer passed.');
1580
- }
1581
-
1582
- // Chain
1583
- return History;
1584
- };
1585
-
1586
-
1587
- // ====================================================================
1588
- // HTML5 State Support
1589
-
1590
- // Non-Native pushState Implementation
1591
- if ( History.emulated.pushState ) {
1592
- /*
1593
- * Provide Skeleton for HTML4 Browsers
1594
- */
1595
-
1596
- // Prepare
1597
- var emptyFunction = function(){};
1598
- History.pushState = History.pushState||emptyFunction;
1599
- History.replaceState = History.replaceState||emptyFunction;
1600
- } // History.emulated.pushState
1601
-
1602
- // Native pushState Implementation
1603
- else {
1604
- /*
1605
- * Use native HTML5 History API Implementation
1606
- */
1607
-
1608
- /**
1609
- * History.onPopState(event,extra)
1610
- * Refresh the Current State
1611
- */
1612
- History.onPopState = function(event,extra){
1613
- // Prepare
1614
- var stateId = false, newState = false, currentHash, currentState;
1615
-
1616
- // Reset the double check
1617
- History.doubleCheckComplete();
1618
-
1619
- // Check for a Hash, and handle apporiatly
1620
- currentHash = History.getHash();
1621
- if ( currentHash ) {
1622
- // Expand Hash
1623
- currentState = History.extractState(currentHash||document.location.href,true);
1624
- if ( currentState ) {
1625
- // We were able to parse it, it must be a State!
1626
- // Let's forward to replaceState
1627
- //History.debug('History.onPopState: state anchor', currentHash, currentState);
1628
- History.replaceState(currentState.data, currentState.title, currentState.url, false);
1629
- }
1630
- else {
1631
- // Traditional Anchor
1632
- //History.debug('History.onPopState: traditional anchor', currentHash);
1633
- History.Adapter.trigger(window,'anchorchange');
1634
- History.busy(false);
1635
- }
1636
-
1637
- // We don't care for hashes
1638
- History.expectedStateId = false;
1639
- return false;
1640
- }
1641
-
1642
- // Ensure
1643
- stateId = History.Adapter.extractEventData('state',event,extra) || false;
1644
-
1645
- // Fetch State
1646
- if ( stateId ) {
1647
- // Vanilla: Back/forward button was used
1648
- newState = History.getStateById(stateId);
1649
- }
1650
- else if ( History.expectedStateId ) {
1651
- // Vanilla: A new state was pushed, and popstate was called manually
1652
- newState = History.getStateById(History.expectedStateId);
1653
- }
1654
- else {
1655
- // Initial State
1656
- newState = History.extractState(document.location.href);
1657
- }
1658
-
1659
- // The State did not exist in our store
1660
- if ( !newState ) {
1661
- // Regenerate the State
1662
- newState = History.createStateObject(null,null,document.location.href);
1663
- }
1664
-
1665
- // Clean
1666
- History.expectedStateId = false;
1667
-
1668
- // Check if we are the same state
1669
- if ( History.isLastSavedState(newState) ) {
1670
- // There has been no change (just the page's hash has finally propagated)
1671
- //History.debug('History.onPopState: no change', newState, History.savedStates);
1672
- History.busy(false);
1673
- return false;
1674
- }
1675
-
1676
- // Store the State
1677
- History.storeState(newState);
1678
- History.saveState(newState);
1679
-
1680
- // Force update of the title
1681
- History.setTitle(newState);
1682
-
1683
- // Fire Our Event
1684
- History.Adapter.trigger(window,'statechange');
1685
- History.busy(false);
1686
-
1687
- // Return true
1688
- return true;
1689
- };
1690
- History.Adapter.bind(window,'popstate',History.onPopState);
1691
-
1692
- /**
1693
- * History.pushState(data,title,url)
1694
- * Add a new State to the history object, become it, and trigger onpopstate
1695
- * We have to trigger for HTML4 compatibility
1696
- * @param {object} data
1697
- * @param {string} title
1698
- * @param {string} url
1699
- * @return {true}
1700
- */
1701
- History.pushState = function(data,title,url,queue){
1702
- //History.debug('History.pushState: called', arguments);
1703
-
1704
- // Check the State
1705
- if ( History.getHashByUrl(url) && History.emulated.pushState ) {
1706
- throw new Error('History.js does not support states with fragement-identifiers (hashes/anchors).');
1707
- }
1708
-
1709
- // Handle Queueing
1710
- if ( queue !== false && History.busy() ) {
1711
- // Wait + Push to Queue
1712
- //History.debug('History.pushState: we must wait', arguments);
1713
- History.pushQueue({
1714
- scope: History,
1715
- callback: History.pushState,
1716
- args: arguments,
1717
- queue: queue
1718
- });
1719
- return false;
1720
- }
1721
-
1722
- // Make Busy + Continue
1723
- History.busy(true);
1724
-
1725
- // Create the newState
1726
- var newState = History.createStateObject(data,title,url);
1727
-
1728
- // Check it
1729
- if ( History.isLastSavedState(newState) ) {
1730
- // Won't be a change
1731
- History.busy(false);
1732
- }
1733
- else {
1734
- // Store the newState
1735
- History.storeState(newState);
1736
- History.expectedStateId = newState.id;
1737
-
1738
- // Push the newState
1739
- history.pushState(newState.id,newState.title,newState.url);
1740
-
1741
- // Fire HTML5 Event
1742
- History.Adapter.trigger(window,'popstate');
1743
- }
1744
-
1745
- // End pushState closure
1746
- return true;
1747
- };
1748
-
1749
- /**
1750
- * History.replaceState(data,title,url)
1751
- * Replace the State and trigger onpopstate
1752
- * We have to trigger for HTML4 compatibility
1753
- * @param {object} data
1754
- * @param {string} title
1755
- * @param {string} url
1756
- * @return {true}
1757
- */
1758
- History.replaceState = function(data,title,url,queue){
1759
- //History.debug('History.replaceState: called', arguments);
1760
-
1761
- // Check the State
1762
- if ( History.getHashByUrl(url) && History.emulated.pushState ) {
1763
- throw new Error('History.js does not support states with fragement-identifiers (hashes/anchors).');
1764
- }
1765
-
1766
- // Handle Queueing
1767
- if ( queue !== false && History.busy() ) {
1768
- // Wait + Push to Queue
1769
- //History.debug('History.replaceState: we must wait', arguments);
1770
- History.pushQueue({
1771
- scope: History,
1772
- callback: History.replaceState,
1773
- args: arguments,
1774
- queue: queue
1775
- });
1776
- return false;
1777
- }
1778
-
1779
- // Make Busy + Continue
1780
- History.busy(true);
1781
-
1782
- // Create the newState
1783
- var newState = History.createStateObject(data,title,url);
1784
-
1785
- // Check it
1786
- if ( History.isLastSavedState(newState) ) {
1787
- // Won't be a change
1788
- History.busy(false);
1789
- }
1790
- else {
1791
- // Store the newState
1792
- History.storeState(newState);
1793
- History.expectedStateId = newState.id;
1794
-
1795
- // Push the newState
1796
- history.replaceState(newState.id,newState.title,newState.url);
1797
-
1798
- // Fire HTML5 Event
1799
- History.Adapter.trigger(window,'popstate');
1800
- }
1801
-
1802
- // End replaceState closure
1803
- return true;
1804
- };
1805
-
1806
- } // !History.emulated.pushState
1807
-
1808
-
1809
- // ====================================================================
1810
- // Initialise
1811
-
1812
- /**
1813
- * Load the Store
1814
- */
1815
- if ( sessionStorage ) {
1816
- // Fetch
1817
- try {
1818
- History.store = JSON.parse(sessionStorage.getItem('History.store'))||{};
1819
- }
1820
- catch ( err ) {
1821
- History.store = {};
1822
- }
1823
-
1824
- // Normalize
1825
- History.normalizeStore();
1826
- }
1827
- else {
1828
- // Default Load
1829
- History.store = {};
1830
- History.normalizeStore();
1831
- }
1832
-
1833
- /**
1834
- * Clear Intervals on exit to prevent memory leaks
1835
- */
1836
- History.Adapter.bind(window,"beforeunload",History.clearAllIntervals);
1837
- History.Adapter.bind(window,"unload",History.clearAllIntervals);
1838
-
1839
- /**
1840
- * Create the initial State
1841
- */
1842
- History.saveState(History.storeState(History.extractState(document.location.href,true)));
1843
-
1844
- /**
1845
- * Bind for Saving Store
1846
- */
1847
- if ( sessionStorage ) {
1848
- // When the page is closed
1849
- History.onUnload = function(){
1850
- // Prepare
1851
- var currentStore, item;
1852
-
1853
- // Fetch
1854
- try {
1855
- currentStore = JSON.parse(sessionStorage.getItem('History.store'))||{};
1856
- }
1857
- catch ( err ) {
1858
- currentStore = {};
1859
- }
1860
-
1861
- // Ensure
1862
- currentStore.idToState = currentStore.idToState || {};
1863
- currentStore.urlToId = currentStore.urlToId || {};
1864
- currentStore.stateToId = currentStore.stateToId || {};
1865
-
1866
- // Sync
1867
- for ( item in History.idToState ) {
1868
- if ( !History.idToState.hasOwnProperty(item) ) {
1869
- continue;
1870
- }
1871
- currentStore.idToState[item] = History.idToState[item];
1872
- }
1873
- for ( item in History.urlToId ) {
1874
- if ( !History.urlToId.hasOwnProperty(item) ) {
1875
- continue;
1876
- }
1877
- currentStore.urlToId[item] = History.urlToId[item];
1878
- }
1879
- for ( item in History.stateToId ) {
1880
- if ( !History.stateToId.hasOwnProperty(item) ) {
1881
- continue;
1882
- }
1883
- currentStore.stateToId[item] = History.stateToId[item];
1884
- }
1885
-
1886
- // Update
1887
- History.store = currentStore;
1888
- History.normalizeStore();
1889
-
1890
- // Store
1891
- sessionStorage.setItem('History.store',JSON.stringify(currentStore));
1892
- };
1893
-
1894
- // For Internet Explorer
1895
- History.intervalList.push(setInterval(History.onUnload,History.options.storeInterval));
1896
-
1897
- // For Other Browsers
1898
- History.Adapter.bind(window,'beforeunload',History.onUnload);
1899
- History.Adapter.bind(window,'unload',History.onUnload);
1900
-
1901
- // Both are enabled for consistency
1902
- }
1903
-
1904
- // Non-Native pushState Implementation
1905
- if ( !History.emulated.pushState ) {
1906
- // Be aware, the following is only for native pushState implementations
1907
- // If you are wanting to include something for all browsers
1908
- // Then include it above this if block
1909
-
1910
- /**
1911
- * Setup Safari Fix
1912
- */
1913
- if ( History.bugs.safariPoll ) {
1914
- History.intervalList.push(setInterval(History.safariStatePoll, History.options.safariPollInterval));
1915
- }
1916
-
1917
- /**
1918
- * Ensure Cross Browser Compatibility
1919
- */
1920
- if ( navigator.vendor === 'Apple Computer, Inc.' || (navigator.appCodeName||'') === 'Mozilla' ) {
1921
- /**
1922
- * Fix Safari HashChange Issue
1923
- */
1924
-
1925
- // Setup Alias
1926
- History.Adapter.bind(window,'hashchange',function(){
1927
- History.Adapter.trigger(window,'popstate');
1928
- });
1929
-
1930
- // Initialise Alias
1931
- if ( History.getHash() ) {
1932
- History.Adapter.onDomLoad(function(){
1933
- History.Adapter.trigger(window,'hashchange');
1934
- });
1935
- }
1936
- }
1937
-
1938
- } // !History.emulated.pushState
1939
-
1940
-
1941
- }; // History.initCore
1942
-
1943
- // Try and Initialise History
1944
- History.init();
1945
-
1946
- })(window);