ajax_pagination 0.6.2 → 0.6.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,4 +0,0 @@
1
- //= require json2
2
- //= require history_core
3
- //= require history_adapter_jquery
4
- //= require history_html4
@@ -1,77 +0,0 @@
1
- /**
2
- * History.js jQuery Adapter
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
- // Closure
9
- (function(window,undefined){
10
- "use strict";
11
-
12
- // Localise Globals
13
- var
14
- History = window.History = window.History||{},
15
- jQuery = window.jQuery;
16
-
17
- // Check Existence
18
- if ( typeof History.Adapter !== 'undefined' ) {
19
- throw new Error('History.js Adapter has already been loaded...');
20
- }
21
-
22
- // Add the Adapter
23
- History.Adapter = {
24
- /**
25
- * History.Adapter.bind(el,event,callback)
26
- * @param {Element|string} el
27
- * @param {string} event - custom and standard events
28
- * @param {function} callback
29
- * @return {void}
30
- */
31
- bind: function(el,event,callback){
32
- jQuery(el).bind(event,callback);
33
- },
34
-
35
- /**
36
- * History.Adapter.trigger(el,event)
37
- * @param {Element|string} el
38
- * @param {string} event - custom and standard events
39
- * @param {Object=} extra - a object of extra event data (optional)
40
- * @return {void}
41
- */
42
- trigger: function(el,event,extra){
43
- jQuery(el).trigger(event,extra);
44
- },
45
-
46
- /**
47
- * History.Adapter.extractEventData(key,event,extra)
48
- * @param {string} key - key for the event data to extract
49
- * @param {string} event - custom and standard events
50
- * @param {Object=} extra - a object of extra event data (optional)
51
- * @return {mixed}
52
- */
53
- extractEventData: function(key,event,extra){
54
- // jQuery Native then jQuery Custom
55
- var result = (event && event.originalEvent && event.originalEvent[key]) || (extra && extra[key]) || undefined;
56
-
57
- // Return
58
- return result;
59
- },
60
-
61
- /**
62
- * History.Adapter.onDomLoad(callback)
63
- * @param {function} callback
64
- * @return {void}
65
- */
66
- onDomLoad: function(callback) {
67
- jQuery(callback);
68
- }
69
- };
70
-
71
- // Try and Initialise History
72
- if ( typeof History.init !== 'undefined' ) {
73
- History.init();
74
- }
75
-
76
- })(window);
77
-
@@ -1,1943 +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
- }
517
-
518
- // Clean It
519
- shortUrl = shortUrl.replace(/^(\.\/)+/g,'./').replace(/\#$/,'');
520
-
521
- // Return
522
- return shortUrl;
523
- };
524
-
525
-
526
- // ====================================================================
527
- // State Storage
528
-
529
- /**
530
- * History.store
531
- * The store for all session specific data
532
- */
533
- History.store = {};
534
-
535
- /**
536
- * History.idToState
537
- * 1-1: State ID to State Object
538
- */
539
- History.idToState = History.idToState||{};
540
-
541
- /**
542
- * History.stateToId
543
- * 1-1: State String to State ID
544
- */
545
- History.stateToId = History.stateToId||{};
546
-
547
- /**
548
- * History.urlToId
549
- * 1-1: State URL to State ID
550
- */
551
- History.urlToId = History.urlToId||{};
552
-
553
- /**
554
- * History.storedStates
555
- * Store the states in an array
556
- */
557
- History.storedStates = History.storedStates||[];
558
-
559
- /**
560
- * History.savedStates
561
- * Saved the states in an array
562
- */
563
- History.savedStates = History.savedStates||[];
564
-
565
- /**
566
- * History.noramlizeStore()
567
- * Noramlize the store by adding necessary values
568
- */
569
- History.normalizeStore = function(){
570
- History.store.idToState = History.store.idToState||{};
571
- History.store.urlToId = History.store.urlToId||{};
572
- History.store.stateToId = History.store.stateToId||{};
573
- };
574
-
575
- /**
576
- * History.getState()
577
- * Get an object containing the data, title and url of the current state
578
- * @param {Boolean} friendly
579
- * @param {Boolean} create
580
- * @return {Object} State
581
- */
582
- History.getState = function(friendly,create){
583
- // Prepare
584
- if ( typeof friendly === 'undefined' ) { friendly = true; }
585
- if ( typeof create === 'undefined' ) { create = true; }
586
-
587
- // Fetch
588
- var State = History.getLastSavedState();
589
-
590
- // Create
591
- if ( !State && create ) {
592
- State = History.createStateObject();
593
- }
594
-
595
- // Adjust
596
- if ( friendly ) {
597
- State = History.cloneObject(State);
598
- State.url = State.cleanUrl||State.url;
599
- }
600
-
601
- // Return
602
- return State;
603
- };
604
-
605
- /**
606
- * History.getIdByState(State)
607
- * Gets a ID for a State
608
- * @param {State} newState
609
- * @return {String} id
610
- */
611
- History.getIdByState = function(newState){
612
-
613
- // Fetch ID
614
- var id = History.extractId(newState.url),
615
- str;
616
-
617
- if ( !id ) {
618
- // Find ID via State String
619
- str = History.getStateString(newState);
620
- if ( typeof History.stateToId[str] !== 'undefined' ) {
621
- id = History.stateToId[str];
622
- }
623
- else if ( typeof History.store.stateToId[str] !== 'undefined' ) {
624
- id = History.store.stateToId[str];
625
- }
626
- else {
627
- // Generate a new ID
628
- while ( true ) {
629
- id = (new Date()).getTime() + String(Math.random()).replace(/\D/g,'');
630
- if ( typeof History.idToState[id] === 'undefined' && typeof History.store.idToState[id] === 'undefined' ) {
631
- break;
632
- }
633
- }
634
-
635
- // Apply the new State to the ID
636
- History.stateToId[str] = id;
637
- History.idToState[id] = newState;
638
- }
639
- }
640
-
641
- // Return ID
642
- return id;
643
- };
644
-
645
- /**
646
- * History.normalizeState(State)
647
- * Expands a State Object
648
- * @param {object} State
649
- * @return {object}
650
- */
651
- History.normalizeState = function(oldState){
652
- // Variables
653
- var newState, dataNotEmpty;
654
-
655
- // Prepare
656
- if ( !oldState || (typeof oldState !== 'object') ) {
657
- oldState = {};
658
- }
659
-
660
- // Check
661
- if ( typeof oldState.normalized !== 'undefined' ) {
662
- return oldState;
663
- }
664
-
665
- // Adjust
666
- if ( !oldState.data || (typeof oldState.data !== 'object') ) {
667
- oldState.data = {};
668
- }
669
-
670
- // ----------------------------------------------------------------
671
-
672
- // Create
673
- newState = {};
674
- newState.normalized = true;
675
- newState.title = oldState.title||'';
676
- newState.url = History.getFullUrl(History.unescapeString(oldState.url||document.location.href));
677
- newState.hash = History.getShortUrl(newState.url);
678
- newState.data = History.cloneObject(oldState.data);
679
-
680
- // Fetch ID
681
- newState.id = History.getIdByState(newState);
682
-
683
- // ----------------------------------------------------------------
684
-
685
- // Clean the URL
686
- newState.cleanUrl = newState.url.replace(/\??\&_suid.*/,'');
687
- newState.url = newState.cleanUrl;
688
-
689
- // Check to see if we have more than just a url
690
- dataNotEmpty = !History.isEmptyObject(newState.data);
691
-
692
- // Apply
693
- if ( newState.title || dataNotEmpty ) {
694
- // Add ID to Hash
695
- newState.hash = History.getShortUrl(newState.url).replace(/\??\&_suid.*/,'');
696
- if ( !/\?/.test(newState.hash) ) {
697
- newState.hash += '?';
698
- }
699
- newState.hash += '&_suid='+newState.id;
700
- }
701
-
702
- // Create the Hashed URL
703
- newState.hashedUrl = History.getFullUrl(newState.hash);
704
-
705
- // ----------------------------------------------------------------
706
-
707
- // Update the URL if we have a duplicate
708
- if ( (History.emulated.pushState || History.bugs.safariPoll) && History.hasUrlDuplicate(newState) ) {
709
- newState.url = newState.hashedUrl;
710
- }
711
-
712
- // ----------------------------------------------------------------
713
-
714
- // Return
715
- return newState;
716
- };
717
-
718
- /**
719
- * History.createStateObject(data,title,url)
720
- * Creates a object based on the data, title and url state params
721
- * @param {object} data
722
- * @param {string} title
723
- * @param {string} url
724
- * @return {object}
725
- */
726
- History.createStateObject = function(data,title,url){
727
- // Hashify
728
- var State = {
729
- 'data': data,
730
- 'title': title,
731
- 'url': url
732
- };
733
-
734
- // Expand the State
735
- State = History.normalizeState(State);
736
-
737
- // Return object
738
- return State;
739
- };
740
-
741
- /**
742
- * History.getStateById(id)
743
- * Get a state by it's UID
744
- * @param {String} id
745
- */
746
- History.getStateById = function(id){
747
- // Prepare
748
- id = String(id);
749
-
750
- // Retrieve
751
- var State = History.idToState[id] || History.store.idToState[id] || undefined;
752
-
753
- // Return State
754
- return State;
755
- };
756
-
757
- /**
758
- * Get a State's String
759
- * @param {State} passedState
760
- */
761
- History.getStateString = function(passedState){
762
- // Prepare
763
- var State, cleanedState, str;
764
-
765
- // Fetch
766
- State = History.normalizeState(passedState);
767
-
768
- // Clean
769
- cleanedState = {
770
- data: State.data,
771
- title: passedState.title,
772
- url: passedState.url
773
- };
774
-
775
- // Fetch
776
- str = JSON.stringify(cleanedState);
777
-
778
- // Return
779
- return str;
780
- };
781
-
782
- /**
783
- * Get a State's ID
784
- * @param {State} passedState
785
- * @return {String} id
786
- */
787
- History.getStateId = function(passedState){
788
- // Prepare
789
- var State, id;
790
-
791
- // Fetch
792
- State = History.normalizeState(passedState);
793
-
794
- // Fetch
795
- id = State.id;
796
-
797
- // Return
798
- return id;
799
- };
800
-
801
- /**
802
- * History.getHashByState(State)
803
- * Creates a Hash for the State Object
804
- * @param {State} passedState
805
- * @return {String} hash
806
- */
807
- History.getHashByState = function(passedState){
808
- // Prepare
809
- var State, hash;
810
-
811
- // Fetch
812
- State = History.normalizeState(passedState);
813
-
814
- // Hash
815
- hash = State.hash;
816
-
817
- // Return
818
- return hash;
819
- };
820
-
821
- /**
822
- * History.extractId(url_or_hash)
823
- * Get a State ID by it's URL or Hash
824
- * @param {string} url_or_hash
825
- * @return {string} id
826
- */
827
- History.extractId = function ( url_or_hash ) {
828
- // Prepare
829
- var id,parts,url;
830
-
831
- // Extract
832
- parts = /(.*)\&_suid=([0-9]+)$/.exec(url_or_hash);
833
- url = parts ? (parts[1]||url_or_hash) : url_or_hash;
834
- id = parts ? String(parts[2]||'') : '';
835
-
836
- // Return
837
- return id||false;
838
- };
839
-
840
- /**
841
- * History.isTraditionalAnchor
842
- * Checks to see if the url is a traditional anchor or not
843
- * @param {String} url_or_hash
844
- * @return {Boolean}
845
- */
846
- History.isTraditionalAnchor = function(url_or_hash){
847
- // Check
848
- var isTraditional = !(/[\/\?\.]/.test(url_or_hash));
849
-
850
- // Return
851
- return isTraditional;
852
- };
853
-
854
- /**
855
- * History.extractState
856
- * Get a State by it's URL or Hash
857
- * @param {String} url_or_hash
858
- * @return {State|null}
859
- */
860
- History.extractState = function(url_or_hash,create){
861
- // Prepare
862
- var State = null, id, url;
863
- create = create||false;
864
-
865
- // Fetch SUID
866
- id = History.extractId(url_or_hash);
867
- if ( id ) {
868
- State = History.getStateById(id);
869
- }
870
-
871
- // Fetch SUID returned no State
872
- if ( !State ) {
873
- // Fetch URL
874
- url = History.getFullUrl(url_or_hash);
875
-
876
- // Check URL
877
- id = History.getIdByUrl(url)||false;
878
- if ( id ) {
879
- State = History.getStateById(id);
880
- }
881
-
882
- // Create State
883
- if ( !State && create && !History.isTraditionalAnchor(url_or_hash) ) {
884
- State = History.createStateObject(null,null,url);
885
- }
886
- }
887
-
888
- // Return
889
- return State;
890
- };
891
-
892
- /**
893
- * History.getIdByUrl()
894
- * Get a State ID by a State URL
895
- */
896
- History.getIdByUrl = function(url){
897
- // Fetch
898
- var id = History.urlToId[url] || History.store.urlToId[url] || undefined;
899
-
900
- // Return
901
- return id;
902
- };
903
-
904
- /**
905
- * History.getLastSavedState()
906
- * Get an object containing the data, title and url of the current state
907
- * @return {Object} State
908
- */
909
- History.getLastSavedState = function(){
910
- return History.savedStates[History.savedStates.length-1]||undefined;
911
- };
912
-
913
- /**
914
- * History.getLastStoredState()
915
- * Get an object containing the data, title and url of the current state
916
- * @return {Object} State
917
- */
918
- History.getLastStoredState = function(){
919
- return History.storedStates[History.storedStates.length-1]||undefined;
920
- };
921
-
922
- /**
923
- * History.hasUrlDuplicate
924
- * Checks if a Url will have a url conflict
925
- * @param {Object} newState
926
- * @return {Boolean} hasDuplicate
927
- */
928
- History.hasUrlDuplicate = function(newState) {
929
- // Prepare
930
- var hasDuplicate = false,
931
- oldState;
932
-
933
- // Fetch
934
- oldState = History.extractState(newState.url);
935
-
936
- // Check
937
- hasDuplicate = oldState && oldState.id !== newState.id;
938
-
939
- // Return
940
- return hasDuplicate;
941
- };
942
-
943
- /**
944
- * History.storeState
945
- * Store a State
946
- * @param {Object} newState
947
- * @return {Object} newState
948
- */
949
- History.storeState = function(newState){
950
- // Store the State
951
- History.urlToId[newState.url] = newState.id;
952
-
953
- // Push the State
954
- History.storedStates.push(History.cloneObject(newState));
955
-
956
- // Return newState
957
- return newState;
958
- };
959
-
960
- /**
961
- * History.isLastSavedState(newState)
962
- * Tests to see if the state is the last state
963
- * @param {Object} newState
964
- * @return {boolean} isLast
965
- */
966
- History.isLastSavedState = function(newState){
967
- // Prepare
968
- var isLast = false,
969
- newId, oldState, oldId;
970
-
971
- // Check
972
- if ( History.savedStates.length ) {
973
- newId = newState.id;
974
- oldState = History.getLastSavedState();
975
- oldId = oldState.id;
976
-
977
- // Check
978
- isLast = (newId === oldId);
979
- }
980
-
981
- // Return
982
- return isLast;
983
- };
984
-
985
- /**
986
- * History.saveState
987
- * Push a State
988
- * @param {Object} newState
989
- * @return {boolean} changed
990
- */
991
- History.saveState = function(newState){
992
- // Check Hash
993
- if ( History.isLastSavedState(newState) ) {
994
- return false;
995
- }
996
-
997
- // Push the State
998
- History.savedStates.push(History.cloneObject(newState));
999
-
1000
- // Return true
1001
- return true;
1002
- };
1003
-
1004
- /**
1005
- * History.getStateByIndex()
1006
- * Gets a state by the index
1007
- * @param {integer} index
1008
- * @return {Object}
1009
- */
1010
- History.getStateByIndex = function(index){
1011
- // Prepare
1012
- var State = null;
1013
-
1014
- // Handle
1015
- if ( typeof index === 'undefined' ) {
1016
- // Get the last inserted
1017
- State = History.savedStates[History.savedStates.length-1];
1018
- }
1019
- else if ( index < 0 ) {
1020
- // Get from the end
1021
- State = History.savedStates[History.savedStates.length+index];
1022
- }
1023
- else {
1024
- // Get from the beginning
1025
- State = History.savedStates[index];
1026
- }
1027
-
1028
- // Return State
1029
- return State;
1030
- };
1031
-
1032
-
1033
- // ====================================================================
1034
- // Hash Helpers
1035
-
1036
- /**
1037
- * History.getHash()
1038
- * Gets the current document hash
1039
- * @return {string}
1040
- */
1041
- History.getHash = function(){
1042
- var hash = History.unescapeHash(document.location.hash);
1043
- return hash;
1044
- };
1045
-
1046
- /**
1047
- * History.unescapeString()
1048
- * Unescape a string
1049
- * @param {String} str
1050
- * @return {string}
1051
- */
1052
- History.unescapeString = function(str){
1053
- // Prepare
1054
- var result = str,
1055
- tmp;
1056
-
1057
- // Unescape hash
1058
- while ( true ) {
1059
- tmp = window.unescape(result);
1060
- if ( tmp === result ) {
1061
- break;
1062
- }
1063
- result = tmp;
1064
- }
1065
-
1066
- // Return result
1067
- return result;
1068
- };
1069
-
1070
- /**
1071
- * History.unescapeHash()
1072
- * normalize and Unescape a Hash
1073
- * @param {String} hash
1074
- * @return {string}
1075
- */
1076
- History.unescapeHash = function(hash){
1077
- // Prepare
1078
- var result = History.normalizeHash(hash);
1079
-
1080
- // Unescape hash
1081
- result = History.unescapeString(result);
1082
-
1083
- // Return result
1084
- return result;
1085
- };
1086
-
1087
- /**
1088
- * History.normalizeHash()
1089
- * normalize a hash across browsers
1090
- * @return {string}
1091
- */
1092
- History.normalizeHash = function(hash){
1093
- // Prepare
1094
- var result = hash.replace(/[^#]*#/,'').replace(/#.*/, '');
1095
-
1096
- // Return result
1097
- return result;
1098
- };
1099
-
1100
- /**
1101
- * History.setHash(hash)
1102
- * Sets the document hash
1103
- * @param {string} hash
1104
- * @return {History}
1105
- */
1106
- History.setHash = function(hash,queue){
1107
- // Prepare
1108
- var adjustedHash, State, pageUrl;
1109
-
1110
- // Handle Queueing
1111
- if ( queue !== false && History.busy() ) {
1112
- // Wait + Push to Queue
1113
- //History.debug('History.setHash: we must wait', arguments);
1114
- History.pushQueue({
1115
- scope: History,
1116
- callback: History.setHash,
1117
- args: arguments,
1118
- queue: queue
1119
- });
1120
- return false;
1121
- }
1122
-
1123
- // Log
1124
- //History.debug('History.setHash: called',hash);
1125
-
1126
- // Prepare
1127
- adjustedHash = History.escapeHash(hash);
1128
-
1129
- // Make Busy + Continue
1130
- History.busy(true);
1131
-
1132
- // Check if hash is a state
1133
- State = History.extractState(hash,true);
1134
- if ( State && !History.emulated.pushState ) {
1135
- // Hash is a state so skip the setHash
1136
- //History.debug('History.setHash: Hash is a state so skipping the hash set with a direct pushState call',arguments);
1137
-
1138
- // PushState
1139
- History.pushState(State.data,State.title,State.url,false);
1140
- }
1141
- else if ( document.location.hash !== adjustedHash ) {
1142
- // Hash is a proper hash, so apply it
1143
-
1144
- // Handle browser bugs
1145
- if ( History.bugs.setHash ) {
1146
- // Fix Safari Bug https://bugs.webkit.org/show_bug.cgi?id=56249
1147
-
1148
- // Fetch the base page
1149
- pageUrl = History.getPageUrl();
1150
-
1151
- // Safari hash apply
1152
- History.pushState(null,null,pageUrl+'#'+adjustedHash,false);
1153
- }
1154
- else {
1155
- // Normal hash apply
1156
- document.location.hash = adjustedHash;
1157
- }
1158
- }
1159
-
1160
- // Chain
1161
- return History;
1162
- };
1163
-
1164
- /**
1165
- * History.escape()
1166
- * normalize and Escape a Hash
1167
- * @return {string}
1168
- */
1169
- History.escapeHash = function(hash){
1170
- // Prepare
1171
- var result = History.normalizeHash(hash);
1172
-
1173
- // Escape hash
1174
- result = window.escape(result);
1175
-
1176
- // IE6 Escape Bug
1177
- if ( !History.bugs.hashEscape ) {
1178
- // Restore common parts
1179
- result = result
1180
- .replace(/\%21/g,'!')
1181
- .replace(/\%26/g,'&')
1182
- .replace(/\%3D/g,'=')
1183
- .replace(/\%3F/g,'?');
1184
- }
1185
-
1186
- // Return result
1187
- return result;
1188
- };
1189
-
1190
- /**
1191
- * History.getHashByUrl(url)
1192
- * Extracts the Hash from a URL
1193
- * @param {string} url
1194
- * @return {string} url
1195
- */
1196
- History.getHashByUrl = function(url){
1197
- // Extract the hash
1198
- var hash = String(url)
1199
- .replace(/([^#]*)#?([^#]*)#?(.*)/, '$2')
1200
- ;
1201
-
1202
- // Unescape hash
1203
- hash = History.unescapeHash(hash);
1204
-
1205
- // Return hash
1206
- return hash;
1207
- };
1208
-
1209
- /**
1210
- * History.setTitle(title)
1211
- * Applies the title to the document
1212
- * @param {State} newState
1213
- * @return {Boolean}
1214
- */
1215
- History.setTitle = function(newState){
1216
- // Prepare
1217
- var title = newState.title,
1218
- firstState;
1219
-
1220
- // Initial
1221
- if ( !title ) {
1222
- firstState = History.getStateByIndex(0);
1223
- if ( firstState && firstState.url === newState.url ) {
1224
- title = firstState.title||History.options.initialTitle;
1225
- }
1226
- }
1227
-
1228
- // Apply
1229
- try {
1230
- document.getElementsByTagName('title')[0].innerHTML = title.replace('<','&lt;').replace('>','&gt;').replace(' & ',' &amp; ');
1231
- }
1232
- catch ( Exception ) { }
1233
- document.title = title;
1234
-
1235
- // Chain
1236
- return History;
1237
- };
1238
-
1239
-
1240
- // ====================================================================
1241
- // Queueing
1242
-
1243
- /**
1244
- * History.queues
1245
- * The list of queues to use
1246
- * First In, First Out
1247
- */
1248
- History.queues = [];
1249
-
1250
- /**
1251
- * History.busy(value)
1252
- * @param {boolean} value [optional]
1253
- * @return {boolean} busy
1254
- */
1255
- History.busy = function(value){
1256
- // Apply
1257
- if ( typeof value !== 'undefined' ) {
1258
- //History.debug('History.busy: changing ['+(History.busy.flag||false)+'] to ['+(value||false)+']', History.queues.length);
1259
- History.busy.flag = value;
1260
- }
1261
- // Default
1262
- else if ( typeof History.busy.flag === 'undefined' ) {
1263
- History.busy.flag = false;
1264
- }
1265
-
1266
- // Queue
1267
- if ( !History.busy.flag ) {
1268
- // Execute the next item in the queue
1269
- clearTimeout(History.busy.timeout);
1270
- var fireNext = function(){
1271
- var i, queue, item;
1272
- if ( History.busy.flag ) return;
1273
- for ( i=History.queues.length-1; i >= 0; --i ) {
1274
- queue = History.queues[i];
1275
- if ( queue.length === 0 ) continue;
1276
- item = queue.shift();
1277
- History.fireQueueItem(item);
1278
- History.busy.timeout = setTimeout(fireNext,History.options.busyDelay);
1279
- }
1280
- };
1281
- History.busy.timeout = setTimeout(fireNext,History.options.busyDelay);
1282
- }
1283
-
1284
- // Return
1285
- return History.busy.flag;
1286
- };
1287
-
1288
- /**
1289
- * History.busy.flag
1290
- */
1291
- History.busy.flag = false;
1292
-
1293
- /**
1294
- * History.fireQueueItem(item)
1295
- * Fire a Queue Item
1296
- * @param {Object} item
1297
- * @return {Mixed} result
1298
- */
1299
- History.fireQueueItem = function(item){
1300
- return item.callback.apply(item.scope||History,item.args||[]);
1301
- };
1302
-
1303
- /**
1304
- * History.pushQueue(callback,args)
1305
- * Add an item to the queue
1306
- * @param {Object} item [scope,callback,args,queue]
1307
- */
1308
- History.pushQueue = function(item){
1309
- // Prepare the queue
1310
- History.queues[item.queue||0] = History.queues[item.queue||0]||[];
1311
-
1312
- // Add to the queue
1313
- History.queues[item.queue||0].push(item);
1314
-
1315
- // Chain
1316
- return History;
1317
- };
1318
-
1319
- /**
1320
- * History.queue (item,queue), (func,queue), (func), (item)
1321
- * Either firs the item now if not busy, or adds it to the queue
1322
- */
1323
- History.queue = function(item,queue){
1324
- // Prepare
1325
- if ( typeof item === 'function' ) {
1326
- item = {
1327
- callback: item
1328
- };
1329
- }
1330
- if ( typeof queue !== 'undefined' ) {
1331
- item.queue = queue;
1332
- }
1333
-
1334
- // Handle
1335
- if ( History.busy() ) {
1336
- History.pushQueue(item);
1337
- } else {
1338
- History.fireQueueItem(item);
1339
- }
1340
-
1341
- // Chain
1342
- return History;
1343
- };
1344
-
1345
- /**
1346
- * History.clearQueue()
1347
- * Clears the Queue
1348
- */
1349
- History.clearQueue = function(){
1350
- History.busy.flag = false;
1351
- History.queues = [];
1352
- return History;
1353
- };
1354
-
1355
-
1356
- // ====================================================================
1357
- // IE Bug Fix
1358
-
1359
- /**
1360
- * History.stateChanged
1361
- * States whether or not the state has changed since the last double check was initialised
1362
- */
1363
- History.stateChanged = false;
1364
-
1365
- /**
1366
- * History.doubleChecker
1367
- * Contains the timeout used for the double checks
1368
- */
1369
- History.doubleChecker = false;
1370
-
1371
- /**
1372
- * History.doubleCheckComplete()
1373
- * Complete a double check
1374
- * @return {History}
1375
- */
1376
- History.doubleCheckComplete = function(){
1377
- // Update
1378
- History.stateChanged = true;
1379
-
1380
- // Clear
1381
- History.doubleCheckClear();
1382
-
1383
- // Chain
1384
- return History;
1385
- };
1386
-
1387
- /**
1388
- * History.doubleCheckClear()
1389
- * Clear a double check
1390
- * @return {History}
1391
- */
1392
- History.doubleCheckClear = function(){
1393
- // Clear
1394
- if ( History.doubleChecker ) {
1395
- clearTimeout(History.doubleChecker);
1396
- History.doubleChecker = false;
1397
- }
1398
-
1399
- // Chain
1400
- return History;
1401
- };
1402
-
1403
- /**
1404
- * History.doubleCheck()
1405
- * Create a double check
1406
- * @return {History}
1407
- */
1408
- History.doubleCheck = function(tryAgain){
1409
- // Reset
1410
- History.stateChanged = false;
1411
- History.doubleCheckClear();
1412
-
1413
- // Fix IE6,IE7 bug where calling history.back or history.forward does not actually change the hash (whereas doing it manually does)
1414
- // Fix Safari 5 bug where sometimes the state does not change: https://bugs.webkit.org/show_bug.cgi?id=42940
1415
- if ( History.bugs.ieDoubleCheck ) {
1416
- // Apply Check
1417
- History.doubleChecker = setTimeout(
1418
- function(){
1419
- History.doubleCheckClear();
1420
- if ( !History.stateChanged ) {
1421
- //History.debug('History.doubleCheck: State has not yet changed, trying again', arguments);
1422
- // Re-Attempt
1423
- tryAgain();
1424
- }
1425
- return true;
1426
- },
1427
- History.options.doubleCheckInterval
1428
- );
1429
- }
1430
-
1431
- // Chain
1432
- return History;
1433
- };
1434
-
1435
-
1436
- // ====================================================================
1437
- // Safari Bug Fix
1438
-
1439
- /**
1440
- * History.safariStatePoll()
1441
- * Poll the current state
1442
- * @return {History}
1443
- */
1444
- History.safariStatePoll = function(){
1445
- // Poll the URL
1446
-
1447
- // Get the Last State which has the new URL
1448
- var
1449
- urlState = History.extractState(document.location.href),
1450
- newState;
1451
-
1452
- // Check for a difference
1453
- if ( !History.isLastSavedState(urlState) ) {
1454
- newState = urlState;
1455
- }
1456
- else {
1457
- return;
1458
- }
1459
-
1460
- // Check if we have a state with that url
1461
- // If not create it
1462
- if ( !newState ) {
1463
- //History.debug('History.safariStatePoll: new');
1464
- newState = History.createStateObject();
1465
- }
1466
-
1467
- // Apply the New State
1468
- //History.debug('History.safariStatePoll: trigger');
1469
- History.Adapter.trigger(window,'popstate');
1470
-
1471
- // Chain
1472
- return History;
1473
- };
1474
-
1475
-
1476
- // ====================================================================
1477
- // State Aliases
1478
-
1479
- /**
1480
- * History.back(queue)
1481
- * Send the browser history back one item
1482
- * @param {Integer} queue [optional]
1483
- */
1484
- History.back = function(queue){
1485
- //History.debug('History.back: called', arguments);
1486
-
1487
- // Handle Queueing
1488
- if ( queue !== false && History.busy() ) {
1489
- // Wait + Push to Queue
1490
- //History.debug('History.back: we must wait', arguments);
1491
- History.pushQueue({
1492
- scope: History,
1493
- callback: History.back,
1494
- args: arguments,
1495
- queue: queue
1496
- });
1497
- return false;
1498
- }
1499
-
1500
- // Make Busy + Continue
1501
- History.busy(true);
1502
-
1503
- // Fix certain browser bugs that prevent the state from changing
1504
- History.doubleCheck(function(){
1505
- History.back(false);
1506
- });
1507
-
1508
- // Go back
1509
- history.go(-1);
1510
-
1511
- // End back closure
1512
- return true;
1513
- };
1514
-
1515
- /**
1516
- * History.forward(queue)
1517
- * Send the browser history forward one item
1518
- * @param {Integer} queue [optional]
1519
- */
1520
- History.forward = function(queue){
1521
- //History.debug('History.forward: called', arguments);
1522
-
1523
- // Handle Queueing
1524
- if ( queue !== false && History.busy() ) {
1525
- // Wait + Push to Queue
1526
- //History.debug('History.forward: we must wait', arguments);
1527
- History.pushQueue({
1528
- scope: History,
1529
- callback: History.forward,
1530
- args: arguments,
1531
- queue: queue
1532
- });
1533
- return false;
1534
- }
1535
-
1536
- // Make Busy + Continue
1537
- History.busy(true);
1538
-
1539
- // Fix certain browser bugs that prevent the state from changing
1540
- History.doubleCheck(function(){
1541
- History.forward(false);
1542
- });
1543
-
1544
- // Go forward
1545
- history.go(1);
1546
-
1547
- // End forward closure
1548
- return true;
1549
- };
1550
-
1551
- /**
1552
- * History.go(index,queue)
1553
- * Send the browser history back or forward index times
1554
- * @param {Integer} queue [optional]
1555
- */
1556
- History.go = function(index,queue){
1557
- //History.debug('History.go: called', arguments);
1558
-
1559
- // Prepare
1560
- var i;
1561
-
1562
- // Handle
1563
- if ( index > 0 ) {
1564
- // Forward
1565
- for ( i=1; i<=index; ++i ) {
1566
- History.forward(queue);
1567
- }
1568
- }
1569
- else if ( index < 0 ) {
1570
- // Backward
1571
- for ( i=-1; i>=index; --i ) {
1572
- History.back(queue);
1573
- }
1574
- }
1575
- else {
1576
- throw new Error('History.go: History.go requires a positive or negative integer passed.');
1577
- }
1578
-
1579
- // Chain
1580
- return History;
1581
- };
1582
-
1583
-
1584
- // ====================================================================
1585
- // HTML5 State Support
1586
-
1587
- // Non-Native pushState Implementation
1588
- if ( History.emulated.pushState ) {
1589
- /*
1590
- * Provide Skeleton for HTML4 Browsers
1591
- */
1592
-
1593
- // Prepare
1594
- var emptyFunction = function(){};
1595
- History.pushState = History.pushState||emptyFunction;
1596
- History.replaceState = History.replaceState||emptyFunction;
1597
- } // History.emulated.pushState
1598
-
1599
- // Native pushState Implementation
1600
- else {
1601
- /*
1602
- * Use native HTML5 History API Implementation
1603
- */
1604
-
1605
- /**
1606
- * History.onPopState(event,extra)
1607
- * Refresh the Current State
1608
- */
1609
- History.onPopState = function(event,extra){
1610
- // Prepare
1611
- var stateId = false, newState = false, currentHash, currentState;
1612
-
1613
- // Reset the double check
1614
- History.doubleCheckComplete();
1615
-
1616
- // Check for a Hash, and handle apporiatly
1617
- currentHash = History.getHash();
1618
- if ( currentHash ) {
1619
- // Expand Hash
1620
- currentState = History.extractState(currentHash||document.location.href,true);
1621
- if ( currentState ) {
1622
- // We were able to parse it, it must be a State!
1623
- // Let's forward to replaceState
1624
- //History.debug('History.onPopState: state anchor', currentHash, currentState);
1625
- History.replaceState(currentState.data, currentState.title, currentState.url, false);
1626
- }
1627
- else {
1628
- // Traditional Anchor
1629
- //History.debug('History.onPopState: traditional anchor', currentHash);
1630
- History.Adapter.trigger(window,'anchorchange');
1631
- History.busy(false);
1632
- }
1633
-
1634
- // We don't care for hashes
1635
- History.expectedStateId = false;
1636
- return false;
1637
- }
1638
-
1639
- // Ensure
1640
- stateId = History.Adapter.extractEventData('state',event,extra) || false;
1641
-
1642
- // Fetch State
1643
- if ( stateId ) {
1644
- // Vanilla: Back/forward button was used
1645
- newState = History.getStateById(stateId);
1646
- }
1647
- else if ( History.expectedStateId ) {
1648
- // Vanilla: A new state was pushed, and popstate was called manually
1649
- newState = History.getStateById(History.expectedStateId);
1650
- }
1651
- else {
1652
- // Initial State
1653
- newState = History.extractState(document.location.href);
1654
- }
1655
-
1656
- // The State did not exist in our store
1657
- if ( !newState ) {
1658
- // Regenerate the State
1659
- newState = History.createStateObject(null,null,document.location.href);
1660
- }
1661
-
1662
- // Clean
1663
- History.expectedStateId = false;
1664
-
1665
- // Check if we are the same state
1666
- if ( History.isLastSavedState(newState) ) {
1667
- // There has been no change (just the page's hash has finally propagated)
1668
- //History.debug('History.onPopState: no change', newState, History.savedStates);
1669
- History.busy(false);
1670
- return false;
1671
- }
1672
-
1673
- // Store the State
1674
- History.storeState(newState);
1675
- History.saveState(newState);
1676
-
1677
- // Force update of the title
1678
- History.setTitle(newState);
1679
-
1680
- // Fire Our Event
1681
- History.Adapter.trigger(window,'statechange');
1682
- History.busy(false);
1683
-
1684
- // Return true
1685
- return true;
1686
- };
1687
- History.Adapter.bind(window,'popstate',History.onPopState);
1688
-
1689
- /**
1690
- * History.pushState(data,title,url)
1691
- * Add a new State to the history object, become it, and trigger onpopstate
1692
- * We have to trigger for HTML4 compatibility
1693
- * @param {object} data
1694
- * @param {string} title
1695
- * @param {string} url
1696
- * @return {true}
1697
- */
1698
- History.pushState = function(data,title,url,queue){
1699
- //History.debug('History.pushState: called', arguments);
1700
-
1701
- // Check the State
1702
- if ( History.getHashByUrl(url) && History.emulated.pushState ) {
1703
- throw new Error('History.js does not support states with fragement-identifiers (hashes/anchors).');
1704
- }
1705
-
1706
- // Handle Queueing
1707
- if ( queue !== false && History.busy() ) {
1708
- // Wait + Push to Queue
1709
- //History.debug('History.pushState: we must wait', arguments);
1710
- History.pushQueue({
1711
- scope: History,
1712
- callback: History.pushState,
1713
- args: arguments,
1714
- queue: queue
1715
- });
1716
- return false;
1717
- }
1718
-
1719
- // Make Busy + Continue
1720
- History.busy(true);
1721
-
1722
- // Create the newState
1723
- var newState = History.createStateObject(data,title,url);
1724
-
1725
- // Check it
1726
- if ( History.isLastSavedState(newState) ) {
1727
- // Won't be a change
1728
- History.busy(false);
1729
- }
1730
- else {
1731
- // Store the newState
1732
- History.storeState(newState);
1733
- History.expectedStateId = newState.id;
1734
-
1735
- // Push the newState
1736
- history.pushState(newState.id,newState.title,newState.url);
1737
-
1738
- // Fire HTML5 Event
1739
- History.Adapter.trigger(window,'popstate');
1740
- }
1741
-
1742
- // End pushState closure
1743
- return true;
1744
- };
1745
-
1746
- /**
1747
- * History.replaceState(data,title,url)
1748
- * Replace the State and trigger onpopstate
1749
- * We have to trigger for HTML4 compatibility
1750
- * @param {object} data
1751
- * @param {string} title
1752
- * @param {string} url
1753
- * @return {true}
1754
- */
1755
- History.replaceState = function(data,title,url,queue){
1756
- //History.debug('History.replaceState: called', arguments);
1757
-
1758
- // Check the State
1759
- if ( History.getHashByUrl(url) && History.emulated.pushState ) {
1760
- throw new Error('History.js does not support states with fragement-identifiers (hashes/anchors).');
1761
- }
1762
-
1763
- // Handle Queueing
1764
- if ( queue !== false && History.busy() ) {
1765
- // Wait + Push to Queue
1766
- //History.debug('History.replaceState: we must wait', arguments);
1767
- History.pushQueue({
1768
- scope: History,
1769
- callback: History.replaceState,
1770
- args: arguments,
1771
- queue: queue
1772
- });
1773
- return false;
1774
- }
1775
-
1776
- // Make Busy + Continue
1777
- History.busy(true);
1778
-
1779
- // Create the newState
1780
- var newState = History.createStateObject(data,title,url);
1781
-
1782
- // Check it
1783
- if ( History.isLastSavedState(newState) ) {
1784
- // Won't be a change
1785
- History.busy(false);
1786
- }
1787
- else {
1788
- // Store the newState
1789
- History.storeState(newState);
1790
- History.expectedStateId = newState.id;
1791
-
1792
- // Push the newState
1793
- history.replaceState(newState.id,newState.title,newState.url);
1794
-
1795
- // Fire HTML5 Event
1796
- History.Adapter.trigger(window,'popstate');
1797
- }
1798
-
1799
- // End replaceState closure
1800
- return true;
1801
- };
1802
-
1803
- } // !History.emulated.pushState
1804
-
1805
-
1806
- // ====================================================================
1807
- // Initialise
1808
-
1809
- /**
1810
- * Load the Store
1811
- */
1812
- if ( sessionStorage ) {
1813
- // Fetch
1814
- try {
1815
- History.store = JSON.parse(sessionStorage.getItem('History.store'))||{};
1816
- }
1817
- catch ( err ) {
1818
- History.store = {};
1819
- }
1820
-
1821
- // Normalize
1822
- History.normalizeStore();
1823
- }
1824
- else {
1825
- // Default Load
1826
- History.store = {};
1827
- History.normalizeStore();
1828
- }
1829
-
1830
- /**
1831
- * Clear Intervals on exit to prevent memory leaks
1832
- */
1833
- History.Adapter.bind(window,"beforeunload",History.clearAllIntervals);
1834
- History.Adapter.bind(window,"unload",History.clearAllIntervals);
1835
-
1836
- /**
1837
- * Create the initial State
1838
- */
1839
- History.saveState(History.storeState(History.extractState(document.location.href,true)));
1840
-
1841
- /**
1842
- * Bind for Saving Store
1843
- */
1844
- if ( sessionStorage ) {
1845
- // When the page is closed
1846
- History.onUnload = function(){
1847
- // Prepare
1848
- var currentStore, item;
1849
-
1850
- // Fetch
1851
- try {
1852
- currentStore = JSON.parse(sessionStorage.getItem('History.store'))||{};
1853
- }
1854
- catch ( err ) {
1855
- currentStore = {};
1856
- }
1857
-
1858
- // Ensure
1859
- currentStore.idToState = currentStore.idToState || {};
1860
- currentStore.urlToId = currentStore.urlToId || {};
1861
- currentStore.stateToId = currentStore.stateToId || {};
1862
-
1863
- // Sync
1864
- for ( item in History.idToState ) {
1865
- if ( !History.idToState.hasOwnProperty(item) ) {
1866
- continue;
1867
- }
1868
- currentStore.idToState[item] = History.idToState[item];
1869
- }
1870
- for ( item in History.urlToId ) {
1871
- if ( !History.urlToId.hasOwnProperty(item) ) {
1872
- continue;
1873
- }
1874
- currentStore.urlToId[item] = History.urlToId[item];
1875
- }
1876
- for ( item in History.stateToId ) {
1877
- if ( !History.stateToId.hasOwnProperty(item) ) {
1878
- continue;
1879
- }
1880
- currentStore.stateToId[item] = History.stateToId[item];
1881
- }
1882
-
1883
- // Update
1884
- History.store = currentStore;
1885
- History.normalizeStore();
1886
-
1887
- // Store
1888
- sessionStorage.setItem('History.store',JSON.stringify(currentStore));
1889
- };
1890
-
1891
- // For Internet Explorer
1892
- History.intervalList.push(setInterval(History.onUnload,History.options.storeInterval));
1893
-
1894
- // For Other Browsers
1895
- History.Adapter.bind(window,'beforeunload',History.onUnload);
1896
- History.Adapter.bind(window,'unload',History.onUnload);
1897
-
1898
- // Both are enabled for consistency
1899
- }
1900
-
1901
- // Non-Native pushState Implementation
1902
- if ( !History.emulated.pushState ) {
1903
- // Be aware, the following is only for native pushState implementations
1904
- // If you are wanting to include something for all browsers
1905
- // Then include it above this if block
1906
-
1907
- /**
1908
- * Setup Safari Fix
1909
- */
1910
- if ( History.bugs.safariPoll ) {
1911
- History.intervalList.push(setInterval(History.safariStatePoll, History.options.safariPollInterval));
1912
- }
1913
-
1914
- /**
1915
- * Ensure Cross Browser Compatibility
1916
- */
1917
- if ( navigator.vendor === 'Apple Computer, Inc.' || (navigator.appCodeName||'') === 'Mozilla' ) {
1918
- /**
1919
- * Fix Safari HashChange Issue
1920
- */
1921
-
1922
- // Setup Alias
1923
- History.Adapter.bind(window,'hashchange',function(){
1924
- History.Adapter.trigger(window,'popstate');
1925
- });
1926
-
1927
- // Initialise Alias
1928
- if ( History.getHash() ) {
1929
- History.Adapter.onDomLoad(function(){
1930
- History.Adapter.trigger(window,'hashchange');
1931
- });
1932
- }
1933
- }
1934
-
1935
- } // !History.emulated.pushState
1936
-
1937
-
1938
- }; // History.initCore
1939
-
1940
- // Try and Initialise History
1941
- History.init();
1942
-
1943
- })(window);