PageRenderTime 32ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/ajax/scripts/history.js

http://showslow.googlecode.com/
JavaScript | 220 lines | 150 code | 36 blank | 34 comment | 14 complexity | 8aba413b4c580f466e6edff9d271dfd8 MD5 | raw file
  1. /*======================================================================
  2. * History
  3. *
  4. * This is a singleton that keeps track of undoable user actions and
  5. * performs undos and redos in response to the browser's Back and
  6. * Forward buttons.
  7. *
  8. * Call addAction(action) to register an undoable user action. action
  9. * must have 4 fields:
  10. *
  11. * perform: an argument-less function that carries out the action
  12. * undo: an argument-less function that undos the action
  13. * label: a short, user-friendly string describing the action
  14. * uiLayer: the UI layer on which the action takes place
  15. *
  16. * By default, the history keeps track of upto 10 actions. You can
  17. * configure this behavior by setting
  18. * SimileAjax.History.maxHistoryLength
  19. * to a different number.
  20. *
  21. * An iframe is inserted into the document's body element to track
  22. * onload events.
  23. *======================================================================
  24. */
  25. SimileAjax.History = {
  26. maxHistoryLength: 10,
  27. historyFile: "__history__.html",
  28. enabled: true,
  29. _initialized: false,
  30. _listeners: new SimileAjax.ListenerQueue(),
  31. _actions: [],
  32. _baseIndex: 0,
  33. _currentIndex: 0,
  34. _plainDocumentTitle: document.title
  35. };
  36. SimileAjax.History.formatHistoryEntryTitle = function(actionLabel) {
  37. return SimileAjax.History._plainDocumentTitle + " {" + actionLabel + "}";
  38. };
  39. SimileAjax.History.initialize = function() {
  40. if (SimileAjax.History._initialized) {
  41. return;
  42. }
  43. if (SimileAjax.History.enabled) {
  44. var iframe = document.createElement("iframe");
  45. iframe.id = "simile-ajax-history";
  46. iframe.style.position = "absolute";
  47. iframe.style.width = "10px";
  48. iframe.style.height = "10px";
  49. iframe.style.top = "0px";
  50. iframe.style.left = "0px";
  51. iframe.style.visibility = "hidden";
  52. iframe.src = SimileAjax.History.historyFile + "?0";
  53. document.body.appendChild(iframe);
  54. SimileAjax.DOM.registerEvent(iframe, "load", SimileAjax.History._handleIFrameOnLoad);
  55. SimileAjax.History._iframe = iframe;
  56. }
  57. SimileAjax.History._initialized = true;
  58. };
  59. SimileAjax.History.addListener = function(listener) {
  60. SimileAjax.History.initialize();
  61. SimileAjax.History._listeners.add(listener);
  62. };
  63. SimileAjax.History.removeListener = function(listener) {
  64. SimileAjax.History.initialize();
  65. SimileAjax.History._listeners.remove(listener);
  66. };
  67. SimileAjax.History.addAction = function(action) {
  68. SimileAjax.History.initialize();
  69. SimileAjax.History._listeners.fire("onBeforePerform", [ action ]);
  70. window.setTimeout(function() {
  71. try {
  72. action.perform();
  73. SimileAjax.History._listeners.fire("onAfterPerform", [ action ]);
  74. if (SimileAjax.History.enabled) {
  75. SimileAjax.History._actions = SimileAjax.History._actions.slice(
  76. 0, SimileAjax.History._currentIndex - SimileAjax.History._baseIndex);
  77. SimileAjax.History._actions.push(action);
  78. SimileAjax.History._currentIndex++;
  79. var diff = SimileAjax.History._actions.length - SimileAjax.History.maxHistoryLength;
  80. if (diff > 0) {
  81. SimileAjax.History._actions = SimileAjax.History._actions.slice(diff);
  82. SimileAjax.History._baseIndex += diff;
  83. }
  84. try {
  85. SimileAjax.History._iframe.contentWindow.location.search =
  86. "?" + SimileAjax.History._currentIndex;
  87. } catch (e) {
  88. /*
  89. * We can't modify location.search most probably because it's a file:// url.
  90. * We'll just going to modify the document's title.
  91. */
  92. var title = SimileAjax.History.formatHistoryEntryTitle(action.label);
  93. document.title = title;
  94. }
  95. }
  96. } catch (e) {
  97. SimileAjax.Debug.exception(e, "Error adding action {" + action.label + "} to history");
  98. }
  99. }, 0);
  100. };
  101. SimileAjax.History.addLengthyAction = function(perform, undo, label) {
  102. SimileAjax.History.addAction({
  103. perform: perform,
  104. undo: undo,
  105. label: label,
  106. uiLayer: SimileAjax.WindowManager.getBaseLayer(),
  107. lengthy: true
  108. });
  109. };
  110. SimileAjax.History._handleIFrameOnLoad = function() {
  111. /*
  112. * This function is invoked when the user herself
  113. * navigates backward or forward. We need to adjust
  114. * the application's state accordingly.
  115. */
  116. try {
  117. var q = SimileAjax.History._iframe.contentWindow.location.search;
  118. var c = (q.length == 0) ? 0 : Math.max(0, parseInt(q.substr(1)));
  119. var finishUp = function() {
  120. var diff = c - SimileAjax.History._currentIndex;
  121. SimileAjax.History._currentIndex += diff;
  122. SimileAjax.History._baseIndex += diff;
  123. SimileAjax.History._iframe.contentWindow.location.search = "?" + c;
  124. };
  125. if (c < SimileAjax.History._currentIndex) { // need to undo
  126. SimileAjax.History._listeners.fire("onBeforeUndoSeveral", []);
  127. window.setTimeout(function() {
  128. while (SimileAjax.History._currentIndex > c &&
  129. SimileAjax.History._currentIndex > SimileAjax.History._baseIndex) {
  130. SimileAjax.History._currentIndex--;
  131. var action = SimileAjax.History._actions[SimileAjax.History._currentIndex - SimileAjax.History._baseIndex];
  132. try {
  133. action.undo();
  134. } catch (e) {
  135. SimileAjax.Debug.exception(e, "History: Failed to undo action {" + action.label + "}");
  136. }
  137. }
  138. SimileAjax.History._listeners.fire("onAfterUndoSeveral", []);
  139. finishUp();
  140. }, 0);
  141. } else if (c > SimileAjax.History._currentIndex) { // need to redo
  142. SimileAjax.History._listeners.fire("onBeforeRedoSeveral", []);
  143. window.setTimeout(function() {
  144. while (SimileAjax.History._currentIndex < c &&
  145. SimileAjax.History._currentIndex - SimileAjax.History._baseIndex < SimileAjax.History._actions.length) {
  146. var action = SimileAjax.History._actions[SimileAjax.History._currentIndex - SimileAjax.History._baseIndex];
  147. try {
  148. action.perform();
  149. } catch (e) {
  150. SimileAjax.Debug.exception(e, "History: Failed to redo action {" + action.label + "}");
  151. }
  152. SimileAjax.History._currentIndex++;
  153. }
  154. SimileAjax.History._listeners.fire("onAfterRedoSeveral", []);
  155. finishUp();
  156. }, 0);
  157. } else {
  158. var index = SimileAjax.History._currentIndex - SimileAjax.History._baseIndex - 1;
  159. var title = (index >= 0 && index < SimileAjax.History._actions.length) ?
  160. SimileAjax.History.formatHistoryEntryTitle(SimileAjax.History._actions[index].label) :
  161. SimileAjax.History._plainDocumentTitle;
  162. SimileAjax.History._iframe.contentWindow.document.title = title;
  163. document.title = title;
  164. }
  165. } catch (e) {
  166. // silent
  167. }
  168. };
  169. SimileAjax.History.getNextUndoAction = function() {
  170. try {
  171. var index = SimileAjax.History._currentIndex - SimileAjax.History._baseIndex - 1;
  172. return SimileAjax.History._actions[index];
  173. } catch (e) {
  174. return null;
  175. }
  176. };
  177. SimileAjax.History.getNextRedoAction = function() {
  178. try {
  179. var index = SimileAjax.History._currentIndex - SimileAjax.History._baseIndex;
  180. return SimileAjax.History._actions[index];
  181. } catch (e) {
  182. return null;
  183. }
  184. };