/ajax/scripts/history.js
JavaScript | 220 lines | 150 code | 36 blank | 34 comment | 14 complexity | 8aba413b4c580f466e6edff9d271dfd8 MD5 | raw file
- /*======================================================================
- * History
- *
- * This is a singleton that keeps track of undoable user actions and
- * performs undos and redos in response to the browser's Back and
- * Forward buttons.
- *
- * Call addAction(action) to register an undoable user action. action
- * must have 4 fields:
- *
- * perform: an argument-less function that carries out the action
- * undo: an argument-less function that undos the action
- * label: a short, user-friendly string describing the action
- * uiLayer: the UI layer on which the action takes place
- *
- * By default, the history keeps track of upto 10 actions. You can
- * configure this behavior by setting
- * SimileAjax.History.maxHistoryLength
- * to a different number.
- *
- * An iframe is inserted into the document's body element to track
- * onload events.
- *======================================================================
- */
-
- SimileAjax.History = {
- maxHistoryLength: 10,
- historyFile: "__history__.html",
- enabled: true,
-
- _initialized: false,
- _listeners: new SimileAjax.ListenerQueue(),
-
- _actions: [],
- _baseIndex: 0,
- _currentIndex: 0,
-
- _plainDocumentTitle: document.title
- };
- SimileAjax.History.formatHistoryEntryTitle = function(actionLabel) {
- return SimileAjax.History._plainDocumentTitle + " {" + actionLabel + "}";
- };
- SimileAjax.History.initialize = function() {
- if (SimileAjax.History._initialized) {
- return;
- }
-
- if (SimileAjax.History.enabled) {
- var iframe = document.createElement("iframe");
- iframe.id = "simile-ajax-history";
- iframe.style.position = "absolute";
- iframe.style.width = "10px";
- iframe.style.height = "10px";
- iframe.style.top = "0px";
- iframe.style.left = "0px";
- iframe.style.visibility = "hidden";
- iframe.src = SimileAjax.History.historyFile + "?0";
-
- document.body.appendChild(iframe);
- SimileAjax.DOM.registerEvent(iframe, "load", SimileAjax.History._handleIFrameOnLoad);
-
- SimileAjax.History._iframe = iframe;
- }
- SimileAjax.History._initialized = true;
- };
- SimileAjax.History.addListener = function(listener) {
- SimileAjax.History.initialize();
-
- SimileAjax.History._listeners.add(listener);
- };
- SimileAjax.History.removeListener = function(listener) {
- SimileAjax.History.initialize();
-
- SimileAjax.History._listeners.remove(listener);
- };
- SimileAjax.History.addAction = function(action) {
- SimileAjax.History.initialize();
-
- SimileAjax.History._listeners.fire("onBeforePerform", [ action ]);
- window.setTimeout(function() {
- try {
- action.perform();
- SimileAjax.History._listeners.fire("onAfterPerform", [ action ]);
-
- if (SimileAjax.History.enabled) {
- SimileAjax.History._actions = SimileAjax.History._actions.slice(
- 0, SimileAjax.History._currentIndex - SimileAjax.History._baseIndex);
-
- SimileAjax.History._actions.push(action);
- SimileAjax.History._currentIndex++;
-
- var diff = SimileAjax.History._actions.length - SimileAjax.History.maxHistoryLength;
- if (diff > 0) {
- SimileAjax.History._actions = SimileAjax.History._actions.slice(diff);
- SimileAjax.History._baseIndex += diff;
- }
-
- try {
- SimileAjax.History._iframe.contentWindow.location.search =
- "?" + SimileAjax.History._currentIndex;
- } catch (e) {
- /*
- * We can't modify location.search most probably because it's a file:// url.
- * We'll just going to modify the document's title.
- */
- var title = SimileAjax.History.formatHistoryEntryTitle(action.label);
- document.title = title;
- }
- }
- } catch (e) {
- SimileAjax.Debug.exception(e, "Error adding action {" + action.label + "} to history");
- }
- }, 0);
- };
- SimileAjax.History.addLengthyAction = function(perform, undo, label) {
- SimileAjax.History.addAction({
- perform: perform,
- undo: undo,
- label: label,
- uiLayer: SimileAjax.WindowManager.getBaseLayer(),
- lengthy: true
- });
- };
- SimileAjax.History._handleIFrameOnLoad = function() {
- /*
- * This function is invoked when the user herself
- * navigates backward or forward. We need to adjust
- * the application's state accordingly.
- */
-
- try {
- var q = SimileAjax.History._iframe.contentWindow.location.search;
- var c = (q.length == 0) ? 0 : Math.max(0, parseInt(q.substr(1)));
-
- var finishUp = function() {
- var diff = c - SimileAjax.History._currentIndex;
- SimileAjax.History._currentIndex += diff;
- SimileAjax.History._baseIndex += diff;
-
- SimileAjax.History._iframe.contentWindow.location.search = "?" + c;
- };
-
- if (c < SimileAjax.History._currentIndex) { // need to undo
- SimileAjax.History._listeners.fire("onBeforeUndoSeveral", []);
- window.setTimeout(function() {
- while (SimileAjax.History._currentIndex > c &&
- SimileAjax.History._currentIndex > SimileAjax.History._baseIndex) {
-
- SimileAjax.History._currentIndex--;
-
- var action = SimileAjax.History._actions[SimileAjax.History._currentIndex - SimileAjax.History._baseIndex];
-
- try {
- action.undo();
- } catch (e) {
- SimileAjax.Debug.exception(e, "History: Failed to undo action {" + action.label + "}");
- }
- }
-
- SimileAjax.History._listeners.fire("onAfterUndoSeveral", []);
- finishUp();
- }, 0);
- } else if (c > SimileAjax.History._currentIndex) { // need to redo
- SimileAjax.History._listeners.fire("onBeforeRedoSeveral", []);
- window.setTimeout(function() {
- while (SimileAjax.History._currentIndex < c &&
- SimileAjax.History._currentIndex - SimileAjax.History._baseIndex < SimileAjax.History._actions.length) {
-
- var action = SimileAjax.History._actions[SimileAjax.History._currentIndex - SimileAjax.History._baseIndex];
-
- try {
- action.perform();
- } catch (e) {
- SimileAjax.Debug.exception(e, "History: Failed to redo action {" + action.label + "}");
- }
-
- SimileAjax.History._currentIndex++;
- }
-
- SimileAjax.History._listeners.fire("onAfterRedoSeveral", []);
- finishUp();
- }, 0);
- } else {
- var index = SimileAjax.History._currentIndex - SimileAjax.History._baseIndex - 1;
- var title = (index >= 0 && index < SimileAjax.History._actions.length) ?
- SimileAjax.History.formatHistoryEntryTitle(SimileAjax.History._actions[index].label) :
- SimileAjax.History._plainDocumentTitle;
-
- SimileAjax.History._iframe.contentWindow.document.title = title;
- document.title = title;
- }
- } catch (e) {
- // silent
- }
- };
- SimileAjax.History.getNextUndoAction = function() {
- try {
- var index = SimileAjax.History._currentIndex - SimileAjax.History._baseIndex - 1;
- return SimileAjax.History._actions[index];
- } catch (e) {
- return null;
- }
- };
- SimileAjax.History.getNextRedoAction = function() {
- try {
- var index = SimileAjax.History._currentIndex - SimileAjax.History._baseIndex;
- return SimileAjax.History._actions[index];
- } catch (e) {
- return null;
- }
- };