UNPKG

infusion

Version:

Infusion is an application framework for developing flexible stuff with JavaScript

545 lines (499 loc) 20.2 kB
/* Copyright 2009 University of Toronto Copyright 2010-2017 OCAD University Copyright 2011 Lucendo Development Ltd. Copyright 2012-2014 Raising the Floor - US Copyright 2015 Raising the Floor - International 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" }, { 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);