UNPKG

infusion

Version:

Infusion is an application framework for developing flexible stuff with JavaScript

525 lines (474 loc) 20.6 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"; fluid.defaults("fluid.prefs.enactor", { gradeNames: ["fluid.modelComponent"] }); /********************************************************************************** * styleElements * * Adds or removes the classname to/from the elements based upon the model value. * This component is used as a grade by enhanceInputs **********************************************************************************/ fluid.defaults("fluid.prefs.enactor.styleElements", { gradeNames: ["fluid.prefs.enactor"], cssClass: null, // Must be supplied by implementors elementsToStyle: null, // Must be supplied by implementors invokers: { applyStyle: { funcName: "fluid.prefs.enactor.styleElements.applyStyle", args: ["{arguments}.0", "{arguments}.1"] }, resetStyle: { funcName: "fluid.prefs.enactor.styleElements.resetStyle", args: ["{arguments}.0", "{arguments}.1"] }, handleStyle: { funcName: "fluid.prefs.enactor.styleElements.handleStyle", args: ["{arguments}.0", "{that}.options.elementsToStyle", "{that}.options.cssClass", "{that}.applyStyle", "{that}.resetStyle"] } }, modelListeners: { value: { listener: "{that}.handleStyle", args: ["{change}.value"], namespace: "handleStyle" } } }); fluid.prefs.enactor.styleElements.applyStyle = function (elements, cssClass) { elements.addClass(cssClass); }; fluid.prefs.enactor.styleElements.resetStyle = function (elements, cssClass) { $(elements, "." + cssClass).addBack().removeClass(cssClass); }; fluid.prefs.enactor.styleElements.handleStyle = function (value, elements, cssClass, applyStyleFunc, resetStyleFunc) { var func = value ? applyStyleFunc : resetStyleFunc; func(elements, cssClass); }; /******************************************************************************* * ClassSwapper * * Has a hash of classes it cares about and will remove all those classes from * its container before setting the new class. * This component tends to be used as a grade by textFont and contrast *******************************************************************************/ fluid.defaults("fluid.prefs.enactor.classSwapper", { gradeNames: ["fluid.prefs.enactor", "fluid.viewComponent"], classes: {}, // Must be supplied by implementors invokers: { clearClasses: { funcName: "fluid.prefs.enactor.classSwapper.clearClasses", args: ["{that}.container", "{that}.classStr"] }, swap: { funcName: "fluid.prefs.enactor.classSwapper.swap", args: ["{arguments}.0", "{that}", "{that}.clearClasses"] } }, modelListeners: { value: { listener: "{that}.swap", args: ["{change}.value"], namespace: "swapClass" } }, members: { classStr: { expander: { func: "fluid.prefs.enactor.classSwapper.joinClassStr", args: "{that}.options.classes" } } } }); fluid.prefs.enactor.classSwapper.clearClasses = function (container, classStr) { container.removeClass(classStr); }; fluid.prefs.enactor.classSwapper.swap = function (value, that, clearClassesFunc) { clearClassesFunc(); that.container.addClass(that.options.classes[value]); }; fluid.prefs.enactor.classSwapper.joinClassStr = function (classes) { var classStr = ""; fluid.each(classes, function (oneClassName) { if (oneClassName) { classStr += classStr ? " " + oneClassName : oneClassName; } }); return classStr; }; /******************************************************************************* * enhanceInputs * * The enactor to enhance inputs in the container according to the value *******************************************************************************/ // Note that the implementors need to provide the container for this view component fluid.defaults("fluid.prefs.enactor.enhanceInputs", { gradeNames: ["fluid.prefs.enactor.styleElements", "fluid.viewComponent"], preferenceMap: { "fluid.prefs.enhanceInputs": { "model.value": "value" } }, cssClass: null, // Must be supplied by implementors elementsToStyle: "{that}.container" }); /******************************************************************************* * textFont * * The enactor to change the font face used according to the value *******************************************************************************/ // Note that the implementors need to provide the container for this view component fluid.defaults("fluid.prefs.enactor.textFont", { gradeNames: ["fluid.prefs.enactor.classSwapper"], preferenceMap: { "fluid.prefs.textFont": { "model.value": "value" } } }); /******************************************************************************* * contrast * * The enactor to change the contrast theme according to the value *******************************************************************************/ // Note that the implementors need to provide the container for this view component fluid.defaults("fluid.prefs.enactor.contrast", { gradeNames: ["fluid.prefs.enactor.classSwapper"], preferenceMap: { "fluid.prefs.contrast": { "model.value": "value" } } }); /******************************************************************************* * Functions shared by textSize and lineSpace *******************************************************************************/ /** * return "font-size" in px * @param {Object} container - The container to evaluate. * @param {Object} fontSizeMap - The mapping between the font size string values ("small", "medium" etc) to px values. * @return {Number} - The size of the container, in px units. */ fluid.prefs.enactor.getTextSizeInPx = function (container, fontSizeMap) { var fontSize = container.css("font-size"); if (fontSizeMap[fontSize]) { fontSize = fontSizeMap[fontSize]; } // return fontSize in px return parseFloat(fontSize); }; /******************************************************************************* * textRelatedSizer * * Provides an abstraction for enactors that need to adjust sizes based on * a text size value from the DOM. This could include things such as: * font-size, line-height, letter-spacing, and etc. *******************************************************************************/ fluid.defaults("fluid.prefs.enactor.textRelatedSizer", { gradeNames: ["fluid.prefs.enactor", "fluid.viewComponent"], fontSizeMap: {}, // must be supplied by implementors invokers: { set: "fluid.notImplemented", // must be supplied by a concrete implementation getTextSizeInPx: { funcName: "fluid.prefs.enactor.getTextSizeInPx", args: ["{that}.container", "{that}.options.fontSizeMap"] } }, modelListeners: { value: { listener: "{that}.set", args: ["{change}.value"], namespace: "setAdaptation" } } }); /******************************************************************************* * spacingSetter * * Sets the css spacing value on the container to the number of units to * increase the space by. If a negative number is provided, the space between * will decrease. Setting the value to 1 or unit to 0 will use the default. *******************************************************************************/ fluid.defaults("fluid.prefs.enactor.spacingSetter", { gradeNames: ["fluid.prefs.enactor.textRelatedSizer"], members: { originalSpacing: { expander: { func: "{that}.getSpacing" } } }, cssProp: "", invokers: { set: { funcName: "fluid.prefs.enactor.spacingSetter.set", args: ["{that}", "{that}.options.cssProp", "{arguments}.0"] }, getSpacing: { funcName: "fluid.prefs.enactor.spacingSetter.getSpacing", args: ["{that}", "{that}.options.cssProp", "{that}.getTextSizeInPx"] } }, modelListeners: { unit: { listener: "{that}.set", args: ["{change}.value"], namespace: "setAdaptation" }, // Replace default model listener, because `value` needs be transformed before being applied. // The `unit` model value should be used for setting the adaptation. value: { listener: "fluid.identity", namespace: "setAdaptation" } }, modelRelay: { target: "unit", namespace: "toUnit", singleTransform: { type: "fluid.transforms.round", scale: 1, input: { transform: { "type": "fluid.transforms.linearScale", "offset": -1, "input": "{that}.model.value" } } } } }); fluid.prefs.enactor.spacingSetter.getSpacing = function (that, cssProp, getTextSizeFn) { var current = parseFloat(that.container.css(cssProp)); var textSize = getTextSizeFn(); return fluid.roundToDecimal(current / textSize, 2); }; fluid.prefs.enactor.spacingSetter.set = function (that, cssProp, units) { var targetSize = that.originalSpacing; if (units) { targetSize = targetSize + units; } // setting the style value to "" will remove it. var spacingSetter = targetSize ? fluid.roundToDecimal(targetSize, 2) + "em" : ""; that.container.css(cssProp, spacingSetter); }; /******************************************************************************* * textSize * * Sets the text size on the root element to the multiple provided. *******************************************************************************/ // Note that the implementors need to provide the container for this view component fluid.defaults("fluid.prefs.enactor.textSize", { gradeNames: ["fluid.prefs.enactor.textRelatedSizer"], preferenceMap: { "fluid.prefs.textSize": { "model.value": "value" } }, members: { root: { expander: { "this": "{that}.container", "method": "closest", // ensure that the correct document is being used. i.e. in an iframe "args": ["html"] } } }, invokers: { set: { funcName: "fluid.prefs.enactor.textSize.set", args: ["{arguments}.0", "{that}", "{that}.getTextSizeInPx"] }, getTextSizeInPx: { args: ["{that}.root", "{that}.options.fontSizeMap"] } } }); fluid.prefs.enactor.textSize.set = function (times, that, getTextSizeInPxFunc) { times = times || 1; // Calculating the initial size here rather than using a members expand because the "font-size" // cannot be detected on hidden containers such as separated paenl iframe. if (!that.initialSize) { that.initialSize = getTextSizeInPxFunc(); } if (that.initialSize) { var targetSize = times * that.initialSize; that.root.css("font-size", targetSize + "px"); } }; /******************************************************************************* * lineSpace * * Sets the line space on the container to the multiple provided. *******************************************************************************/ // Note that the implementors need to provide the container for this view component fluid.defaults("fluid.prefs.enactor.lineSpace", { gradeNames: ["fluid.prefs.enactor.textRelatedSizer"], preferenceMap: { "fluid.prefs.lineSpace": { "model.value": "value" } }, invokers: { set: { funcName: "fluid.prefs.enactor.lineSpace.set", args: ["{that}", "{arguments}.0"] }, getLineHeight: { funcName: "fluid.prefs.enactor.lineSpace.getLineHeight", args: "{that}.container" }, getLineHeightMultiplier: { funcName: "fluid.prefs.enactor.lineSpace.getLineHeightMultiplier", args: [{expander: {func: "{that}.getLineHeight"}}, {expander: {func: "{that}.getTextSizeInPx"}}] } } }); // Get the line-height of an element // In IE8 and IE9 this will return the line-height multiplier // In other browsers it will return the pixel value of the line height. fluid.prefs.enactor.lineSpace.getLineHeight = function (container) { return container.css("line-height"); }; // Interprets browser returned "line-height" value, either a string "normal", a number with "px" suffix or "undefined" // into a numeric value in em. // Return 0 when the given "lineHeight" argument is "undefined" (http://issues.fluidproject.org/browse/FLUID-4500). fluid.prefs.enactor.lineSpace.getLineHeightMultiplier = function (lineHeight, fontSize) { // Handle the given "lineHeight" argument is "undefined", which occurs when firefox detects // "line-height" css value on a hidden container. (http://issues.fluidproject.org/browse/FLUID-4500) if (!lineHeight) { return 0; } // Needs a better solution. For now, "line-height" value "normal" is defaulted to 1.2em // according to https://developer.mozilla.org/en/CSS/line-height if (lineHeight === "normal") { return 1.2; } // Continuing the work-around of jQuery + IE bug - http://bugs.jquery.com/ticket/2671 if (lineHeight.match(/[0-9]$/)) { return Number(lineHeight); } return fluid.roundToDecimal(parseFloat(lineHeight) / fontSize, 2); }; fluid.prefs.enactor.lineSpace.set = function (that, times) { // Calculating the initial size here rather than using a members expand because the "line-height" // cannot be detected on hidden containers such as separated panel iframe. if (!that.initialSize) { that.initialSize = that.getLineHeight(); that.lineHeightMultiplier = that.getLineHeightMultiplier(); } // that.initialSize === 0 when the browser returned "lineHeight" css value is undefined, // which occurs when firefox detects "line-height" value on a hidden container. // @ See getLineHeightMultiplier() & http://issues.fluidproject.org/browse/FLUID-4500 if (that.lineHeightMultiplier) { var targetLineSpace = that.initialSize === "normal" && times === 1 ? that.initialSize : times * that.lineHeightMultiplier; that.container.css("line-height", targetLineSpace); } }; /******************************************************************************* * tableOfContents * * To create and show/hide table of contents *******************************************************************************/ // Note that the implementors need to provide the container for this view component fluid.defaults("fluid.prefs.enactor.tableOfContents", { gradeNames: ["fluid.prefs.enactor", "fluid.viewComponent"], preferenceMap: { "fluid.prefs.tableOfContents": { "model.toc": "value" } }, tocTemplate: null, // must be supplied by implementors tocMessage: null, // must be supplied by implementors components: { // TODO: When FLUID-6312 and FLUID-6300 are addressed, make sure that this message loader is updated when // the locale is changed. It should also trigger the table of contents to re-render with the // correct message bundle applied. messageLoader: { type: "fluid.resourceLoader", options: { resourceOptions: { dataType: "json" }, events: { onResourcesLoaded: "{fluid.prefs.enactor.tableOfContents}.events.onMessagesLoaded" } } }, tableOfContents: { type: "fluid.tableOfContents", container: "{fluid.prefs.enactor.tableOfContents}.container", createOnEvent: "onCreateTOCReady", options: { listeners: { "afterRender.boilAfterTocRender": "{fluid.prefs.enactor.tableOfContents}.events.afterTocRender" }, strings: { tocHeader: "{messageLoader}.resources.tocMessage.resourceText.tocHeader" } } } }, invokers: { applyToc: { funcName: "fluid.prefs.enactor.tableOfContents.applyToc", args: ["{arguments}.0", "{that}"] } }, events: { afterTocRender: null, onCreateTOC: null, onMessagesLoaded: null, onCreateTOCReady: { events: { onCreateTOC: "onCreateTOC", onMessagesLoaded: "onMessagesLoaded" } } }, modelListeners: { toc: { listener: "{that}.applyToc", args: ["{change}.value"], namespace: "toggleToc" } }, distributeOptions: { "tocEnactor.tableOfContents.ignoreForToC": { source: "{that}.options.ignoreForToC", target: "{that tableOfContents}.options.ignoreForToC" }, "tocEnactor.tableOfContents.tocTemplate": { source: "{that}.options.tocTemplate", target: "{that > tableOfContents > levels}.options.resources.template.url" }, "tocEnactor.messageLoader.tocMessage": { source: "{that}.options.tocMessage", target: "{that messageLoader}.options.resources.tocMessage" } } }); fluid.prefs.enactor.tableOfContents.applyToc = function (value, that) { if (value) { if (that.tableOfContents) { that.tableOfContents.show(); } else { that.events.onCreateTOC.fire(); } } else if (that.tableOfContents) { that.tableOfContents.hide(); } }; })(jQuery, fluid_3_0_0);