jquery-historyjs 0.2.3 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: f4534c2578530802db05295b2e2499f6c3260d8e
4
+ data.tar.gz: f6703c4de9968fd01c78ea774582b0179cb7e261
5
+ SHA512:
6
+ metadata.gz: 922e0f0c1f36c8ffd03152abad7a9241296fa318566e7e1471cd84ceb4e565676dca8924353e1906172501243ff1164e7f271845bbfe5f8943de69134ebe0e6e
7
+ data.tar.gz: c98fec73c3879ad64d8a9669535a0a4c8501e0dab76ae4a06ee5bb7c1c323036d5b71db9885a33b990999b04558a642176a410f2316ab03289f40c9e4c741582
@@ -1,7 +1,7 @@
1
1
  module Historyjs
2
2
  module Rails
3
- VERSION = "0.2.3"
4
- HISTORYJS_VERSION = "1.7.1-r2"
5
- JSON2_VERSION = "47a9882cdd"
3
+ VERSION = "0.3.0"
4
+ HISTORYJS_VERSION = "1.8.0-b2"
5
+ JSON2_VERSION = "e39db4b7e6"
6
6
  end
7
7
  end
@@ -26,6 +26,13 @@
26
26
  History = window.History = window.History||{}, // Public History Object
27
27
  history = window.history; // Old History Object
28
28
 
29
+ try {
30
+ sessionStorage.setItem('TEST', '1');
31
+ sessionStorage.removeItem('TEST');
32
+ } catch(e) {
33
+ sessionStorage = false;
34
+ }
35
+
29
36
  // MooTools Compatibility
30
37
  JSON.stringify = JSON.stringify||JSON.encode;
31
38
  JSON.parse = JSON.parse||JSON.decode;
@@ -36,7 +43,7 @@
36
43
  }
37
44
 
38
45
  // Initialise History
