UNPKG

infusion

Version:

Infusion is an application framework for developing flexible stuff with JavaScript

544 lines (498 loc) 20.2 kB
/* 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/master/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/master/Infusion-LICENSE.txt */ var fluid_3_0_0 = fluid_3_0_0 || {}; (function ($, fluid) { "use strict"; /***************************** * Preferences Editor Loader * *****************************/ /** * An Preferences Editor top-level component that reflects the collaboration between prefsEditor, templateLoader and messageLoader. * This component is the only Preferences Editor component that is intended to be called by the outside world. * * @param options {Object} */ fluid.defaults("fluid.prefs.prefsEditorLoader", { gradeNames: ["fluid.prefs.settingsGetter", "fluid.prefs.initialModel", "fluid.viewComponent"], defaultLocale: "en", components: { prefsEditor: { priority: "last", type: "fluid.prefs.prefsEditor", createOnEvent: "onCreatePrefsEditorReady", options: { members: { initialModel: "{prefsEditorLoader}.initialModel" }, invokers: { getSettings: "{prefsEditorLoader}.getSettings" }, listeners: { "onReady.boil": { listener: "{prefsEditorLoader}.events.onReady", args: ["{prefsEditorLoader}"] } } } }, templateLoader: { type: "fluid.resourceLoader", options: { events: { onResourcesLoaded: "{prefsEditorLoader}.events.onPrefsEditorTemplatesLoaded" } } }, messageLoader: { type: "fluid.resourceLoader", createOnEvent: "afterInitialSettingsFetched", options: { defaultLocale: "{prefsEditorLoader}.options.defaultLocale", locale: "{prefsEditorLoader}.settings.preferences.locale", resourceOptions: { dataType: "json" }, events: { onResourcesLoaded: "{prefsEditorLoader}.events.onPrefsEditorMessagesLoaded" } } } }, listeners: { "onCreate.getInitialSettings": { listener: "fluid.prefs.prefsEditorLoader.getInitialSettings", args: ["{that}"] } }, events: { afterInitialSettingsFetched: null, onPrefsEditorTemplatesLoaded: null, onPrefsEditorMessagesLoaded: null, onCreatePrefsEditorReady: { events: { templateLoaded: "onPrefsEditorTemplatesLoaded", prefsEditorMessagesLoaded: "onPrefsEditorMessagesLoaded" } }, onReady: null }, distributeOptions: { "prefsEditorLoader.templateLoader": { source: "{that}.options.templateLoader", removeSource: true, target: "{that > templateLoader}.options" }, "prefsEditorLoader.templateLoader.terms": { source: "{that}.options.terms", target: "{that > templateLoader}.options.terms" }, "prefsEditorLoader.messageLoader": { source: "{that}.options.messageLoader", removeSource: true, target: "{that > messageLoader}.options" }, "prefsEditorLoader.messageLoader.terms": { source: "{that}.options.terms", target: "{that > messageLoader}.options.terms" }, "prefsEditorLoader.prefsEditor": { source: "{that}.options.prefsEditor", removeSource: true, target: "{that > prefsEditor}.options" } } }); fluid.prefs.prefsEditorLoader.getInitialSettings = function (that) { var promise = fluid.promise(); var fetchPromise = that.getSettings(); fetchPromise.then(function (savedSettings) { that.settings = $.extend(true, {}, that.initialModel, savedSettings); that.events.afterInitialSettingsFetched.fire(that.settings); }, function (error) { fluid.log(fluid.logLevel.WARN, error); that.settings = that.initialModel; that.events.afterInitialSettingsFetched.fire(that.settings); }); fluid.promise.follow(fetchPromise, promise); return promise; }; // TODO: This mixin grade appears to be supplied manually by various test cases but no longer appears in // the main configuration. We should remove the need for users to supply this - also the use of "defaultPanels" in fact // refers to "starter panels" fluid.defaults("fluid.prefs.transformDefaultPanelsOptions", { // Do not supply "fluid.prefs.inline" here, since when this is used as a mixin for separatedPanel, it ends up displacing the // more refined type of the prefsEditorLoader gradeNames: ["fluid.viewComponent"], distributeOptions: { "transformDefaultPanelsOptions.textSize": { source: "{that}.options.textSize", removeSource: true, target: "{that textSize}.options" }, "transformDefaultPanelsOptions.lineSpace": { source: "{that}.options.lineSpace", removeSource: true, target: "{that lineSpace}.options" }, "transformDefaultPanelsOptions.textFont": { source: "{that}.options.textFont", removeSource: true, target: "{that textFont}.options" }, "transformDefaultPanelsOptions.contrast": { source: "{that}.options.contrast", removeSource: true, target: "{that contrast}.options" }, "transformDefaultPanelsOptions.layoutControls": { source: "{that}.options.layoutControls", removeSource: true, target: "{that layoutControls}.options" }, "transformDefaultPanelsOptions.enhanceInputs": { source: "{that}.options.enhanceInputs", removeSource: true, target: "{that enhanceInputs}.options" } } }); /********************** * Preferences Editor * **********************/ fluid.defaults("fluid.prefs.settingsGetter", { gradeNames: ["fluid.component"], members: { getSettings: "{fluid.prefs.store}.get" } }); fluid.defaults("fluid.prefs.settingsSetter", { gradeNames: ["fluid.component"], invokers: { setSettings: { funcName: "fluid.prefs.settingsSetter.setSettings", args: ["{arguments}.0", "{arguments}.1", "{fluid.prefs.store}.set"] } } }); fluid.prefs.settingsSetter.setSettings = function (model, directModel, set) { var userSettings = fluid.copy(model); return set(directModel, userSettings); }; fluid.defaults("fluid.prefs.uiEnhancerRelay", { gradeNames: ["fluid.modelComponent"], listeners: { "onCreate.addListener": "{that}.addListener", "onDestroy.removeListener": "{that}.removeListener" }, events: { updateEnhancerModel: "{fluid.prefs.prefsEditor}.events.onUpdateEnhancerModel" }, invokers: { addListener: { funcName: "fluid.prefs.uiEnhancerRelay.addListener", args: ["{that}.events.updateEnhancerModel", "{that}.updateEnhancerModel"] }, removeListener: { funcName: "fluid.prefs.uiEnhancerRelay.removeListener", args: ["{that}.events.updateEnhancerModel", "{that}.updateEnhancerModel"] }, updateEnhancerModel: { funcName: "fluid.prefs.uiEnhancerRelay.updateEnhancerModel", args: ["{uiEnhancer}", "{fluid.prefs.prefsEditor}.model.preferences"] } } }); fluid.prefs.uiEnhancerRelay.addListener = function (modelChanged, listener) { modelChanged.addListener(listener); }; fluid.prefs.uiEnhancerRelay.removeListener = function (modelChanged, listener) { modelChanged.removeListener(listener); }; fluid.prefs.uiEnhancerRelay.updateEnhancerModel = function (uiEnhancer, newModel) { uiEnhancer.updateModel(newModel); }; /** * A component that works in conjunction with the UI Enhancer component * to allow users to set personal user interface preferences. The Preferences Editor component provides a user * interface for setting and saving personal preferences, and the UI Enhancer component carries out the * work of applying those preferences to the user interface. * * @param container {Object} * @param options {Object} */ fluid.defaults("fluid.prefs.prefsEditor", { gradeNames: ["fluid.prefs.settingsGetter", "fluid.prefs.settingsSetter", "fluid.prefs.initialModel", "fluid.remoteModelComponent", "fluid.viewComponent"], invokers: { /** * Updates the change applier and fires modelChanged on subcomponent fluid.prefs.controls * * @param newModel {Object} * @param source {Object} */ fetchImpl: { funcName: "fluid.prefs.prefsEditor.fetchImpl", args: ["{that}"] }, writeImpl: { funcName: "fluid.prefs.prefsEditor.writeImpl", args: ["{that}", "{arguments}.0"] }, applyChanges: { funcName: "fluid.prefs.prefsEditor.applyChanges", args: ["{that}"] }, save: { funcName: "fluid.prefs.prefsEditor.save", args: ["{that}"] }, saveAndApply: { funcName: "fluid.prefs.prefsEditor.saveAndApply", args: ["{that}"] }, reset: { funcName: "fluid.prefs.prefsEditor.reset", args: ["{that}"] }, cancel: { funcName: "fluid.prefs.prefsEditor.cancel", args: ["{that}"] } }, selectors: { panels: ".flc-prefsEditor-panel", cancel: ".flc-prefsEditor-cancel", reset: ".flc-prefsEditor-reset", save: ".flc-prefsEditor-save", previewFrame : ".flc-prefsEditor-preview-frame" }, events: { onSave: null, onCancel: null, beforeReset: null, afterReset: null, onAutoSave: null, modelChanged: null, onPrefsEditorRefresh: null, onUpdateEnhancerModel: null, onPrefsEditorMarkupReady: null, onReady: null }, listeners: { "onCreate.init": "fluid.prefs.prefsEditor.init", "onAutoSave.save": "{that}.save" }, model: { local: { preferences: "{that}.model.preferences" } }, modelListeners: { "preferences": [{ listener: "fluid.prefs.prefsEditor.handleAutoSave", args: ["{that}"], namespace: "autoSave", excludeSource: ["init"] }, { listener: "{that}.events.modelChanged.fire", args: ["{change}.value"], namespace: "modelChange" }] }, resources: { template: "{templateLoader}.resources.prefsEditor" }, autoSave: false }); /* * Refresh PrefsEditor */ fluid.prefs.prefsEditor.applyChanges = function (that) { that.events.onUpdateEnhancerModel.fire(); }; fluid.prefs.prefsEditor.fetchImpl = function (that) { var promise = fluid.promise(), fetchPromise = that.getSettings(); fetchPromise.then(function (savedModel) { var completeModel = $.extend(true, {}, that.initialModel, savedModel); promise.resolve(completeModel); }, promise.reject); return promise; }; fluid.prefs.prefsEditor.writeImpl = function (that, modelToSave) { var promise = fluid.promise(), stats = {changes: 0, unchanged: 0, changeMap: {}}, changedPrefs = {}; modelToSave = fluid.copy(modelToSave); // To address https://issues.fluidproject.org/browse/FLUID-4686 fluid.model.diff(modelToSave.preferences, fluid.get(that.initialModel, ["preferences"]), stats); if (stats.changes === 0) { delete modelToSave.preferences; } else { fluid.each(stats.changeMap, function (state, pref) { fluid.set(changedPrefs, pref, modelToSave.preferences[pref]); }); modelToSave.preferences = changedPrefs; } that.events.onSave.fire(modelToSave); var setPromise = that.setSettings(modelToSave); fluid.promise.follow(setPromise, promise); return promise; }; /** * Sends the prefsEditor.model to the store and fires onSave * @param {Object} that: A fluid.prefs.prefsEditor instance * @return {Promise} A promise that will be resolved with the saved model or rejected on error. */ fluid.prefs.prefsEditor.save = function (that) { var promise = fluid.promise(); if (!that.model || $.isEmptyObject(that.model)) { // Don't save a reset model promise.resolve({}); } else { var writePromise = that.write(); fluid.promise.follow(writePromise, promise); } return promise; }; fluid.prefs.prefsEditor.saveAndApply = function (that) { var promise = fluid.promise(); var prevSettingsPromise = that.getSettings(), savePromise = that.save(); prevSettingsPromise.then(function (prevSettings) { savePromise.then(function (changedSelections) { // Only when preferences are changed, re-render panels and trigger enactors to apply changes if (!fluid.model.diff(fluid.get(changedSelections, "preferences"), fluid.get(prevSettings, "preferences"))) { that.events.onPrefsEditorRefresh.fire(); that.applyChanges(); } }); fluid.promise.follow(savePromise, promise); }); return promise; }; /* * Resets the selections to the integrator's defaults and fires afterReset */ fluid.prefs.prefsEditor.reset = function (that) { var transaction = that.applier.initiate(); that.events.beforeReset.fire(that); transaction.fireChangeRequest({path: "preferences", type: "DELETE"}); transaction.change("", fluid.copy(that.initialModel)); transaction.commit(); that.events.onPrefsEditorRefresh.fire(); that.events.afterReset.fire(that); }; /* * Resets the selections to the last saved selections and fires onCancel */ fluid.prefs.prefsEditor.cancel = function (that) { that.events.onCancel.fire(); var fetchPromise = that.fetch(); fetchPromise.then(function () { var transaction = that.applier.initiate(); transaction.fireChangeRequest({path: "preferences", type: "DELETE"}); transaction.change("", that.model.remote); transaction.commit(); that.events.onPrefsEditorRefresh.fire(); }); }; // called once markup is applied to the document containing tab component roots fluid.prefs.prefsEditor.finishInit = function (that) { var bindHandlers = function (that) { var saveButton = that.locate("save"); if (saveButton.length > 0) { saveButton.click(that.saveAndApply); var form = fluid.findForm(saveButton); $(form).submit(function () { that.saveAndApply(); }); } that.locate("reset").click(that.reset); that.locate("cancel").click(that.cancel); }; that.container.append(that.options.resources.template.resourceText); bindHandlers(that); var fetchPromise = that.fetch(); fetchPromise.then(function () { that.events.onPrefsEditorMarkupReady.fire(); that.events.onPrefsEditorRefresh.fire(); that.applyChanges(); that.events.onReady.fire(that); }); }; fluid.prefs.prefsEditor.handleAutoSave = function (that) { if (that.options.autoSave) { that.events.onAutoSave.fire(); } }; fluid.prefs.prefsEditor.init = function (that) { // This setTimeout is to ensure that fetching of resources is asynchronous, // and so that component construction does not run ahead of subcomponents for SeparatedPanel // (FLUID-4453 - this may be a replacement for a branch removed for a FLUID-2248 fix) setTimeout(function () { if (!fluid.isDestroyed(that)) { fluid.prefs.prefsEditor.finishInit(that); } }, 1); }; /****************************** * Preferences Editor Preview * ******************************/ fluid.defaults("fluid.prefs.preview", { gradeNames: ["fluid.viewComponent"], components: { enhancer: { type: "fluid.uiEnhancer", container: "{preview}.enhancerContainer", createOnEvent: "onReady" }, templateLoader: "{templateLoader}" }, invokers: { updateModel: { funcName: "fluid.prefs.preview.updateModel", args: [ "{preview}", "{prefsEditor}.model.preferences" ] } }, events: { onReady: null }, listeners: { "onCreate.startLoadingContainer": "fluid.prefs.preview.startLoadingContainer", "{prefsEditor}.events.modelChanged": { listener: "{that}.updateModel", namespace: "updateModel" }, "onReady.updateModel": "{that}.updateModel" }, templateUrl: "%prefix/PrefsEditorPreview.html" }); fluid.prefs.preview.updateModel = function (that, preferences) { /** * SetTimeout is temp fix for http://issues.fluidproject.org/browse/FLUID-2248 */ setTimeout(function () { if (that.enhancer) { that.enhancer.updateModel(preferences); } }, 0); }; fluid.prefs.preview.startLoadingContainer = function (that) { var templateUrl = that.templateLoader.transformURL(that.options.templateUrl); that.container.on("load", function () { that.enhancerContainer = $("body", that.container.contents()); that.events.onReady.fire(); }); that.container.attr("src", templateUrl); }; })(jQuery, fluid_3_0_0);