infusion
Version:
Infusion is an application framework for developing flexible stuff with JavaScript
170 lines (153 loc) • 5.78 kB
JavaScript
/*
Copyright The Infusion copyright holders
See the AUTHORS.md file at the top-level directory of this distribution and at
https://github.com/fluid-project/infusion/raw/main/AUTHORS.md.
Licensed under the Educational Community License (ECL), Version 2.0 or the New
BSD license. You may not use this file except in compliance with one these
Licenses.
You may obtain a copy of the ECL 2.0 License and BSD License at
https://github.com/fluid-project/infusion/raw/main/Infusion-LICENSE.txt
*/
"use strict";
fluid.registerNamespace("fluid.undo");
// The three states of the undo component
fluid.undo.STATE_INITIAL = "state_initial";
fluid.undo.STATE_CHANGED = "state_changed";
fluid.undo.STATE_REVERTED = "state_reverted";
fluid.undo.defaultRenderer = function (that, targetContainer) {
var str = that.options.strings;
var markup = "<span class='flc-undo'>" +
"<a href='#' class='flc-undo-undoControl'>" + str.undo + "</a>" +
"<a href='#' class='flc-undo-redoControl'>" + str.redo + "</a>" +
"</span>";
var markupNode = $(markup).attr({
"role": "region",
"aria-live": "polite",
"aria-relevant": "all"
});
targetContainer.append(markupNode);
return markupNode;
};
fluid.undo.refreshView = function (that) {
if (that.state === fluid.undo.STATE_INITIAL) {
that.locate("undoContainer").hide();
that.locate("redoContainer").hide();
} else if (that.state === fluid.undo.STATE_CHANGED) {
that.locate("undoContainer").show();
that.locate("redoContainer").hide();
} else if (that.state === fluid.undo.STATE_REVERTED) {
that.locate("undoContainer").hide();
that.locate("redoContainer").show();
}
};
fluid.undo.undoControlClick = function (that) {
if (that.state !== fluid.undo.STATE_REVERTED) {
fluid.model.copyModel(that.extremalModel, that.component.model);
that.component.updateModel(that.initialModel, that);
that.state = fluid.undo.STATE_REVERTED;
fluid.undo.refreshView(that);
that.locate("redoControl").trigger("focus");
}
return false;
};
fluid.undo.redoControlClick = function (that) {
if (that.state !== fluid.undo.STATE_CHANGED) {
that.component.updateModel(that.extremalModel, that);
that.state = fluid.undo.STATE_CHANGED;
fluid.undo.refreshView(that);
that.locate("undoControl").trigger("focus");
}
return false;
};
fluid.undo.modelChanged = function (that, newModel, oldModel, source) {
if (source !== that) {
that.state = fluid.undo.STATE_CHANGED;
fluid.model.copyModel(that.initialModel, oldModel);
fluid.undo.refreshView(that);
}
};
fluid.undo.copyInitialModel = function (that) {
fluid.model.copyModel(that.initialModel, that.component.model);
fluid.model.copyModel(that.extremalModel, that.component.model);
};
fluid.undo.setTabindex = function (that) {
fluid.tabindex(that.locate("undoControl"), 0);
fluid.tabindex(that.locate("redoControl"), 0);
};
/**
* Decorates a target component with the function of "undoability". This component is intended to be attached as a
* subcomponent to the target component, which will bear a grade of "fluid.undoable"
*
* @param {Object} component - a "model-bearing" standard Fluid component to receive the "undo" functionality
* @param {Object} options - (optional) a collection of options settings
*/
fluid.defaults("fluid.undo", {
gradeNames: ["fluid.viewComponent"],
members: {
state: fluid.undo.STATE_INITIAL,
initialModel: {},
extremalModel: {},
component: "{fluid.undoable}",
container: {
expander: {
func: "{that}.options.renderer",
args: ["{that}", "{that}.component.container"]
}
}
},
invokers: {
undoControlClick: {
funcName: "fluid.undo.undoControlClick",
args: "{that}"
},
redoControlClick: {
funcName: "fluid.undo.redoControlClick",
args: "{that}"
}
},
listeners: {
"onCreate.copyInitialModel": {
funcName: "fluid.undo.copyInitialModel",
priority: "before:refreshView"
},
"onCreate.setTabindex": "fluid.undo.setTabindex",
"onCreate.refreshView": "fluid.undo.refreshView",
"onCreate.bindUndoClick": {
"this": "{that}.dom.undoControl",
method: "on",
args: ["click", "{that}.undoControlClick"]
},
"onCreate.bindRedoClick": {
"this": "{that}.dom.redoControl",
method: "on",
args: ["click", "{that}.redoControlClick"]
},
"{fluid.undoable}.events.modelChanged": {
funcName: "fluid.undo.modelChanged",
args: ["{that}", "{arguments}.0", "{arguments}.1", "{arguments}.2"]
}
},
selectors: {
undoContainer: ".flc-undo-undoControl",
undoControl: ".flc-undo-undoControl",
redoContainer: ".flc-undo-redoControl",
redoControl: ".flc-undo-redoControl"
},
strings: {
undo: "undo edit",
redo: "redo edit"
},
renderer: fluid.undo.defaultRenderer
});
// An uninstantiable grade expressing the contract of the "fluid.undoable" grade
// WARNING: Note that the only component which complies with this "corrupt contract" of being a fluid.modelComponent but
// having an unrelated means of updating the model is fluid.inlineEdit - it should not be used.
fluid.defaults("fluid.undoable", {
gradeNames: ["fluid.modelComponent"],
invokers: {
updateModel: {} // will be implemented by concrete grades
},
events: {
modelChanged: null
}
});