jquery.history.js 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. /*
  2. * jQuery history plugin
  3. *
  4. * Copyright (c) 2006 Taku Sano (Mikage Sawatari)
  5. * Licensed under the MIT License:
  6. * http://www.opensource.org/licenses/mit-license.php
  7. *
  8. * Modified by Lincoln Cooper to add Safari support and only call the callback once during initialization
  9. * for msie when no initial hash supplied.
  10. * API rewrite by Lauris Bukðis-Haberkorns
  11. */
  12. (function($) {
  13. function History()
  14. {
  15. this._curHash = '';
  16. this._callback = function(hash){};
  17. };
  18. $.extend(History.prototype, {
  19. init: function(callback) {
  20. this._callback = callback;
  21. this._curHash = location.hash;
  22. if($.browser.msie) {
  23. // To stop the callback firing twice during initilization if no hash present
  24. if (this._curHash == '') {
  25. this._curHash = '#';
  26. }
  27. // add hidden iframe for IE
  28. $("body").prepend('<iframe id="jQuery_history" style="display: none;"></iframe>');
  29. var iframe = $("#jQuery_history")[0].contentWindow.document;
  30. iframe.open();
  31. iframe.close();
  32. iframe.location.hash = this._curHash;
  33. }
  34. else if ($.browser.safari) {
  35. // etablish back/forward stacks
  36. this._historyBackStack = [];
  37. this._historyBackStack.length = history.length;
  38. this._historyForwardStack = [];
  39. this._isFirst = true;
  40. this._dontCheck = false;
  41. }
  42. this._callback(this._curHash.replace(/^#/, ''));
  43. setInterval(this._check, 100);
  44. },
  45. add: function(hash) {
  46. // This makes the looping function do something
  47. this._historyBackStack.push(hash);
  48. this._historyForwardStack.length = 0; // clear forwardStack (true click occured)
  49. this._isFirst = true;
  50. },
  51. _check: function() {
  52. if($.browser.msie) {
  53. // On IE, check for location.hash of iframe
  54. var ihistory = $("#jQuery_history")[0];
  55. var iframe = ihistory.contentDocument || ihistory.contentWindow.document;
  56. var current_hash = iframe.location.hash;
  57. if(current_hash != $.history._curHash) {
  58. location.hash = current_hash;
  59. $.history._curHash = current_hash;
  60. $.history._callback(current_hash.replace(/^#/, ''));
  61. }
  62. } else if ($.browser.safari) {
  63. if (!$.history._dontCheck) {
  64. var historyDelta = history.length - $.history._historyBackStack.length;
  65. if (historyDelta) { // back or forward button has been pushed
  66. $.history._isFirst = false;
  67. if (historyDelta < 0) { // back button has been pushed
  68. // move items to forward stack
  69. for (var i = 0; i < Math.abs(historyDelta); i++) $.history._historyForwardStack.unshift($.history._historyBackStack.pop());
  70. } else { // forward button has been pushed
  71. // move items to back stack
  72. for (var i = 0; i < historyDelta; i++) $.history._historyBackStack.push($.history._historyForwardStack.shift());
  73. }
  74. var cachedHash = $.history._historyBackStack[$.history._historyBackStack.length - 1];
  75. if (cachedHash != undefined) {
  76. $.history._curHash = location.hash;
  77. $.history._callback(cachedHash);
  78. }
  79. } else if ($.history._historyBackStack[$.history._historyBackStack.length - 1] == undefined && !$.history._isFirst) {
  80. // back button has been pushed to beginning and URL already pointed to hash (e.g. a bookmark)
  81. // document.URL doesn't change in Safari
  82. if (document.URL.indexOf('#') >= 0) {
  83. $.history._callback(document.URL.split('#')[1]);
  84. } else {
  85. $.history._callback('');
  86. }
  87. $.history._isFirst = true;
  88. }
  89. }
  90. } else {
  91. // otherwise, check for location.hash
  92. var current_hash = location.hash;
  93. if(current_hash != $.history._curHash) {
  94. $.history._curHash = current_hash;
  95. $.history._callback(current_hash.replace(/^#/, ''));
  96. }
  97. }
  98. },
  99. load: function(hash) {
  100. var newhash;
  101. if ($.browser.safari) {
  102. newhash = hash;
  103. } else {
  104. newhash = '#' + hash;
  105. location.hash = newhash;
  106. }
  107. this._curHash = newhash;
  108. if ($.browser.msie) {
  109. var ihistory = $("#jQuery_history")[0]; // TODO: need contentDocument?
  110. var iframe = ihistory.contentWindow.document;
  111. iframe.open();
  112. iframe.close();
  113. iframe.location.hash = newhash;
  114. this._callback(hash);
  115. }
  116. else if ($.browser.safari) {
  117. this._dontCheck = true;
  118. // Manually keep track of the history values for Safari
  119. this.add(hash);
  120. // Wait a while before allowing checking so that Safari has time to update the "history" object
  121. // correctly (otherwise the check loop would detect a false change in hash).
  122. var fn = function() {$.history._dontCheck = false;};
  123. window.setTimeout(fn, 200);
  124. this._callback(hash);
  125. // N.B. "location.hash=" must be the last line of code for Safari as execution stops afterwards.
  126. // By explicitly using the "location.hash" command (instead of using a variable set to "location.hash") the
  127. // URL in the browser and the "history" object are both updated correctly.
  128. location.hash = newhash;
  129. }
  130. else {
  131. this._callback(hash);
  132. }
  133. }
  134. });
  135. $(document).ready(function() {
  136. $.history = new History(); // singleton instance
  137. });
  138. })(jQuery);