📄 history.js
字号:
/*======================================================================
* 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;
}
};
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -