jquery.history.js 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. /*
  2. * jQuery history plugin
  3. *
  4. * The MIT License
  5. *
  6. * Copyright (c) 2006-2009 Taku Sano (Mikage Sawatari)
  7. * Copyright (c) 2010 Takayuki Miwa
  8. *
  9. * Permission is hereby granted, free of charge, to any person obtaining a copy
  10. * of this software and associated documentation files (the "Software"), to deal
  11. * in the Software without restriction, including without limitation the rights
  12. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  13. * copies of the Software, and to permit persons to whom the Software is
  14. * furnished to do so, subject to the following conditions:
  15. *
  16. * The above copyright notice and this permission notice shall be included in
  17. * all copies or substantial portions of the Software.
  18. *
  19. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  20. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  21. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  22. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  23. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  24. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  25. * THE SOFTWARE.
  26. */
  27. (function($) {
  28. var locationWrapper = {
  29. put: function(hash, win) {
  30. (win || window).location.hash = this.encoder(hash);
  31. },
  32. get: function(win) {
  33. var hash = ((win || window).location.hash).replace(/^#/, '');
  34. try {
  35. return $.browser.mozilla ? hash : decodeURIComponent(hash);
  36. }
  37. catch (error) {
  38. return hash;
  39. }
  40. },
  41. encoder: encodeURIComponent
  42. };
  43. var iframeWrapper = {
  44. id: "__jQuery_history",
  45. init: function() {
  46. var html = '<iframe id="'+ this.id +'" style="display:none" src="javascript:false;" />';
  47. $("body").prepend(html);
  48. return this;
  49. },
  50. _document: function() {
  51. return $("#"+ this.id)[0].contentWindow.document;
  52. },
  53. put: function(hash) {
  54. var doc = this._document();
  55. doc.open();
  56. doc.close();
  57. locationWrapper.put(hash, doc);
  58. },
  59. get: function() {
  60. return locationWrapper.get(this._document());
  61. }
  62. };
  63. function initObjects(options) {
  64. options = $.extend({
  65. unescape: false
  66. }, options || {});
  67. locationWrapper.encoder = encoder(options.unescape);
  68. function encoder(unescape_) {
  69. if(unescape_ === true) {
  70. return function(hash){ return hash; };
  71. }
  72. if(typeof unescape_ == "string" &&
  73. (unescape_ = partialDecoder(unescape_.split("")))
  74. || typeof unescape_ == "function") {
  75. return function(hash) { return unescape_(encodeURIComponent(hash)); };
  76. }
  77. return encodeURIComponent;
  78. }
  79. function partialDecoder(chars) {
  80. var re = new RegExp($.map(chars, encodeURIComponent).join("|"), "ig");
  81. return function(enc) { return enc.replace(re, decodeURIComponent); };
  82. }
  83. }
  84. var implementations = {};
  85. implementations.base = {
  86. callback: undefined,
  87. type: undefined,
  88. check: function() {},
  89. load: function(hash) {},
  90. init: function(callback, options) {
  91. initObjects(options);
  92. self.callback = callback;
  93. self._options = options;
  94. self._init();
  95. },
  96. _init: function() {},
  97. _options: {}
  98. };
  99. implementations.timer = {
  100. _appState: undefined,
  101. _init: function() {
  102. var current_hash = locationWrapper.get();
  103. self._appState = current_hash;
  104. self.callback(current_hash);
  105. setInterval(self.check, 100);
  106. },
  107. check: function() {
  108. var current_hash = locationWrapper.get();
  109. if(current_hash != self._appState) {
  110. self._appState = current_hash;
  111. self.callback(current_hash);
  112. }
  113. },
  114. load: function(hash) {
  115. if(hash != self._appState) {
  116. locationWrapper.put(hash);
  117. self._appState = hash;
  118. self.callback(hash);
  119. }
  120. }
  121. };
  122. implementations.iframeTimer = {
  123. _appState: undefined,
  124. _init: function() {
  125. var current_hash = locationWrapper.get();
  126. self._appState = current_hash;
  127. iframeWrapper.init().put(current_hash);
  128. self.callback(current_hash);
  129. setInterval(self.check, 100);
  130. },
  131. check: function() {
  132. var iframe_hash = iframeWrapper.get(),
  133. location_hash = locationWrapper.get();
  134. if (location_hash != iframe_hash) {
  135. if (location_hash == self._appState) { // user used Back or Forward button
  136. self._appState = iframe_hash;
  137. locationWrapper.put(iframe_hash);
  138. self.callback(iframe_hash);
  139. } else { // user loaded new bookmark
  140. self._appState = location_hash;
  141. iframeWrapper.put(location_hash);
  142. self.callback(location_hash);
  143. }
  144. }
  145. },
  146. load: function(hash) {
  147. if(hash != self._appState) {
  148. locationWrapper.put(hash);
  149. iframeWrapper.put(hash);
  150. self._appState = hash;
  151. self.callback(hash);
  152. }
  153. }
  154. };
  155. implementations.hashchangeEvent = {
  156. _init: function() {
  157. self.callback(locationWrapper.get());
  158. $(window).bind('hashchange', self.check);
  159. },
  160. check: function() {
  161. self.callback(locationWrapper.get());
  162. },
  163. load: function(hash) {
  164. locationWrapper.put(hash);
  165. }
  166. };
  167. var self = $.extend({}, implementations.base);
  168. if($.browser.msie && ($.browser.version < 8 || document.documentMode < 8)) {
  169. self.type = 'iframeTimer';
  170. } else if("onhashchange" in window) {
  171. self.type = 'hashchangeEvent';
  172. } else {
  173. self.type = 'timer';
  174. }
  175. $.extend(self, implementations[self.type]);
  176. $.history = self;
  177. })(jQuery);