39
- History.init = function(){
46
+ History.init = function(options){
40
47
  // Check Load Status of Adapter
41
48
  if ( typeof History.Adapter === 'undefined' ) {
42
49
  return false;
@@ -61,7 +68,7 @@
61
68
  // Initialise Core
62
69
 
63
70
  // Initialise Core
64
- History.initCore = function(){
71
+ History.initCore = function(options){
65
72
  // Initialise
66
73
  if ( typeof History.initCore.initialized !== 'undefined' ) {
67
74
  // Already Loaded
@@ -99,6 +106,12 @@
99
106
  */
100
107
  History.options.doubleCheckInterval = History.options.doubleCheckInterval || 500;
101
108
 
109
+ /**
110
+ * History.options.disableSuid
111
+ * Force History not to append suid
112
+ */
113
+ History.options.disableSuid = History.options.disableSuid || false;
114
+
102
115
  /**
103
116
  * History.options.storeInterval
104
117
  * How long should we wait between store calls
@@ -123,6 +136,18 @@
123
136
  */
124
137
  History.options.initialTitle = History.options.initialTitle || document.title;
125
138
 
139
+ /**
140
+ * History.options.html4Mode
141
+ * If true, will force HTMl4 mode (hashtags)
142
+ */
143
+ History.options.html4Mode = History.options.html4Mode || false;
144
+
145
+ /**
146
+ * History.options.delayInit
147
+ * Want to override default options and call init manually.
148
+ */
149
+ History.options.delayInit = History.options.delayInit || false;
150
+
126
151
 
127
152
  // ====================================================================
128
153
  // Interval record
@@ -266,20 +291,31 @@
266
291
  * History.emulated
267
292
  * Which features require emulating?
268
293
  */
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 */
294
+
295
+ if (History.options.html4Mode) {
296
+ History.emulated = {
297
+ pushState : true,
298
+ hashChange: true
299
+ };
300
+ }
301
+
302
+ else {
303
+
304
+ History.emulated = {
305
+ pushState: !Boolean(
306
+ window.history && window.history.pushState && window.history.replaceState
307
+ && !(
308
+ (/ 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) */
309
+ || (/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 */
310
+ )
311
+ ),
312
+ hashChange: Boolean(
313
+ !(('onhashchange' in window) || ('onhashchange' in document))
314
+ ||
315
+ (History.isInternetExplorer() && History.getInternetExplorerMajorVersion() < 8)
275
316
  )
276
- ),
277
- hashChange: Boolean(
278
- !(('onhashchange' in window) || ('onhashchange' in document))
279
- ||
280
- (History.isInternetExplorer() && History.getInternetExplorerMajorVersion() < 8)
281
- )
282
- };
317
+ };
318
+ }
283
319
 
284
320
  /**
285
321
  * History.enabled
@@ -323,7 +359,9 @@
323
359
  */
324
360
  History.isEmptyObject = function(obj) {
325
361
  for ( var name in obj ) {
326
- return false;
362
+ if ( obj.hasOwnProperty(name) ) {
363
+ return false;
364
+ }
327
365
  }
328
366
  return true;
329
367
  };
@@ -416,7 +454,7 @@
416
454
  // Fetch
417
455
  var
418
456
  State = History.getState(false,false),
419
- stateUrl = (State||{}).url||document.location.href,
457
+ stateUrl = (State||{}).url||History.getLocationHref(),
420
458
  pageUrl;
421
459
 
422
460
  // Create
@@ -435,7 +473,7 @@
435
473
  */
436
474
  History.getBasePageUrl = function(){
437
475
  // Create
438
- var basePageUrl = document.location.href.replace(/[#\?].*/,'').replace(/[^\/]+$/,function(part,index,string){
476
+ var basePageUrl = (History.getLocationHref()).replace(/[#\?].*/,'').replace(/[^\/]+$/,function(part,index,string){
439
477
  return (/[^\/]$/).test(part) ? '' : part;
440
478
  }).replace(/\/+$/,'')+'/';
441
479
 
@@ -522,6 +560,39 @@
522
560
  return shortUrl;
523
561
  };
524
562
 
563
+ /**
564
+ * History.getLocationHref(document)
565
+ * Returns a normalized version of document.location.href
566
+ * accounting for browser inconsistencies, etc.
567
+ *
568
+ * This URL will be URI-encoded and will include the hash
569
+ *
570
+ * @param {object} document
571
+ * @return {string} url
572
+ */
573
+ History.getLocationHref = function(doc) {
574
+ doc = doc || document;
575
+
576
+ // most of the time, this will be true
577
+ if (doc.URL === doc.location.href)
578
+ return doc.location.href;
579
+
580
+ // some versions of webkit URI-decode document.location.href
581
+ // but they leave document.URL in an encoded state
582
+ if (doc.location.href === decodeURIComponent(doc.URL))
583
+ return doc.URL;
584
+
585
+ // FF 3.6 only updates document.URL when a page is reloaded
586
+ // document.location.href is updated correctly
587
+ if (doc.location.hash && decodeURIComponent(doc.location.href.replace(/^[^#]+/, "")) === doc.location.hash)
588
+ return doc.location.href;
589
+
590
+ if (doc.URL.indexOf('#') == -1 && doc.location.href.indexOf('#') != -1)
591
+ return doc.location.href;
592
+
593
+ return doc.URL || doc.location.href;
594
+ };
595
+
525
596
 
526
597
  // ====================================================================
527
598
  // State Storage
@@ -613,7 +684,7 @@
613
684
  // Fetch ID
614
685
  var id = History.extractId(newState.url),
615
686
  str;
616
-
687
+
617
688
  if ( !id ) {
618
689
  // Find ID via State String
619
690
  str = History.getStateString(newState);
@@ -673,7 +744,7 @@
673
744
  newState = {};
674
745
  newState.normalized = true;
675
746
  newState.title = oldState.title||'';
676
- newState.url = History.getFullUrl(History.unescapeString(oldState.url||document.location.href));
747
+ newState.url = History.getFullUrl(oldState.url?oldState.url:(History.getLocationHref()));
677
748
  newState.hash = History.getShortUrl(newState.url);
678
749
  newState.data = History.cloneObject(oldState.data);
679
750
 
@@ -690,7 +761,7 @@
690
761
  dataNotEmpty = !History.isEmptyObject(newState.data);
691
762
 
692
763
  // Apply
693
- if ( newState.title || dataNotEmpty ) {
764
+ if ( (newState.title || dataNotEmpty) && History.options.disableSuid !== true ) {
694
765
  // Add ID to Hash
695
766
  newState.hash = History.getShortUrl(newState.url).replace(/\??\&_suid.*/,'');
696
767
  if ( !/\?/.test(newState.hash) ) {
@@ -787,7 +858,7 @@
787
858
  History.getStateId = function(passedState){
788
859
  // Prepare
789
860
  var State, id;
790
-
861
+
791
862
  // Fetch
792
863
  State = History.normalizeState(passedState);
793
864
 
@@ -807,7 +878,7 @@
807
878
  History.getHashByState = function(passedState){
808
879
  // Prepare
809
880
  var State, hash;
810
-
881
+
811
882
  // Fetch
812
883
  State = History.normalizeState(passedState);
813
884
 
@@ -826,10 +897,21 @@
826
897
  */
827
898
  History.extractId = function ( url_or_hash ) {
828
899
  // Prepare
829
- var id,parts,url;
900
+ var id,parts,url, tmp;
830
901
 
831
902
  // Extract
832
- parts = /(.*)\&_suid=([0-9]+)$/.exec(url_or_hash);
903
+
904
+ // If the URL has a #, use the id from before the #
905
+ if (url_or_hash.indexOf('#') != -1)
906
+ {
907
+ tmp = url_or_hash.split("#")[0];
908
+ }
909
+ else
910
+ {
911
+ tmp = url_or_hash;
912
+ }
913
+
914
+ parts = /(.*)\&_suid=([0-9]+)$/.exec(tmp);
833
915
  url = parts ? (parts[1]||url_or_hash) : url_or_hash;
834
916
  id = parts ? String(parts[2]||'') : '';
835
917
 
@@ -1029,44 +1111,42 @@
1029
1111
  return State;
1030
1112
  };
1031
1113
 
1114
+ /**
1115
+ * History.getCurrentIndex()
1116
+ * Gets the current index
1117
+ * @return (integer)
1118
+ */
1119
+ History.getCurrentIndex = function(){
1120
+ // Prepare
1121
+ var index = null;
1122
+
1123
+ // No states saved
1124
+ if(History.savedStates.length < 1) {
1125
+ index = 0;
1126
+ }
1127
+ else {
1128
+ index = History.savedStates.length-1;
1129
+ }
1130
+ return index;
1131
+ };
1032
1132
 
1033
1133
  // ====================================================================
1034
1134
  // Hash Helpers
1035
1135
 
1036
1136
  /**
1037
1137
  * History.getHash()
1138
+ * @param {Location=} location
1038
1139
  * Gets the current document hash
1140
+ * Note: unlike location.hash, this is guaranteed to return the escaped hash in all browsers
1039
1141
  * @return {string}
1040
1142
  */
1041
- History.getHash = function(){
1042
- var hash = History.unescapeHash(document.location.hash);
1143
+ History.getHash = function(doc){
1144
+ var url = History.getLocationHref(doc),
1145
+ hash;
1146
+ hash = History.getHashByUrl(url);
1043
1147
  return hash;
1044
1148
  };
1045
1149
 
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
1150
  /**
1071
1151
  * History.unescapeHash()
1072
1152
  * normalize and Unescape a Hash
@@ -1078,7 +1158,7 @@
1078
1158
  var result = History.normalizeHash(hash);
1079
1159
 
1080
1160
  // Unescape hash
1081
- result = History.unescapeString(result);
1161
+ result = decodeURIComponent(result);
1082
1162
 
1083
1163
  // Return result
1084
1164
  return result;
@@ -1105,7 +1185,7 @@
1105
1185
  */
1106
1186
  History.setHash = function(hash,queue){
1107
1187
  // Prepare
1108
- var adjustedHash, State, pageUrl;
1188
+ var State, pageUrl;
1109
1189
 
1110
1190
  // Handle Queueing
1111
1191
  if ( queue !== false && History.busy() ) {
@@ -1123,9 +1203,6 @@
1123
1203
  // Log
1124
1204
  //History.debug('History.setHash: called',hash);
1125
1205
 
1126
- // Prepare
1127
- adjustedHash = History.escapeHash(hash);
1128
-
1129
1206
  // Make Busy + Continue
1130
1207
  History.busy(true);
1131
1208
 
@@ -1138,7 +1215,7 @@
1138
1215
  // PushState
1139
1216
  History.pushState(State.data,State.title,State.url,false);
1140
1217
  }
1141
- else if ( document.location.hash !== adjustedHash ) {
1218
+ else if ( History.getHash() !== hash ) {
1142
1219
  // Hash is a proper hash, so apply it
1143
1220
 
1144
1221
  // Handle browser bugs
@@ -1149,11 +1226,11 @@
1149
1226
  pageUrl = History.getPageUrl();
1150
1227
 
1151
1228
  // Safari hash apply
1152
- History.pushState(null,null,pageUrl+'#'+adjustedHash,false);
1229
+ History.pushState(null,null,pageUrl+'#'+hash,false);
1153
1230
  }
1154
1231
  else {
1155
1232
  // Normal hash apply
1156
- document.location.hash = adjustedHash;
1233
+ document.location.hash = hash;
1157
1234
  }
1158
1235
  }
1159
1236
 
@@ -1171,7 +1248,7 @@
1171
1248
  var result = History.normalizeHash(hash);
1172
1249
 
1173
1250
  // Escape hash
1174
- result = window.escape(result);
1251
+ result = window.encodeURIComponent(result);
1175
1252
 
1176
1253
  // IE6 Escape Bug
1177
1254
  if ( !History.bugs.hashEscape ) {
@@ -1446,7 +1523,7 @@
1446
1523
 
1447
1524
  // Get the Last State which has the new URL
1448
1525
  var
1449
- urlState = History.extractState(document.location.href),
1526
+ urlState = History.extractState(History.getLocationHref()),
1450
1527
  newState;
1451
1528
 
1452
1529
  // Check for a difference
@@ -1614,10 +1691,10 @@
1614
1691
  History.doubleCheckComplete();
1615
1692
 
1616
1693
  // Check for a Hash, and handle apporiatly
1617
- currentHash = History.getHash();
1694
+ currentHash = History.getHash();
1618
1695
  if ( currentHash ) {
1619
1696
  // Expand Hash
1620
- currentState = History.extractState(currentHash||document.location.href,true);
1697
+ currentState = History.extractState(currentHash||History.getLocationHref(),true);
1621
1698
  if ( currentState ) {
1622
1699
  // We were able to parse it, it must be a State!
1623
1700
  // Let's forward to replaceState
@@ -1650,13 +1727,13 @@
1650
1727
  }
1651
1728
  else {
1652
1729
  // Initial State
1653
- newState = History.extractState(document.location.href);
1730
+ newState = History.extractState(History.getLocationHref());
1654
1731
  }
1655
1732
 
1656
1733
  // The State did not exist in our store
1657
1734
  if ( !newState ) {
1658
1735
  // Regenerate the State
1659
- newState = History.createStateObject(null,null,document.location.href);
1736
+ newState = History.createStateObject(null,null,History.getLocationHref());
1660
1737
  }
1661
1738
 
1662
1739
  // Clean
@@ -1830,13 +1907,12 @@
1830
1907
  /**
1831
1908
  * Clear Intervals on exit to prevent memory leaks
1832
1909
  */
1833
- History.Adapter.bind(window,"beforeunload",History.clearAllIntervals);
1834
1910
  History.Adapter.bind(window,"unload",History.clearAllIntervals);
1835
1911
 
1836
1912
  /**
1837
1913
  * Create the initial State
1838
1914
  */
1839
- History.saveState(History.storeState(History.extractState(document.location.href,true)));
1915
+ History.saveState(History.storeState(History.extractState(History.getLocationHref(),true)));
1840
1916
 
1841
1917
  /**
1842
1918
  * Bind for Saving Store
@@ -1845,7 +1921,7 @@
1845
1921
  // When the page is closed
1846
1922
  History.onUnload = function(){
1847
1923
  // Prepare
1848
- var currentStore, item;
1924
+ var currentStore, item, currentStoreString;
1849
1925
 
1850
1926
  // Fetch
1851
1927
  try {
@@ -1884,17 +1960,40 @@
1884
1960
  History.store = currentStore;
1885
1961
  History.normalizeStore();
1886
1962
 
1887
- // Store
1888
- sessionStorage.setItem('History.store',JSON.stringify(currentStore));
1963
+ // In Safari, going into Private Browsing mode causes the
1964
+ // Session Storage object to still exist but if you try and use
1965
+ // or set any property/function of it it throws the exception
1966
+ // "QUOTA_EXCEEDED_ERR: DOM Exception 22: An attempt was made to
1967
+ // add something to storage that exceeded the quota." infinitely
1968
+ // every second.
1969
+ currentStoreString = JSON.stringify(currentStore);
1970
+ try {
1971
+ // Store
1972
+ sessionStorage.setItem('History.store', currentStoreString);
1973
+ }
1974
+ catch (e) {
1975
+ if (e.code === DOMException.QUOTA_EXCEEDED_ERR) {
1976
+ if (sessionStorage.length) {
1977
+ // Workaround for a bug seen on iPads. Sometimes the quota exceeded error comes up and simply
1978
+ // removing/resetting the storage can work.
1979
+ sessionStorage.removeItem('History.store');
1980
+ sessionStorage.setItem('History.store', currentStoreString);
1981
+ } else {
1982
+ // Otherwise, we're probably private browsing in Safari, so we'll ignore the exception.
1983
+ }
1984
+ } else {
1985
+ throw e;
1986
+ }
1987
+ }
1889
1988
  };
1890
1989
 
1891
1990
  // For Internet Explorer
1892
1991
  History.intervalList.push(setInterval(History.onUnload,History.options.storeInterval));
1893
-
1992
+
1894
1993
  // For Other Browsers
1895
1994
  History.Adapter.bind(window,'beforeunload',History.onUnload);
1896
1995
  History.Adapter.bind(window,'unload',History.onUnload);
1897
-
1996
+
1898
1997
  // Both are enabled for consistency
1899
1998
  }
1900
1999
 
@@ -1937,7 +2036,9 @@
1937
2036
 
1938
2037
  }; // History.initCore
1939
2038
 
1940
- // Try and Initialise History
1941
- History.init();
2039
+ // Try to Initialise History
2040
+ if (!History.options || !History.options.delayInit) {
2041
+ History.init();
2042
+ }
1942
2043
 
1943
- })(window);
2044
+ })(window);
@@ -78,6 +78,19 @@
78
78
  return isLast;
79
79
  };
80
80
 
81
+ /**
82
+ * History.isHashEqual(newHash, oldHash)
83
+ * Checks to see if two hashes are functionally equal
84
+ * @param {string} newHash
85
+ * @param {string} oldHash
86
+ * @return {boolean} true
87
+ */
88
+ History.isHashEqual = function(newHash, oldHash){
89
+ newHash = encodeURIComponent(newHash).replace(/%25/g, "%");
90
+ oldHash = encodeURIComponent(oldHash).replace(/%25/g, "%");
91
+ return newHash === oldHash;
92
+ };
93
+
81
94
  /**
82
95
  * History.saveHash(newHash)
83
96
  * Push a Hash
@@ -190,7 +203,7 @@
190
203
  };
191
204
 
192
205
  /**
193
- * History.discardState(State)
206
+ * History.discardedState(State)
194
207
  * Checks to see if the state is discarded
195
208
  * @param {object} State
196
209
  * @return {bool}
@@ -263,7 +276,8 @@
263
276
  // Define some variables that will help in our checker function
264
277
  var lastDocumentHash = '',
265
278
  iframeId, iframe,
266
- lastIframeHash, checkerRunning;
279
+ lastIframeHash, checkerRunning,
280
+ startedWithHash = Boolean(History.getHash());
267
281
 
268
282
  // Handle depending on the browser
269
283
  if ( History.isInternetExplorer() ) {
@@ -275,7 +289,10 @@
275
289
  iframe = document.createElement('iframe');
276
290
 
277
291
  // Adjust iFarme
292
+ // IE 6 requires iframe to have a src on HTTPS pages, otherwise it will throw a
293
+ // "This page contains both secure and nonsecure items" warning.
278
294
  iframe.setAttribute('id', iframeId);
295
+ iframe.setAttribute('src', '#');
279
296
  iframe.style.display = 'none';
280
297
 
281
298
  // Append iFrame
@@ -300,8 +317,9 @@
300
317
  checkerRunning = true;
301
318
 
302
319
  // Fetch
303
- var documentHash = History.getHash()||'',
304
- iframeHash = History.unescapeHash(iframe.contentWindow.document.location.hash)||'';
320
+ var
321
+ documentHash = History.getHash(),
322
+ iframeHash = History.getHash(iframe.contentWindow.document);
305
323
 
306
324
  // The Document Hash has changed (application caused)
307
325
  if ( documentHash !== lastDocumentHash ) {
@@ -334,8 +352,18 @@
334
352
  // Equalise
335
353
  lastIframeHash = iframeHash;
336
354
 
337
- // Update the Hash
338
- History.setHash(iframeHash,false);
355
+ // If there is no iframe hash that means we're at the original
356
+ // iframe state.
357
+ // And if there was a hash on the original request, the original
358
+ // iframe state was replaced instantly, so skip this state and take
359
+ // the user back to where they came from.
360
+ if (startedWithHash && iframeHash === '') {
361
+ History.back();
362
+ }
363
+ else {
364
+ // Update the Hash
365
+ History.setHash(iframeHash,false);
366
+ }
339
367
  }
340
368
 
341
369
  // Reset Running
@@ -352,7 +380,7 @@
352
380
  // Define the checker function
353
381
  History.checkerFunction = function(){
354
382
  // Prepare
355
- var documentHash = History.getHash();
383
+ var documentHash = History.getHash()||'';
356
384
 
357
385
  // The Document Hash has changed (application caused)
358
386
  if ( documentHash !== lastDocumentHash ) {
@@ -398,7 +426,7 @@
398
426
  //History.debug('History.onHashChange', arguments);
399
427
 
400
428
  // Prepare
401
- var currentUrl = ((event && event.newURL) || document.location.href),
429
+ var currentUrl = ((event && event.newURL) || History.getLocationHref()),
402
430
  currentHash = History.getHashByUrl(currentUrl),
403
431
  currentState = null,
404
432
  currentStateHash = null,
@@ -429,7 +457,7 @@
429
457
  }
430
458
 
431
459
  // Create State
432
- currentState = History.extractState(History.getFullUrl(currentHash||document.location.href,false),true);
460
+ currentState = History.extractState(History.getFullUrl(currentHash||History.getLocationHref()),true);
433
461
 
434
462
  // Check if we are the same state
435
463
  if ( History.isLastSavedState(currentState) ) {
@@ -460,7 +488,7 @@
460
488
 
461
489
  // Push the new HTML5 State
462
490
  //History.debug('History.onHashChange: success hashchange');
463
- History.pushState(currentState.data,currentState.title,currentState.url,false);
491
+ History.pushState(currentState.data,currentState.title,encodeURI(currentState.url),false);
464
492
 
465
493
  // End onHashChange closure
466
494
  return true;
@@ -479,9 +507,14 @@
479
507
  History.pushState = function(data,title,url,queue){
480
508
  //History.debug('History.pushState: called', arguments);
481
509
 
510
+ // We assume that the URL passed in is URI-encoded, but this makes
511
+ // sure that it's fully URI encoded; any '%'s that are encoded are
512
+ // converted back into '%'s
513
+ url = encodeURI(url).replace(/%25/g, "%");
514
+
482
515
  // Check the State
483
516
  if ( History.getHashByUrl(url) ) {
484
- throw new Error('History.js does not support states with fragement-identifiers (hashes/anchors).');
517
+ throw new Error('History.js does not support states with fragment-identifiers (hashes/anchors).');
485
518
  }
486
519
 
487
520
  // Handle Queueing
@@ -505,7 +538,8 @@
505
538
  newStateHash = History.getHashByState(newState),
506
539
  oldState = History.getState(false),
507
540
  oldStateHash = History.getHashByState(oldState),
508
- html4Hash = History.getHash();
541
+ html4Hash = History.getHash(),
542
+ wasExpected = History.expectedStateId == newState.id;
509
543
 
510
544
  // Store the newState
511
545
  History.storeState(newState);
@@ -524,19 +558,18 @@
524
558
  return false;
525
559
  }
526
560
 
527
- // Update HTML4 Hash
528
- if ( newStateHash !== html4Hash && newStateHash !== History.getShortUrl(document.location.href) ) {
529
- //History.debug('History.pushState: update hash', newStateHash, html4Hash);
530
- History.setHash(newStateHash,false);
531
- return false;
532
- }
533
-
534
561
  // Update HTML5 State
535
562
  History.saveState(newState);
536
563
 
537
564
  // Fire HTML5 Event
538
- //History.debug('History.pushState: trigger popstate');
539
- History.Adapter.trigger(window,'statechange');
565
+ if(!wasExpected)
566
+ History.Adapter.trigger(window,'statechange');
567
+
568
+ // Update HTML4 Hash
569
+ if ( !History.isHashEqual(newStateHash, html4Hash) && !History.isHashEqual(newStateHash, History.getShortUrl(History.getLocationHref())) ) {
570
+ History.setHash(newStateHash,false);
571
+ }
572
+
540
573
  History.busy(false);
541
574
 
542
575
  // End pushState closure
@@ -555,9 +588,14 @@
555
588
  History.replaceState = function(data,title,url,queue){
556
589
  //History.debug('History.replaceState: called', arguments);
557
590
 
591
+ // We assume that the URL passed in is URI-encoded, but this makes
592
+ // sure that it's fully URI encoded; any '%'s that are encoded are
593
+ // converted back into '%'s
594
+ url = encodeURI(url).replace(/%25/g, "%");
595
+
558
596
  // Check the State
559
597
  if ( History.getHashByUrl(url) ) {
560
- throw new Error('History.js does not support states with fragement-identifiers (hashes/anchors).');
598
+ throw new Error('History.js does not support states with fragment-identifiers (hashes/anchors).');
561
599
  }
562
600
 
563
601
  // Handle Queueing
@@ -578,14 +616,40 @@
578
616
 
579
617
  // Fetch the State Objects
580
618
  var newState = History.createStateObject(data,title,url),
619
+ newStateHash = History.getHashByState(newState),
581
620
  oldState = History.getState(false),
621
+ oldStateHash = History.getHashByState(oldState),
582
622
  previousState = History.getStateByIndex(-2);
583
623
 
584
624
  // Discard Old State
585
625
  History.discardState(oldState,newState,previousState);
586
626
 
587
- // Alias to PushState
588
- History.pushState(newState.data,newState.title,newState.url,false);
627
+ // If the url hasn't changed, just store and save the state
628
+ // and fire a statechange event to be consistent with the
629
+ // html 5 api
630
+ if ( newStateHash === oldStateHash ) {
631
+ // Store the newState
632
+ History.storeState(newState);
633
+ History.expectedStateId = newState.id;
634
+
635
+ // Recycle the State
636
+ History.recycleState(newState);
637
+
638
+ // Force update of the title
639
+ History.setTitle(newState);
640
+
641
+ // Update HTML5 State
642
+ History.saveState(newState);
643
+
644
+ // Fire HTML5 Event
645
+ //History.debug('History.pushState: trigger popstate');
646
+ History.Adapter.trigger(window,'statechange');
647
+ History.busy(false);
648
+ }
649
+ else {
650
+ // Alias to PushState
651
+ History.pushState(newState.data,newState.title,newState.url,false);
652
+ }
589
653
 
590
654
  // End replaceState closure
591
655
  return true;
@@ -613,9 +677,9 @@
613
677
 
614
678
  }; // History.initHtml4
615
679
 
616
- // Try and Initialise History
680
+ // Try to Initialise History
617
681
  if ( typeof History.init !== 'undefined' ) {
618
682
  History.init();
619
683
  }
620
684
 
621
- })(window);
685
+ })(window);
@@ -1,6 +1,6 @@
1
1
  /*
2
- http://www.JSON.org/json2.js
3
- 2011-10-19
2
+ json2.js
3
+ 2013-05-26
4
4
 
5
5
  Public Domain.
6
6
 
@@ -159,8 +159,7 @@
159
159
  // Create a JSON object only if one does not already exist. We create the
160
160
  // methods in a closure to avoid creating global variables.
161
161
 
162
- var JSON;
163
- if (!JSON) {
162
+ if (typeof JSON !== 'object') {
164
163
  JSON = {};
165
164
  }
166
165
 
@@ -174,7 +173,7 @@ if (!JSON) {
174
173
 
175
174
  if (typeof Date.prototype.toJSON !== 'function') {
176
175
 
177
- Date.prototype.toJSON = function (key) {
176
+ Date.prototype.toJSON = function () {
178
177
 
179
178
  return isFinite(this.valueOf())
180
179
  ? this.getUTCFullYear() + '-' +
@@ -188,7 +187,7 @@ if (!JSON) {
188
187
 
189
188
  String.prototype.toJSON =
190
189
  Number.prototype.toJSON =
191
- Boolean.prototype.toJSON = function (key) {
190
+ Boolean.prototype.toJSON = function () {
192
191
  return this.valueOf();
193
192
  };
194
193
  }
metadata CHANGED
@@ -1,49 +1,57 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jquery-historyjs
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.3
5
- prerelease:
4
+ version: 0.3.0
6
5
  platform: ruby
7
6
  authors:
8
7
  - William Weidendorf
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2012-03-09 00:00:00.000000000 Z
11
+ date: 2013-10-25 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: railties
16
- requirement: &70137982107680 !ruby/object:Gem::Requirement
17
- none: false
15
+ requirement: !ruby/object:Gem::Requirement
18
16
  requirements:
19
- - - ! '>='
17
+ - - '>='
20
18
  - !ruby/object:Gem::Version
21
19
  version: '3.0'
22
20
  type: :runtime
23
21
  prerelease: false
24
- version_requirements: *70137982107680
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '3.0'
25
27
  - !ruby/object:Gem::Dependency
26
28
  name: thor
27
- requirement: &70137982117720 !ruby/object:Gem::Requirement
28
- none: false
29
+ requirement: !ruby/object:Gem::Requirement
29
30
  requirements:
30
- - - ! '>='
31
+ - - '>='
31
32
  - !ruby/object:Gem::Version
32
33
  version: '0.14'
33
34
  type: :runtime
34
35
  prerelease: false
35
- version_requirements: *70137982117720
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0.14'
36
41
  - !ruby/object:Gem::Dependency
37
42
  name: bundler
38
- requirement: &70137982114440 !ruby/object:Gem::Requirement
39
- none: false
43
+ requirement: !ruby/object:Gem::Requirement
40
44
  requirements:
41
- - - ! '>='
45
+ - - '>='
42
46
  - !ruby/object:Gem::Version
43
47
  version: 1.0.0
44
48
  type: :development
45
49
  prerelease: false
46
- version_requirements: *70137982114440
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: 1.0.0
47
55
  description: This gem provides History.js and the related HTML4 dependencies for using
48
56
  History.js with jQuery in your Rails 3+ application.
49
57
  email:
@@ -73,29 +81,25 @@ files:
73
81
  - vendor/assets/javascripts/json2.js
74
82
  homepage: http://github.com/wweidendorf/jquery-historyjs
75
83
  licenses: []
84
+ metadata: {}
76
85
  post_install_message:
77
86
  rdoc_options: []
78
87
  require_paths:
79
88
  - lib
80
89
  required_ruby_version: !ruby/object:Gem::Requirement
81
- none: false
82
90
  requirements:
83
- - - ! '>='
91
+ - - '>='
84
92
  - !ruby/object:Gem::Version
85
93
  version: '0'
86
- segments:
87
- - 0
88
- hash: -3263187480763743088
89
94
  required_rubygems_version: !ruby/object:Gem::Requirement
90
- none: false
91
95
  requirements:
92
- - - ! '>='
96
+ - - '>='
93
97
  - !ruby/object:Gem::Version
94
98
  version: 1.3.6
95
99
  requirements: []
96
100
  rubyforge_project: jquery-historyjs
97
- rubygems_version: 1.8.11
101
+ rubygems_version: 2.0.2
98
102
  signing_key:
99
- specification_version: 3
103
+ specification_version: 4
100
104
  summary: Use History.js with Rails 3 and jQuery
101
105
  test_files: []