From 23ca9113289d033e9b243294e527b24c51fc34f7 Mon Sep 17 00:00:00 2001 From: Cindy Qi Li Date: Thu, 1 Aug 2013 11:32:13 -0400 Subject: [PATCH 1/7] FLUID-5104: The reset button on the full page with preview demo only resets preview window rather than both preview and the main page. Also updated uiOptions component to be more comply with the latest framework. --- .../uiOptions/js/FatPanelUIOptions.js | 6 +- .../uiOptions/js/FullNoPreviewUIOptions.js | 8 +- src/components/uiOptions/js/UIOptions.js | 222 ++++++++++-------- 3 files changed, 135 insertions(+), 101 deletions(-) diff --git a/src/components/uiOptions/js/FatPanelUIOptions.js b/src/components/uiOptions/js/FatPanelUIOptions.js index 0cde9a080d..508d975f1a 100644 --- a/src/components/uiOptions/js/FatPanelUIOptions.js +++ b/src/components/uiOptions/js/FatPanelUIOptions.js @@ -122,8 +122,7 @@ var fluid_1_5 = fluid_1_5 || {}; source: "{that}.options.outerEnhancerOptions", removeSource: true, target: "{that iframeEnhancer}.options" - }, - { + }, { source: "{that}.options.prefix", target: "{that > iframeRenderer}.options.prefix" }] @@ -268,7 +267,8 @@ var fluid_1_5 = fluid_1_5 || {}; onCreate: { listener: "{fatPanel}.bindReset", args: ["{that}.reset"] - } + }, + onReset: "{that}.applyChanges" }, components: { iframeRenderer: "{fatPanel}.iframeRenderer" diff --git a/src/components/uiOptions/js/FullNoPreviewUIOptions.js b/src/components/uiOptions/js/FullNoPreviewUIOptions.js index cbcbb0fd51..dd6efaab66 100644 --- a/src/components/uiOptions/js/FullNoPreviewUIOptions.js +++ b/src/components/uiOptions/js/FullNoPreviewUIOptions.js @@ -36,9 +36,11 @@ var fluid_1_5 = fluid_1_5 || {}; uiOptions: { options: { listeners: { - onReset: function (uiOptions) { - uiOptions.save(); - } + onReset: [{ + listener: "{that}.applyChanges" + }, { + listener: "{that}.save" + }] } } } diff --git a/src/components/uiOptions/js/UIOptions.js b/src/components/uiOptions/js/UIOptions.js index 4dcb79e79f..ea9a7b350a 100644 --- a/src/components/uiOptions/js/UIOptions.js +++ b/src/components/uiOptions/js/UIOptions.js @@ -197,59 +197,6 @@ var fluid_1_5 = fluid_1_5 || {}; }); }; - /** - * A component that works in conjunction with the UI Enhancer component and the Fluid Skinning System (FSS) - * to allow users to set personal user interface preferences. The UI Options 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 {Object} container - * @param {Object} options - */ - fluid.defaults("fluid.uiOptions", { - gradeNames: ["fluid.viewComponent", "fluid.uiOptions.settingsGetter", "fluid.uiOptions.settingsSetter", "fluid.uiOptions.rootModel", "autoInit"], - components: { - eventBinder: { - type: "fluid.uiOptions.eventBinder" - } - }, - invokers: { - /** - * Updates the change applier and fires modelChanged on subcomponent fluid.uiOptions.controls - * - * @param {Object} newModel - * @param {Object} source - */ - updateModel: { - funcName: "fluid.fireSourcedChange", - args: ["{that}.applier", "selections", "{arguments}.0", "{arguments}.1"] - } - }, - selectors: { - cancel: ".flc-uiOptions-cancel", - reset: ".flc-uiOptions-reset", - save: ".flc-uiOptions-save", - previewFrame : ".flc-uiOptions-preview-frame" - }, - events: { - onSave: null, - onCancel: null, - onReset: null, - onAutoSave: null, - modelChanged: null, - onUIOptionsRefresh: null, - onUIOptionsMarkupReady: null, - onUIOptionsComponentReady: null - }, - listeners: { - onAutoSave: "{that}.save" - }, - resources: { - template: "{templateLoader}.resources.uiOptions" - }, - autoSave: false - }); - fluid.defaults("fluid.uiOptions.settingsGetter", { gradeNames: ["fluid.littleComponent", "autoInit"], members: { @@ -337,6 +284,132 @@ var fluid_1_5 = fluid_1_5 || {}; uiEnhancer.updateModel(newModel); }; + /** + * A component that works in conjunction with the UI Enhancer component and the Fluid Skinning System (FSS) + * to allow users to set personal user interface preferences. The UI Options 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 {Object} container + * @param {Object} options + */ + fluid.defaults("fluid.uiOptions", { + gradeNames: ["fluid.viewComponent", "fluid.uiOptions.settingsGetter", "fluid.uiOptions.settingsSetter", "fluid.uiOptions.rootModel", "autoInit"], + components: { + eventBinder: { + type: "fluid.uiOptions.eventBinder" + } + }, + invokers: { + /** + * Updates the change applier and fires modelChanged on subcomponent fluid.uiOptions.controls + * + * @param {Object} newModel + * @param {Object} source + */ + updateModel: { + funcName: "fluid.fireSourcedChange", + args: ["{that}.applier", "selections", "{arguments}.0", "{arguments}.1"] + }, + fetch: { + funcName: "fluid.uiOptions.fetch", + args: ["{that}"] + }, + applyChanges: { + funcName: "fluid.uiOptions.applyChanges", + args: ["{that}"] + }, + save: { + funcName: "fluid.uiOptions.save", + args: ["{that}"] + }, + saveAndApply: { + funcName: "fluid.uiOptions.saveAndApply", + args: ["{that}"] + }, + reset: { + funcName: "fluid.uiOptions.reset", + args: ["{that}"] + }, + cancel: { + funcName: "fluid.uiOptions.cancel", + args: ["{that}"] + } + }, + selectors: { + cancel: ".flc-uiOptions-cancel", + reset: ".flc-uiOptions-reset", + save: ".flc-uiOptions-save", + previewFrame : ".flc-uiOptions-preview-frame" + }, + events: { + onSave: null, + onCancel: null, + onReset: null, + onAutoSave: null, + modelChanged: null, + onUIOptionsRefresh: null, + onUIOptionsMarkupReady: null, + onUIOptionsComponentReady: null + }, + listeners: { + "onCreate": { + listener: "fluid.uiOptions.init", + args: ["{that}"] + }, + onAutoSave: "{that}.save" + }, + resources: { + template: "{templateLoader}.resources.uiOptions" + }, + autoSave: false + }); + + /** + * Refresh UIOptions + */ + fluid.uiOptions.applyChanges = function (that) { + that.events.onUIOptionsRefresh.fire(); + }; + + fluid.uiOptions.fetch = function (that) { + var completeModel = that.getSettings(); + completeModel = $.extend(true, {}, that.rootModel, completeModel); + that.updateModel(completeModel, "settingsStore"); + that.applyChanges(); + }; + + /** + * Saves the current model and fires onSave + */ + fluid.uiOptions.save = function (that) { + that.events.onSave.fire(that.model.selections); + + var savedSelections = fluid.copy(that.model.selections); + that.setSettings(savedSelections); + }; + + fluid.uiOptions.saveAndApply = function (that) { + that.save(); + that.applyChanges(); + }; + + /** + * Resets the selections to the integrator's defaults and fires onReset + */ + fluid.uiOptions.reset = function (that) { + that.updateModel(fluid.copy(that.rootModel)); + that.events.onReset.fire(that); + }; + + /** + * Resets the selections to the last saved selections and fires onCancel + */ + fluid.uiOptions.cancel = function (that) { + that.events.onCancel.fire(); + that.fetch(); + }; + // called once markup is applied to the document containing tab component roots fluid.uiOptions.finishInit = function (that) { var bindHandlers = function (that) { @@ -361,55 +434,14 @@ var fluid_1_5 = fluid_1_5 || {}; that.events.onUIOptionsComponentReady.fire(that); }; - fluid.uiOptions.preInit = function (that) { - that.fetch = function () { - var completeModel = that.getSettings(); - completeModel = $.extend(true, {}, that.rootModel, completeModel); - that.updateModel(completeModel, "settingsStore"); - that.events.onUIOptionsRefresh.fire(); - }; - - /** - * Saves the current model and fires onSave - */ - that.save = function () { - that.events.onSave.fire(that.model.selections); - - var savedSelections = fluid.copy(that.model.selections); - that.setSettings(savedSelections); - }; - - that.saveAndApply = function () { - that.save(); - that.events.onUIOptionsRefresh.fire(); - }; - - /** - * Resets the selections to the integrator's defaults and fires onReset - */ - that.reset = function () { - that.updateModel(fluid.copy(that.rootModel)); - that.events.onReset.fire(that); - that.events.onUIOptionsRefresh.fire(); - }; - - /** - * Resets the selections to the last saved selections and fires onCancel - */ - that.cancel = function () { - that.events.onCancel.fire(); - that.fetch(); - }; - + fluid.uiOptions.init = function (that) { that.applier.modelChanged.addListener("selections", function (newModel, oldModel, changeRequest) { that.events.modelChanged.fire(newModel, oldModel, changeRequest[0].source); if (that.options.autoSave) { that.events.onAutoSave.fire(); } }); - }; - fluid.uiOptions.finalInit = function (that) { fluid.fetchResources(that.options.resources, function () { // This setTimeout is to ensure that fetching of resources is asynchronous, // and so that component construction does not run ahead of subcomponents for FatPanel From 48f3f6c4268227610b24d42deedd4708338904c1 Mon Sep 17 00:00:00 2001 From: Cindy Qi Li Date: Tue, 6 Aug 2013 13:52:41 -0400 Subject: [PATCH 2/7] FLUID-5104: Clicking reset button on the full page with preview demo would reset the adjusters as well as the preview window, without resetting applied settings on the main page. --- src/components/uiOptions/js/UIOptions.js | 13 +++++++------ .../uiOptions/js/IntegrationTestsCommon.js | 4 +++- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/components/uiOptions/js/UIOptions.js b/src/components/uiOptions/js/UIOptions.js index ea9a7b350a..106f04389f 100644 --- a/src/components/uiOptions/js/UIOptions.js +++ b/src/components/uiOptions/js/UIOptions.js @@ -254,7 +254,7 @@ var fluid_1_5 = fluid_1_5 || {}; onDestroy: "{that}.removeListener" }, events: { - updateEnhancerModel: "{fluid.uiOptions}.events.onUIOptionsRefresh" + updateEnhancerModel: "{fluid.uiOptions}.events.onUpdateEnhancerModel" }, invokers: { addListener: { @@ -349,6 +349,7 @@ var fluid_1_5 = fluid_1_5 || {}; onAutoSave: null, modelChanged: null, onUIOptionsRefresh: null, + onUpdateEnhancerModel: null, onUIOptionsMarkupReady: null, onUIOptionsComponentReady: null }, @@ -369,13 +370,14 @@ var fluid_1_5 = fluid_1_5 || {}; * Refresh UIOptions */ fluid.uiOptions.applyChanges = function (that) { - that.events.onUIOptionsRefresh.fire(); + that.events.onUpdateEnhancerModel.fire(); }; fluid.uiOptions.fetch = function (that) { var completeModel = that.getSettings(); completeModel = $.extend(true, {}, that.rootModel, completeModel); that.updateModel(completeModel, "settingsStore"); + that.events.onUIOptionsRefresh.fire(); that.applyChanges(); }; @@ -391,6 +393,7 @@ var fluid_1_5 = fluid_1_5 || {}; fluid.uiOptions.saveAndApply = function (that) { that.save(); + that.events.onUIOptionsRefresh.fire(); that.applyChanges(); }; @@ -399,6 +402,7 @@ var fluid_1_5 = fluid_1_5 || {}; */ fluid.uiOptions.reset = function (that) { that.updateModel(fluid.copy(that.rootModel)); + that.events.onUIOptionsRefresh.fire(); that.events.onReset.fire(that); }; @@ -481,10 +485,7 @@ var fluid_1_5 = fluid_1_5 || {}; enhancer: { type: "fluid.uiEnhancer", container: "{preview}.enhancerContainer", - createOnEvent: "onReady", - options: { - gradeNames: ["fluid.uiOptions.uiEnhancerRelay"] - } + createOnEvent: "onReady" }, // TODO: This is a violation of containment, but we can't use up our allowance of demands // blocks as a result of FLUID-4392 diff --git a/src/tests/component-tests/uiOptions/js/IntegrationTestsCommon.js b/src/tests/component-tests/uiOptions/js/IntegrationTestsCommon.js index 6a95f45a2d..7def48673b 100644 --- a/src/tests/component-tests/uiOptions/js/IntegrationTestsCommon.js +++ b/src/tests/component-tests/uiOptions/js/IntegrationTestsCommon.js @@ -134,6 +134,8 @@ https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt resetButton.click(); fluid.tests.uiOptions.checkModelSelections("model from original", rootModel, uiOptions.model.selections); fluid.tests.uiOptions.applierRequestChanges(uiOptions, fluid.tests.uiOptions.bwSkin); + fluid.tests.uiOptions.checkModelSelections("model from original (correct state after reset)", + (resetShouldSave ? rootModel : fluid.tests.uiOptions.bwSkin), fluid.staticEnvironment.uiEnhancer.model); cancelButton.click(); fluid.tests.uiOptions.checkModelSelections("model from original (correct state after reset and cancel)", @@ -159,7 +161,7 @@ https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt }, uiOptions: { options: { - gradeNames: ["fluid.uiOptions.starterPanels", "fluid.uiOptions.rootModel.starter"], + gradeNames: ["fluid.uiOptions.starterPanels", "fluid.uiOptions.rootModel.starter", "fluid.uiOptions.uiEnhancerRelay"], listeners: { "onSave.munged": testSave } From 7d7fc18488c7b7843ad9b88f1cdb9e47d4a6b138 Mon Sep 17 00:00:00 2001 From: Justin Obara Date: Thu, 8 Aug 2013 13:38:21 -0400 Subject: [PATCH 3/7] FLUID-5110: Moved show and hide to invokers Converted the show and hide methods to be invokers instead of declared in the final init function. --- .../tableOfContents/js/TableOfContents.js | 106 +++++++++--------- 1 file changed, 53 insertions(+), 53 deletions(-) diff --git a/src/components/tableOfContents/js/TableOfContents.js b/src/components/tableOfContents/js/TableOfContents.js index 827818e905..9ac90eba64 100644 --- a/src/components/tableOfContents/js/TableOfContents.js +++ b/src/components/tableOfContents/js/TableOfContents.js @@ -13,20 +13,20 @@ https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt // Declare dependencies /*global fluid_1_5:true, jQuery, window */ -// JSLint options +// JSLint options /*jslint white: true, funcinvoke: true, undef: true, newcap: true, nomen: true, regexp: true, bitwise: true, browser: true, forin: true, maxerr: 100, indent: 4 */ var fluid_1_5 = fluid_1_5 || {}; (function ($, fluid) { - + /****** * ToC * *******/ fluid.registerNamespace("fluid.tableOfContents"); - + fluid.tableOfContents.insertAnchor = function (name, element) { // In order to resolve FLUID-4453, we need to make sure that the owner document is correctly // taken from the target element (the preview may be in an iframe) @@ -37,11 +37,11 @@ var fluid_1_5 = fluid_1_5 || {}; }); anchor.insertBefore(element); }; - + fluid.tableOfContents.generateGUID = function () { return fluid.allocateSimpleId(); }; - + /** * Invoker function to filter headings. Default is to filter out the visible headings. * @param Object Contains a list of headings, usually generated by that.locate("headings") @@ -50,43 +50,34 @@ var fluid_1_5 = fluid_1_5 || {}; fluid.tableOfContents.filterHeadings = function (headings) { return headings.filter(":visible"); }; - + fluid.tableOfContents.finalInit = function (that) { var headings = that.filterHeadings(that.locate("headings")); - + that.headingTextToAnchor = function (heading) { var guid = that.generateGUID(); - + var anchorInfo = { id: guid, url: "#" + guid }; - + that.insertAnchor(anchorInfo.id, heading); return anchorInfo; }; - + that.anchorInfo = fluid.transform(headings, function (heading) { return that.headingTextToAnchor(heading); }); - - // TODO: is it weird to have hide and show on a component? - that.hide = function () { - that.locate("tocContainer").hide(); - }; - - that.show = function () { - that.locate("tocContainer").show(); - }; - + var headingsModel = that.modelBuilder.assembleModel(headings, that.anchorInfo); - + that.applier.requestChange("", headingsModel); that.events.onReady.fire(); }; - - + + fluid.defaults("fluid.tableOfContents", { gradeNames: ["fluid.viewComponent", "autoInit"], finalInitFunction: "fluid.tableOfContents.finalInit", @@ -98,7 +89,7 @@ var fluid_1_5 = fluid_1_5 || {}; options: { model: { headings: "{tableOfContents}.model" - }, + }, events: { afterRender: "{tableOfContents}.events.afterRender" } @@ -112,7 +103,16 @@ var fluid_1_5 = fluid_1_5 || {}; invokers: { insertAnchor: "fluid.tableOfContents.insertAnchor", generateGUID: "fluid.tableOfContents.generateGUID", - filterHeadings: "fluid.tableOfContents.filterHeadings" + filterHeadings: "fluid.tableOfContents.filterHeadings", + // TODO: is it weird to have hide and show on a component? + hide: { + "this": "{that}.dom.tocContainer", + "method": "hide" + }, + show: { + "this": "{that}.dom.tocContainer", + "method": "show" + } }, selectors: { headings: ":header", @@ -123,13 +123,13 @@ var fluid_1_5 = fluid_1_5 || {}; afterRender: null } }); - - + + /******************* * ToC ModelBuilder * ********************/ fluid.registerNamespace("fluid.tableOfContents.modelBuilder"); - + fluid.tableOfContents.modelBuilder.toModel = function (headingInfo, modelLevelFn) { var headings = fluid.copy(headingInfo); var buildModelLevel = function (headings, level) { @@ -148,7 +148,7 @@ var fluid_1_5 = fluid_1_5 || {}; } } if (heading.level === level) { - modelLevel.push(heading); + modelLevel.push(heading); headings.shift(); } } @@ -156,9 +156,9 @@ var fluid_1_5 = fluid_1_5 || {}; }; return buildModelLevel(headings, 1); }; - + fluid.tableOfContents.modelBuilder.gradualModelLevelFn = function (modelLevel, subHeadings) { - // Clone the subHeadings because we don't want to modify the reference of the subHeadings. + // Clone the subHeadings because we don't want to modify the reference of the subHeadings. // the reference will affect the equality condition in generateTree(), resulting an unwanted tree. var subHeadingsClone = fluid.copy(subHeadings); subHeadingsClone[0].level--; @@ -169,9 +169,9 @@ var fluid_1_5 = fluid_1_5 || {}; modelLevel.push({headings: subHeadings}); return modelLevel; }; - + fluid.tableOfContents.modelBuilder.finalInit = function (that) { - + that.convertToHeadingObjects = function (headings, anchorInfo) { headings = $(headings); return fluid.transform(headings, function (heading, index) { @@ -182,13 +182,13 @@ var fluid_1_5 = fluid_1_5 || {}; }; }); }; - + that.assembleModel = function (headings, anchorInfo) { var headingInfo = that.convertToHeadingObjects(headings, anchorInfo); return that.toModel(headingInfo); }; }; - + fluid.defaults("fluid.tableOfContents.modelBuilder", { gradeNames: ["fluid.littleComponent", "autoInit"], finalInitFunction: "fluid.tableOfContents.modelBuilder.finalInit", @@ -205,34 +205,34 @@ var fluid_1_5 = fluid_1_5 || {}; modelLevelFn: "fluid.tableOfContents.modelBuilder.gradualModelLevelFn" } }); - + /************************************* * ToC ModelBuilder headingCalculator * **************************************/ fluid.registerNamespace("fluid.tableOfContents.modelBuilder.headingCalculator"); - + fluid.tableOfContents.modelBuilder.headingCalculator.finalInit = function (that) { that.getHeadingLevel = function (heading) { return $.inArray(heading.tagName, that.options.levels) + 1; }; }; - + fluid.defaults("fluid.tableOfContents.modelBuilder.headingCalculator", { gradeNames: ["fluid.littleComponent", "autoInit"], finalInitFunction: "fluid.tableOfContents.modelBuilder.headingCalculator.finalInit", levels: ["H1", "H2", "H3", "H4", "H5", "H6"] }); - + /************* * ToC Levels * **************/ fluid.registerNamespace("fluid.tableOfContents.levels"); - + fluid.tableOfContents.levels.finalInit = function (that) { fluid.fetchResources(that.options.resources, function () { that.container.append(that.options.resources.template.resourceText); that.refreshView(); - }); + }); }; /** @@ -248,8 +248,8 @@ var fluid_1_5 = fluid_1_5 || {}; }; return objModel; }; - - /** + + /** * Configure item object when item object has no text, uri, level in it. * defaults to add a decorator to hide the bullets. */ @@ -259,7 +259,7 @@ var fluid_1_5 = fluid_1_5 || {}; classes: "fl-tableOfContents-hide-bullet" }]; }; - + /** * @param Object that.model, the model with all the headings, it should be in the format of {headings: [...]} * @param int the current level we want to generate the tree for. default to 1 if not defined. @@ -268,12 +268,12 @@ var fluid_1_5 = fluid_1_5 || {}; fluid.tableOfContents.levels.generateTree = function (headingsModel, currentLevel) { currentLevel = currentLevel || 0; var levelObj = fluid.tableOfContents.levels.objModel("level", currentLevel); - + // FLUID-4352, run generateTree iff there are headings in the model. if (headingsModel.headings.length === 0) { return []; } - + // base case: level is 0, returns {children:[generateTree(nextLevel)]} // purpose is to wrap the first level with a children object. if (currentLevel === 0) { @@ -284,7 +284,7 @@ var fluid_1_5 = fluid_1_5 || {}; }; return tree; } - + // Loop through the heading array, which can have multiple headings on the same level $.each(headingsModel.headings, function (index, model) { var itemObj = fluid.tableOfContents.levels.objModel("items", currentLevel); @@ -293,7 +293,7 @@ var fluid_1_5 = fluid_1_5 || {}; target: model.url, linktext: model.text }; - + // If level is undefined, then add decorator to it, otherwise add the links to it. if (!model.level) { fluid.tableOfContents.levels.handleEmptyItemObj(itemObj); @@ -309,14 +309,14 @@ var fluid_1_5 = fluid_1_5 || {}; }); return levelObj; }; - - /** + + /** * @return Object Returned produceTree must be in {headings: [trees]} */ fluid.tableOfContents.levels.produceTree = function (that) { return fluid.tableOfContents.levels.generateTree(that.model); }; - + fluid.defaults("fluid.tableOfContents.levels", { gradeNames: ["fluid.rendererComponent", "autoInit"], finalInitFunction: "fluid.tableOfContents.levels.finalInit", @@ -339,7 +339,7 @@ var fluid_1_5 = fluid_1_5 || {}; link3: ".flc-toc-levels-link3", link4: ".flc-toc-levels-link4", link5: ".flc-toc-levels-link5", - link6: ".flc-toc-levels-link6" + link6: ".flc-toc-levels-link6" }, repeatingSelectors: ["level1", "level2", "level3", "level4", "level5", "level6", "items1", "items2", "items3", "items4", "items5", "items6"], model: { @@ -350,7 +350,7 @@ var fluid_1_5 = fluid_1_5 || {}; forceCache: true, url: "../html/TableOfContents.html" } - }, + }, rendererFnOptions: { noexpand: true }, From 1eb8d4181dfe3f7470345c29caf0db6a33da1d66 Mon Sep 17 00:00:00 2001 From: Justin Obara Date: Fri, 9 Aug 2013 11:01:40 -0400 Subject: [PATCH 4/7] FLUID-5110: Refactored to all for refresh Refactored to allow the table of contents to be refreshed. Still need to remove all of the old anchors so that we don't pollute the DOM when refreshing. --- .../tableOfContents/js/TableOfContents.js | 75 ++--- .../js/table-of-contents-example.js | 8 +- .../js/TableOfContentsTests.js | 256 +++++++++--------- 3 files changed, 179 insertions(+), 160 deletions(-) diff --git a/src/components/tableOfContents/js/TableOfContents.js b/src/components/tableOfContents/js/TableOfContents.js index 9ac90eba64..4b53054459 100644 --- a/src/components/tableOfContents/js/TableOfContents.js +++ b/src/components/tableOfContents/js/TableOfContents.js @@ -26,7 +26,6 @@ var fluid_1_5 = fluid_1_5 || {}; fluid.registerNamespace("fluid.tableOfContents"); - fluid.tableOfContents.insertAnchor = function (name, element) { // In order to resolve FLUID-4453, we need to make sure that the owner document is correctly // taken from the target element (the preview may be in an iframe) @@ -38,60 +37,49 @@ var fluid_1_5 = fluid_1_5 || {}; anchor.insertBefore(element); }; - fluid.tableOfContents.generateGUID = function () { - return fluid.allocateSimpleId(); - }; + fluid.tableOfContents.headingTextToAnchorInfo = function (heading, guidFunc) { + var guid = guidFunc(); - /** - * Invoker function to filter headings. Default is to filter out the visible headings. - * @param Object Contains a list of headings, usually generated by that.locate("headings") - * @return filtered headings - */ - fluid.tableOfContents.filterHeadings = function (headings) { - return headings.filter(":visible"); - }; - - fluid.tableOfContents.finalInit = function (that) { - var headings = that.filterHeadings(that.locate("headings")); - - that.headingTextToAnchor = function (heading) { - var guid = that.generateGUID(); + var anchorInfo = { + id: guid, + url: "#" + guid + }; - var anchorInfo = { - id: guid, - url: "#" + guid - }; + return anchorInfo; + }; - that.insertAnchor(anchorInfo.id, heading); - return anchorInfo; - }; + fluid.tableOfContents.refreshView = function (that) { + var headings = that.locate("headings"); that.anchorInfo = fluid.transform(headings, function (heading) { - return that.headingTextToAnchor(heading); + var info = that.headingTextToAnchorInfo(heading); + that.insertAnchor(info.id, heading); + return info; }); var headingsModel = that.modelBuilder.assembleModel(headings, that.anchorInfo); that.applier.requestChange("", headingsModel); - that.events.onReady.fire(); + that.events.onRefresh.fire(); }; - fluid.defaults("fluid.tableOfContents", { gradeNames: ["fluid.viewComponent", "autoInit"], - finalInitFunction: "fluid.tableOfContents.finalInit", components: { levels: { type: "fluid.tableOfContents.levels", + createOnEvent: "onCreate", container: "{tableOfContents}.dom.tocContainer", - createOnEvent: "onReady", options: { model: { headings: "{tableOfContents}.model" }, events: { afterRender: "{tableOfContents}.events.afterRender" + }, + listeners: { + "{tableOfContents}.events.onRefresh": "{that}.refreshView" } } }, @@ -101,9 +89,16 @@ var fluid_1_5 = fluid_1_5 || {}; }, model: [], invokers: { + headingTextToAnchorInfo: { + funcName: "fluid.tableOfContents.headingTextToAnchorInfo", + args: ["{arguments}.0", "{that}.generateGUID"] + }, insertAnchor: "fluid.tableOfContents.insertAnchor", - generateGUID: "fluid.tableOfContents.generateGUID", - filterHeadings: "fluid.tableOfContents.filterHeadings", + generateGUID: "fluid.allocateSimpleId", + refreshView: { + funcName: "fluid.tableOfContents.refreshView", + args: ["{that}"] + }, // TODO: is it weird to have hide and show on a component? hide: { "this": "{that}.dom.tocContainer", @@ -115,12 +110,22 @@ var fluid_1_5 = fluid_1_5 || {}; } }, selectors: { - headings: ":header", + headings: ":header:visible:not(.flc-toc-tocContainer :header)", tocContainer: ".flc-toc-tocContainer" }, events: { - onReady: null, - afterRender: null + onRefresh: null, + afterRender: null, + onReady: { + events: { + "onCreate": "onCreate", + "afterRender": "afterRender" + }, + args: ["{that}"] + } + }, + listeners: { + "onCreate.refreshView": "{that}.refreshView" } }); diff --git a/src/standalone-demos/table-of-contents/js/table-of-contents-example.js b/src/standalone-demos/table-of-contents/js/table-of-contents-example.js index 3ffbf6bb09..8500ba7352 100644 --- a/src/standalone-demos/table-of-contents/js/table-of-contents-example.js +++ b/src/standalone-demos/table-of-contents/js/table-of-contents-example.js @@ -12,14 +12,14 @@ https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt // Declare dependencies /*global demo:true, fluid, jQuery*/ -// JSLint options +// JSLint options /*jslint white: true, funcinvoke: true, undef: true, newcap: true, nomen: true, regexp: true, bitwise: true, browser: true, forin: true, maxerr: 100, indent: 4 */ var demo = demo || {}; (function ($, fluid) { demo.initTableOfContent = function () { - fluid.staticEnvironment.demo = fluid.typeTag("fluid.tableOfContentsDemo"); + fluid.staticEnvironment.demo = fluid.typeTag("fluid.tableOfContentsDemo"); fluid.demands("fluid.tableOfContents.levels", ["fluid.tableOfContents", "fluid.tableOfContentsDemo"], { options: { resources: { @@ -30,7 +30,7 @@ var demo = demo || {}; } } }); - + fluid.tableOfContents("body"); - }; + }; })(jQuery, fluid); diff --git a/src/tests/component-tests/tableOfContents/js/TableOfContentsTests.js b/src/tests/component-tests/tableOfContents/js/TableOfContentsTests.js index 0d6ecd534c..f867c9277a 100644 --- a/src/tests/component-tests/tableOfContents/js/TableOfContentsTests.js +++ b/src/tests/component-tests/tableOfContents/js/TableOfContentsTests.js @@ -12,7 +12,7 @@ https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt // Declare dependencies /*global fluid, jqUnit, QUnit, jQuery */ -// JSLint options +// JSLint options /*jslint white: true, funcinvoke: true, undef: true, newcap: true, nomen: true, regexp: true, bitwise: true, browser: true, forin: true, maxerr: 100, indent: 4 */ (function ($) { @@ -28,16 +28,16 @@ https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt } } }); - + fluid.tableOfContents.generateGUIDMock = function () { return 'test'; }; - + // Use our custom GUID for testing purposes. - fluid.demands("fluid.tableOfContents.generateGUID", "fluid.tableOfContents", { + fluid.demands("fluid.allocateSimpleId", "fluid.tableOfContents", { funcName: 'fluid.tableOfContents.generateGUIDMock' }); - + /* For testing a page with no headings */ var emptyHeadings = { headingTags: [], @@ -45,7 +45,7 @@ https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt headingInfo: [], model: [] }; - + /* TODO: Might want to rename this and "skippedHeadingsForGradualIndentationModel" */ var skippedHeadingsForSkippedIndentationModel = { headingTags: ["h1", "h6"], @@ -55,16 +55,16 @@ https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt {level: 6, text: "h6", url: "#h6"} ], model: [{ - level: 1, - text: "h1", + level: 1, + text: "h1", url: "#h1", headings: [{ headings: [{ headings: [{ headings: [{ headings: [{ - level: 6, - text: "h6", + level: 6, + text: "h6", url: "#h6" }] }] @@ -73,7 +73,7 @@ https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt }] }] }; - + /* TODO: Might want to rename this and "skippedHeadingsForSkippedIndentationModel" */ var skippedHeadingsForGradualIndentationModel = { headingTags: ["h1", "h6"], @@ -83,17 +83,17 @@ https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt {level: 6, text: "h6", url: "#h6"} ], model: [{ - level: 1, - text: "h1", + level: 1, + text: "h1", url: "#h1", headings: [{ - level: 2, - text: "h6", + level: 2, + text: "h6", url: "#h6" }] }] }; - + var linearHeadings = { headingTags: ["h1", "h2", "h3", "h4", "h5", "h6"], anchorInfo: [{url: "#h1"}, {url: "#h2"}, {url: "#h3"}, {url: "#h4"}, {url: "#h5"}, {url: "#h6"}], @@ -106,8 +106,8 @@ https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt {level: 6, text: "h6", url: "#h6"} ], model: [{ - level: 1, - text: "h1", + level: 1, + text: "h1", url: "#h1", headings: [{ level: 2, @@ -126,8 +126,8 @@ https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt text: "h5", url: "#h5", headings: [{ - level: 6, - text: "h6", + level: 6, + text: "h6", url: "#h6" }] }] @@ -136,10 +136,10 @@ https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt }] }] }; - + var skippedHeadingsForSkippedIndentationTree = { - children: [{ - ID: "level1:", + children: [{ + ID: "level1:", children: [{ ID: "items1:", children: [{ @@ -200,10 +200,10 @@ https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt }] }] }; - + var skippedHeadingsForGradualIndentationTree = { - children: [{ - ID: "level1:", + children: [{ + ID: "level1:", children: [{ ID: "items1:", children: [{ @@ -224,38 +224,38 @@ https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt }] }] }; - + var createElm = function (tagName) { return fluid.unwrap($("<" + tagName + "/>", {text: tagName})); }; - + var createElms = function (tagArray) { return fluid.transform(tagArray, createElm); }; - + var toModelTests = function (headingInfo, expectedModel, modelLevelFn) { var model = fluid.tableOfContents.modelBuilder.toModel(headingInfo, modelLevelFn); jqUnit.assertDeepEq("headingInfo converted to toModel correctly", model, expectedModel); }; - + var convertToHeadingObjectsTests = function (headings, anchorInfo, expectedHeadingInfo) { var modelBuilder = fluid.tableOfContents.modelBuilder(); var headingInfo = modelBuilder.convertToHeadingObjects(headings, anchorInfo); jqUnit.assertDeepEq("Heading objects created correctly", headingInfo, expectedHeadingInfo); }; - + var assembleModelTests = function (headings, anchorInfo, expectedModel) { var modelBuilder = fluid.tableOfContents.modelBuilder(); var model = modelBuilder.assembleModel(headings, anchorInfo); jqUnit.assertDeepEq("Model assembled correctly", model, expectedModel); }; - + var generateTreeTests = function (model, expectedTree) { model = {headings: model}; var tree = fluid.tableOfContents.levels.generateTree(model); jqUnit.assertDeepEq("tree generated correctly", tree, expectedTree); }; - + /** * Retrieve a jquery that's associated with all the selectorNames * @param Array Array of selector names @@ -267,7 +267,7 @@ https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt }); return set; }; - + var renderTOCTest = function (that, testHeadings) { var tocLinks = locateSet(that, ["link1", "link2", "link3", "link4", "link5", "link6"]); jqUnit.assertEquals("The correct number of links are rendered", testHeadings.headingInfo.length, tocLinks.length); @@ -278,7 +278,7 @@ https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt fluid.each(tocLinks, function (elm, idx) { var hInfo = testHeadings.headingInfo[idx]; elm = $(elm); - + jqUnit.assertEquals("ToC text set correctly", fluid.get(hInfo, "text"), elm.text()); // To address IE7 problem, http://bugs.jquery.com/ticket/7117 // To fix, strip it URI if the windows.location is in href. Otherwise, do nothing. @@ -286,7 +286,7 @@ https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt jqUnit.assertEquals("ToC anchor set correctly", fluid.get(hInfo, "url"), eleHref); }); }; - + var renderTOCTests = function (testHeadings) { var container = $(".flc-toc-tocContainer", "#flc-toc"); fluid.tableOfContents.levels(container, { @@ -307,7 +307,7 @@ https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt } }); }; - + /** * Returns a ToC Component with the predefined demand block * defaults container is #flc-toc @@ -318,12 +318,12 @@ https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt }; $(document).ready(function () { - + jqUnit.module("Table of Contents: Heading Calculator Tests"); - + jqUnit.test("getHeadingLevel", function () { var headingCalc = fluid.tableOfContents.modelBuilder.headingCalculator(); - + for (var i = 1; i <= 6; i++) { var tagName = "h" + i; var heading = createElm(tagName); @@ -332,8 +332,8 @@ https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt }); - jqUnit.module("Table of Contents: Model Builder Tests"); - + jqUnit.module("Table of Contents: Model Builder Tests"); + jqUnit.test("toModel: linear headings with gradual indentation models", function () { toModelTests(linearHeadings.headingInfo, linearHeadings.model, fluid.tableOfContents.modelBuilder.gradualModelLevelFn); }); @@ -345,52 +345,52 @@ https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt fluid.tableOfContents.modelBuilder.gradualModelLevelFn); }); jqUnit.test("toModel: skipped headings with skipped indentation models", function () { - toModelTests(skippedHeadingsForSkippedIndentationModel.headingInfo, skippedHeadingsForSkippedIndentationModel.model, + toModelTests(skippedHeadingsForSkippedIndentationModel.headingInfo, skippedHeadingsForSkippedIndentationModel.model, fluid.tableOfContents.modelBuilder.skippedModelLevelFn); }); - + jqUnit.test("convertToHeadingObjects: linear headings", function () { convertToHeadingObjectsTests(createElms(linearHeadings.headingTags), linearHeadings.anchorInfo, linearHeadings.headingInfo); }); jqUnit.test("convertToHeadingObjects: skipped headings", function () { - convertToHeadingObjectsTests(createElms(skippedHeadingsForSkippedIndentationModel.headingTags), + convertToHeadingObjectsTests(createElms(skippedHeadingsForSkippedIndentationModel.headingTags), skippedHeadingsForSkippedIndentationModel.anchorInfo, skippedHeadingsForSkippedIndentationModel.headingInfo); - convertToHeadingObjectsTests(createElms(skippedHeadingsForGradualIndentationModel.headingTags), + convertToHeadingObjectsTests(createElms(skippedHeadingsForGradualIndentationModel.headingTags), skippedHeadingsForGradualIndentationModel.anchorInfo, skippedHeadingsForGradualIndentationModel.headingInfo); }); - + jqUnit.test("assembleModel: linear headings", function () { assembleModelTests(createElms(linearHeadings.headingTags), linearHeadings.anchorInfo, linearHeadings.model); }); jqUnit.test("assembleModel: skipped headings", function () { // test assembleModel with default toModel invoker - skippedHeadingsForGradualIndentationModel - assembleModelTests(createElms(skippedHeadingsForGradualIndentationModel.headingTags), + assembleModelTests(createElms(skippedHeadingsForGradualIndentationModel.headingTags), skippedHeadingsForGradualIndentationModel.anchorInfo, skippedHeadingsForGradualIndentationModel.model); }); - + jqUnit.test("Test gradualModelLevelFn", function () { var modelLevel = ['level1', 'level2']; var subHeadings = [{level: 6, text: 'h6', url: '#h6'}]; var expectedModelLevel = [{level: 5, text: 'h6', url: '#h6'}]; - + var gradualIndentationModel = fluid.tableOfContents.modelBuilder.gradualModelLevelFn(modelLevel, subHeadings); jqUnit.assertDeepEq("gradual indentation model returns the subHeadings with level decremented by exactly 1.", gradualIndentationModel, expectedModelLevel); - + //reference check. The function should not modify the object with the same reference. jqUnit.assertFalse("This function should not modify the level value directly on the object. Returned value should not have the same reference as parameter.", subHeadings === gradualIndentationModel); - }); - + }); + jqUnit.test("Test skippedModelLevelFn", function () { var modelLevel = ['level1', 'level2', {headings: ['subHeading1', 'subHeading2']}]; var modelLevelClone = fluid.copy(modelLevel); var subHeadings = modelLevelClone.pop(); var skippedIndentationModel = fluid.tableOfContents.modelBuilder.skippedModelLevelFn(modelLevelClone, subHeadings.headings); jqUnit.assertDeepEq("skipped indentation model should always return the modelLevel with subHeadings as a child.", modelLevel, skippedIndentationModel); - }); - - + }); + + jqUnit.module("Table of Contents: Levels Tests"); - + jqUnit.test("generateTree: skipped indentation tree, [h1, '', '', '', '', h6]", function () { generateTreeTests(skippedHeadingsForSkippedIndentationModel.model, skippedHeadingsForSkippedIndentationTree); }); @@ -400,23 +400,23 @@ https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt jqUnit.test("generateTree: empty tree, []", function () { generateTreeTests([], []); }); - - + + jqUnit.test("objModel: test construction of the levels, items object used by generateTree", function () { var levelObj = fluid.tableOfContents.levels.objModel('level', 1); jqUnit.assertEquals("The last character of the ID should be a ':'", ":", levelObj.ID.substr(levelObj.ID.length - 1)); jqUnit.assertEquals("Should create an empty children array", 0, levelObj.children.length); }); - + jqUnit.test("handleEmptyItemObj: Add decorator to item object", function () { var itemObj = {}; fluid.tableOfContents.levels.handleEmptyItemObj(itemObj); var decorator = itemObj.decorators[0]; jqUnit.assertEquals("Decorator.type is 'addClass'", "addClass", decorator.type); jqUnit.assertEquals("Decorator.classes is 'fl-tableOfContents-hide-bullet'", "fl-tableOfContents-hide-bullet", decorator.classes); - + }); - + jqUnit.asyncTest("Render toc: empty headings", function () { //FLUID-4352 renderTOCTests(emptyHeadings); @@ -442,37 +442,20 @@ https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt // check if the anchor is inserted before the element, in that case, index 0 should be the anchor // the first-child test below assumes that there is only 2 element in the wrapper, including the inserted anchor element. var tocInsertAnchorWrapperFirstChild = $('#tocInsertAnchorWrapper :first-child'); - jqUnit.assertEquals("ToC insert anchor correctly: id", tocTestAnchorName, tocInsertAnchorWrapperFirstChild.prop('id')); + jqUnit.assertEquals("ToC insert anchor correctly: id", tocTestAnchorName, tocInsertAnchorWrapperFirstChild.prop('id')); jqUnit.assertEquals("ToC insert anchor correctly: name", tocTestAnchorName, tocInsertAnchorWrapperFirstChild.attr('name')); }); - - jqUnit.test("generateGUID", function () { - var GUID = fluid.tableOfContents.generateGUID(); - var GUID2 = fluid.tableOfContents.generateGUID(); - - jqUnit.assertNotEquals("GUID should not be the same with the same randomBaseName", GUID, GUID2); - }); - - jqUnit.test("filterHeadings", function () { - var allHeadings = $('#tocFilterHeadings :header'); - var expectedHeadings = allHeadings.not(":hidden"); - var filteredHeadings = fluid.tableOfContents.filterHeadings(allHeadings); - jqUnit.assertEquals("The size of headings should be exactly 1 less from the original", allHeadings.size() - 1, filteredHeadings.size()); - jqUnit.assertEquals("The size of headings should be exactly the same as a sliced clone of the original", expectedHeadings.size(), filteredHeadings.size()); - // Use QUnit's native deepEqual to avoid cloning problems in qunit's propEqual on Safari - QUnit.deepEqual(expectedHeadings.toArray(), filteredHeadings.toArray(), "The headings object array should be identical between the filtered headings and the sliced clone"); - }); - - jqUnit.test("finalInit public function: headingTextToAnchor", function () { + + jqUnit.test("finalInit public function: headingTextToAnchorInfo", function () { // setup and init the ToC component var toc = renderTOCComponent(); var tocBodyHeading = $('#amphibians'); - var anchorInfo = toc.headingTextToAnchor(tocBodyHeading); - + var anchorInfo = toc.headingTextToAnchorInfo(tocBodyHeading); + // test goes here jqUnit.assertEquals("anchor url is the same as id except url has a '#' in front", anchorInfo.url.substr(1), anchorInfo.id); }); - + jqUnit.test("finalInit public function: show/hide component", function () { //setup and init the ToC component var tocContainer = renderTOCComponent().locate("tocContainer"); @@ -484,7 +467,7 @@ https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt //verify toc is visible again jqUnit.isVisible("After calling show, the component is visible.", tocContainer); }); - + /** * Test anchor links created by TOC. Check if the heading table a href link maps to the correct header * @precondition Must be rendered @@ -496,7 +479,7 @@ https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt jqUnit.assertTrue("Component test headings: TOC anchors should map to the headers correctly - " + anchorHref, $(anchorHref)[0]); }); }; - + /** * Test component and make sure the number of links, text and anchors are set correctly. */ @@ -513,40 +496,25 @@ https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt headings.each(function (headingsIndex, headingsInfo) { var currLink = headings.eq(headingsIndex); testHeadings.headingInfo.push(serializeHeading( - currLink.prop('tagName').substr(currLink.prop('tagName').length - 1), - currLink.text(), + currLink.prop('tagName').substr(currLink.prop('tagName').length - 1), + currLink.text(), '#test' )); }); renderTOCComponent("#flc-toc", { listeners: { - afterRender: function (that) { - renderTOCTest(that, testHeadings); - renderTOCAnchorTest(); - jqUnit.start(); + onReady: { + func: function (that) { + renderTOCTest(that, testHeadings); + renderTOCAnchorTest(); + jqUnit.start(); + }, + args: ["{that}.levels"] } } }); - - /* - * the following is to demonstrated what the headingInfo should be like - * should remove this once code reviewed. - * - componentHeadings.headingInfo = [ - {level: 1, text: "Amphibians", url: "#toc_Amphibians_14"}, - {level: 2, text: "Toads", url: "#toc_Toads_15"}, - {level: 3, text: "Natterjack Toads", url: "#toc_Natterjack-Toads_16"}, - {level: 2, text: "Salamander", url: "#toc_Salamander_17"}, - {level: 2, text: "Newt", url: "#toc_Newt_18"}, - {level: 1, text: "Birds", url: "#toc_Birds_19"}, - {level: 2, text: "Anseriformes", url: "#toc_Anseriformes_20"}, - {level: 3, text: "Ducks", url: "#toc_Ducks_21"}, - {level: 1, text: "Mammals", url: "#toc_Mammals_22"}, - {level: 3, text: "CATT", url: "#toc_CATT_23"} - ]; - */ }); - + /** * #FLUID-4352: Test component with no headings. Make sure no
    is set */ @@ -557,10 +525,13 @@ https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt }; renderTOCComponent("#flc-toc-noHeaders", { listeners: { - afterRender: function (that) { - renderTOCTest(that, testHeadings); - renderTOCAnchorTest(); - jqUnit.start(); + onReady: { + func: function (that) { + renderTOCTest(that, testHeadings); + renderTOCAnchorTest(); + jqUnit.start(); + }, + args: ["{that}.levels"] } } }); @@ -573,11 +544,54 @@ https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt jqUnit.asyncTest("Output includes a heading", function () { renderTOCComponent("#flc-toc", { listeners: { - afterRender: function (that) { - var header = $("h1", that.container); - jqUnit.assertEquals("The output should contain exactly one H1", 1, header.length); - jqUnit.assertEquals("The H1 should contain the expected text", "Table of Contents", header.text()); - jqUnit.start(); + onReady: { + func: function (that) { + var header = $("h1", that.container); + jqUnit.assertEquals("The output should contain exactly one H1", 1, header.length); + jqUnit.assertEquals("The H1 should contain the expected text", "Table of Contents", header.text()); + jqUnit.start(); + }, + args: ["{that}.levels"] + } + } + }); + }); + + /** + * #FLUID-5110: refreshView updates headings + */ + jqUnit.asyncTest("Component test refreshView", function () { + // craft headingInfo so renderTOCTest() can use it + var testHeadingsStart = { + headingInfo : [] + }; + var testHeadingRefreshed = { + headingInfo: [{ + level: "1", + text: "test", + url: "#test" + }] + } + renderTOCComponent("#flc-toc-noHeaders", { + listeners: { + //FLUID-5112: have to use the onCreate event instead of onReady to prevent infinite recursion. + "onCreate.intialState": { + listener: function (levels, that) { + renderTOCTest(levels, testHeadingsStart); + renderTOCAnchorTest(); + + that.events.onRefresh.addListener(function () { + jqUnit.assert("The onRefresh event should have fired"); + renderTOCTest(levels, testHeadingRefreshed); + renderTOCAnchorTest(); + jqUnit.start(); + }, "inTestCase", null, "last"); + + that.container.append("

    test

    "); + that.refreshView(); + }, + args: ["{that}.levels", "{that}"], + priority: "last" } } }); From 84861737fff4cec0db4575c5497881223f9d1012 Mon Sep 17 00:00:00 2001 From: Justin Obara Date: Fri, 9 Aug 2013 11:32:57 -0400 Subject: [PATCH 5/7] FLUID-5110: Removing stale anchors The ToC will remove it's stale anchors on refresh. --- .../tableOfContents/js/TableOfContents.js | 13 ++++++---- .../html/TableOfContents-test.html | 19 +++++++++------ .../js/TableOfContentsTests.js | 24 ++++++++++++------- 3 files changed, 37 insertions(+), 19 deletions(-) diff --git a/src/components/tableOfContents/js/TableOfContents.js b/src/components/tableOfContents/js/TableOfContents.js index 4b53054459..13769d39fa 100644 --- a/src/components/tableOfContents/js/TableOfContents.js +++ b/src/components/tableOfContents/js/TableOfContents.js @@ -26,11 +26,12 @@ var fluid_1_5 = fluid_1_5 || {}; fluid.registerNamespace("fluid.tableOfContents"); - fluid.tableOfContents.insertAnchor = function (name, element) { + fluid.tableOfContents.insertAnchor = function (name, element, anchorIdentifier) { // In order to resolve FLUID-4453, we need to make sure that the owner document is correctly // taken from the target element (the preview may be in an iframe) var anchor = $("", element.ownerDocument); anchor.prop({ + "class": anchorIdentifier, name: name, id: name }); @@ -51,14 +52,16 @@ var fluid_1_5 = fluid_1_5 || {}; fluid.tableOfContents.refreshView = function (that) { var headings = that.locate("headings"); + // remove existing toc anchors from the the DOM, before adding any new ones. + that.locate("tocAnchors").remove(); + that.anchorInfo = fluid.transform(headings, function (heading) { var info = that.headingTextToAnchorInfo(heading); - that.insertAnchor(info.id, heading); + that.insertAnchor(info.id, heading, that.options.anchorIdentifier); return info; }); var headingsModel = that.modelBuilder.assembleModel(headings, that.anchorInfo); - that.applier.requestChange("", headingsModel); that.events.onRefresh.fire(); @@ -111,8 +114,10 @@ var fluid_1_5 = fluid_1_5 || {}; }, selectors: { headings: ":header:visible:not(.flc-toc-tocContainer :header)", - tocContainer: ".flc-toc-tocContainer" + tocContainer: ".flc-toc-tocContainer", + tocAnchors: ".flc-toc-anchors" }, + anchorIdentifier: "flc-toc-anchors", events: { onRefresh: null, afterRender: null, diff --git a/src/tests/component-tests/tableOfContents/html/TableOfContents-test.html b/src/tests/component-tests/tableOfContents/html/TableOfContents-test.html index bd577bf707..31f6b5fbfb 100644 --- a/src/tests/component-tests/tableOfContents/html/TableOfContents-test.html +++ b/src/tests/component-tests/tableOfContents/html/TableOfContents-test.html @@ -3,10 +3,10 @@ Table of Contents Tests - + - + @@ -48,11 +48,11 @@

    Amphibians

    Toads

    - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec blandit rhoncus diam. Donec nunc magna, volutpat ac, fermentum in, venenatis id, nibh. Nulla vitae erat. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; In sed quam. Curabitur luctus tempor nunc. Donec mollis. Integer accumsan viverra massa. Morbi accumsan placerat tellus. Aenean ornare ultrices lorem. Nam in quam. Integer placerat commodo diam. Quisque a tellus. + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec blandit rhoncus diam. Donec nunc magna, volutpat ac, fermentum in, venenatis id, nibh. Nulla vitae erat. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; In sed quam. Curabitur luctus tempor nunc. Donec mollis. Integer accumsan viverra massa. Morbi accumsan placerat tellus. Aenean ornare ultrices lorem. Nam in quam. Integer placerat commodo diam. Quisque a tellus.

    Natterjack Toads

    - Nullam sed ligula. Curabitur ac urna sit amet nisl sagittis placerat. Proin tincidunt massa nec enim. Mauris aliquam lacus ac metus. Integer tempus molestie augue. Morbi sodales erat non nisl. Aliquam fringilla. Fusce dui odio, dapibus eu, venenatis ac, viverra vel, mauris. Vestibulum tortor lorem, ultricies vitae, adipiscing et, posuere et, orci. Pellentesque tincidunt. Vivamus venenatis mollis metus. Nunc ullamcorper, ipsum a dictum dictum, enim purus sagittis risus, ut aliquam tellus risus at mauris. + Nullam sed ligula. Curabitur ac urna sit amet nisl sagittis placerat. Proin tincidunt massa nec enim. Mauris aliquam lacus ac metus. Integer tempus molestie augue. Morbi sodales erat non nisl. Aliquam fringilla. Fusce dui odio, dapibus eu, venenatis ac, viverra vel, mauris. Vestibulum tortor lorem, ultricies vitae, adipiscing et, posuere et, orci. Pellentesque tincidunt. Vivamus venenatis mollis metus. Nunc ullamcorper, ipsum a dictum dictum, enim purus sagittis risus, ut aliquam tellus risus at mauris.

    Salamander

    @@ -85,18 +85,18 @@

    CATT

    - +
    - +

    H1

    H2

    H3

    H4

    - +

    Paragraph 1

    @@ -105,5 +105,10 @@

    H4

    Paragraph 3

    + +
    +
    +

    H2

    +
    diff --git a/src/tests/component-tests/tableOfContents/js/TableOfContentsTests.js b/src/tests/component-tests/tableOfContents/js/TableOfContentsTests.js index f867c9277a..d68479ef86 100644 --- a/src/tests/component-tests/tableOfContents/js/TableOfContentsTests.js +++ b/src/tests/component-tests/tableOfContents/js/TableOfContentsTests.js @@ -563,31 +563,39 @@ https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt jqUnit.asyncTest("Component test refreshView", function () { // craft headingInfo so renderTOCTest() can use it var testHeadingsStart = { - headingInfo : [] + headingInfo: [{ + level: "2", + text: "H2", + url: "#test" + }] }; var testHeadingRefreshed = { headingInfo: [{ - level: "1", + level: "2", + text: "H2", + url: "#test" + }, { + level: "2", text: "test", url: "#test" }] - } - renderTOCComponent("#flc-toc-noHeaders", { + }; + renderTOCComponent("#flc-toc-refreshHeadings", { listeners: { //FLUID-5112: have to use the onCreate event instead of onReady to prevent infinite recursion. "onCreate.intialState": { listener: function (levels, that) { - renderTOCTest(levels, testHeadingsStart); - renderTOCAnchorTest(); - that.events.onRefresh.addListener(function () { jqUnit.assert("The onRefresh event should have fired"); renderTOCTest(levels, testHeadingRefreshed); renderTOCAnchorTest(); + var numHeadings = testHeadingRefreshed.headingInfo.length; + + jqUnit.assertEquals("The correct number of anchors should be present", numHeadings, that.locate("tocAnchors").length); jqUnit.start(); }, "inTestCase", null, "last"); - that.container.append("

    test

    "); + that.container.append("

    test

    "); that.refreshView(); }, args: ["{that}.levels", "{that}"], From 30b974b7dcf33ebc122f5a578c2059207c00bad8 Mon Sep 17 00:00:00 2001 From: Justin Obara Date: Mon, 12 Aug 2013 09:16:00 -0400 Subject: [PATCH 6/7] FLUID-5075: wrapped code in a closure. The textfield slider code had been missing the function ($, fluid) closure. --- .../uiOptions/js/TextfieldSlider.js | 298 +++++++++--------- 1 file changed, 152 insertions(+), 146 deletions(-) diff --git a/src/components/uiOptions/js/TextfieldSlider.js b/src/components/uiOptions/js/TextfieldSlider.js index 447a6954b2..8373efee41 100644 --- a/src/components/uiOptions/js/TextfieldSlider.js +++ b/src/components/uiOptions/js/TextfieldSlider.js @@ -16,171 +16,177 @@ https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt /*jslint white: true, funcinvoke: true, undef: true, newcap: true, nomen: true, regexp: true, bitwise: true, browser: true, forin: true, maxerr: 100, indent: 4 */ -/******************** - * Textfield Slider * - ********************/ - -fluid.defaults("fluid.textfieldSlider", { - gradeNames: ["fluid.viewComponent", "autoInit"], - components: { - textfield: { - type: "fluid.textfieldSlider.textfield", - container: "{textfieldSlider}.dom.textfield", - options: { - model: "{textfieldSlider}.model", - range: "{textfieldSlider}.options.range", - applier: "{textfieldSlider}.applier" +var fluid_1_5 = fluid_1_5 || {}; + +(function ($, fluid) { + + /******************** + * Textfield Slider * + ********************/ + + fluid.defaults("fluid.textfieldSlider", { + gradeNames: ["fluid.viewComponent", "autoInit"], + components: { + textfield: { + type: "fluid.textfieldSlider.textfield", + container: "{textfieldSlider}.dom.textfield", + options: { + model: "{textfieldSlider}.model", + range: "{textfieldSlider}.options.range", + applier: "{textfieldSlider}.applier" + } + }, + slider: { + type: "fluid.textfieldSlider.slider", + container: "{textfieldSlider}.dom.slider", + options: { + model: "{textfieldSlider}.model", + range: "{textfieldSlider}.options.range", + applier: "{textfieldSlider}.applier", + sliderOptions: "{textfieldSlider}.options.sliderOptions" + } } }, - slider: { - type: "fluid.textfieldSlider.slider", - container: "{textfieldSlider}.dom.slider", - options: { - model: "{textfieldSlider}.model", - range: "{textfieldSlider}.options.range", - applier: "{textfieldSlider}.applier", - sliderOptions: "{textfieldSlider}.options.sliderOptions" + selectors: { + textfield: ".flc-textfieldSlider-field", + slider: ".flc-textfieldSlider-slider" + }, + events: { + modelChanged: null, + afterRender: null + }, + listeners: { + modelChanged: "{that}.refreshView" + }, + model: { + value: null + }, + range: { + min: 0, + max: 100 + }, + sliderOptions: { + orientation: "horizontal", + step: 1.0 + }, + invokers: { + refreshView: { + funcName: "fluid.textfieldSlider.refreshView", + args: ["{that}"] } + }, + finalInitFunction: "fluid.textfieldSlider.finalInit", + renderOnInit: true + }); + + fluid.textfieldSlider.finalInit = function (that) { + + that.applier.modelChanged.addListener("value", function (newModel) { + that.events.modelChanged.fire(newModel.value); + }); + + if (that.options.renderOnInit) { + that.refreshView(); } - }, - selectors: { - textfield: ".flc-textfieldSlider-field", - slider: ".flc-textfieldSlider-slider" - }, - events: { - modelChanged: null, - afterRender: null - }, - listeners: { - modelChanged: "{that}.refreshView" - }, - model: { - value: null - }, - range: { - min: 0, - max: 100 - }, - sliderOptions: { - orientation: "horizontal", - step: 1.0 - }, - invokers: { - refreshView: { - funcName: "fluid.textfieldSlider.refreshView", - args: ["{that}"] - } - }, - finalInitFunction: "fluid.textfieldSlider.finalInit", - renderOnInit: true -}); + }; -fluid.textfieldSlider.finalInit = function (that) { + fluid.textfieldSlider.refreshView = function (that) { + that.textfield.container.val(that.model.value); + that.events.afterRender.fire(that); + }; - that.applier.modelChanged.addListener("value", function (newModel) { - that.events.modelChanged.fire(newModel.value); + fluid.defaults("fluid.textfieldSlider.textfield", { + gradeNames: ["fluid.viewComponent", "autoInit"], + listeners: { + onCreate: { + listener: "fluid.textfieldSlider.textfield.init", + args: "{that}" + } + }, + range: {} // should be used to specify the min, max range e.g. {min: 0, max: 100} }); - if (that.options.renderOnInit) { - that.refreshView(); - } -}; - -fluid.textfieldSlider.refreshView = function (that) { - that.textfield.container.val(that.model.value); - that.events.afterRender.fire(that); -}; - -fluid.defaults("fluid.textfieldSlider.textfield", { - gradeNames: ["fluid.viewComponent", "autoInit"], - listeners: { - onCreate: { - listener: "fluid.textfieldSlider.textfield.init", - args: "{that}" + fluid.textfieldSlider.validateValue = function (model, range, changeRequest) { + var oldValue = model.value; + var newValue = changeRequest.value; + + var isValidNum = !isNaN(parseInt(newValue, 10)); + + if (isValidNum) { + if (newValue < range.min) { + newValue = range.min; + } else if (newValue > range.max) { + newValue = range.max; + } + changeRequest.value = newValue; + } else { + changeRequest.value = oldValue; } - }, - range: {} // should be used to specify the min, max range e.g. {min: 0, max: 100} -}); + }; -fluid.textfieldSlider.validateValue = function (model, range, changeRequest) { - var oldValue = model.value; - var newValue = changeRequest.value; + fluid.textfieldSlider.textfield.init = function (that) { + that.applier.guards.addListener({path: "value", transactional: true}, function (model, changeRequest) { + fluid.textfieldSlider.validateValue(model, that.options.range, changeRequest); + }); - var isValidNum = !isNaN(parseInt(newValue, 10)); + that.container.change(function (source) { + that.applier.requestChange("value", source.target.value); + }); + }; - if (isValidNum) { - if (newValue < range.min) { - newValue = range.min; - } else if (newValue > range.max) { - newValue = range.max; - } - changeRequest.value = newValue; - } else { - changeRequest.value = oldValue; - } -}; - -fluid.textfieldSlider.textfield.init = function (that) { - that.applier.guards.addListener({path: "value", transactional: true}, function (model, changeRequest) { - fluid.textfieldSlider.validateValue(model, that.options.range, changeRequest); + fluid.defaults("fluid.textfieldSlider.slider", { + gradeNames: ["fluid.viewComponent", "autoInit"], + selectors: { + thumb: ".ui-slider-handle" + }, + events: { + modelChanged: null + }, + listeners: { + onCreate: { + listener: "fluid.textfieldSlider.slider.init", + args: "{that}" + } + }, + range: {} // should be used to specify the min, max range e.g. {min: 0, max: 100} }); - that.container.change(function (source) { - that.applier.requestChange("value", source.target.value); - }); -}; - -fluid.defaults("fluid.textfieldSlider.slider", { - gradeNames: ["fluid.viewComponent", "autoInit"], - selectors: { - thumb: ".ui-slider-handle" - }, - events: { - modelChanged: null - }, - listeners: { - onCreate: { - listener: "fluid.textfieldSlider.slider.init", - args: "{that}" - } - }, - range: {} // should be used to specify the min, max range e.g. {min: 0, max: 100} -}); - -// This will be removed once the jQuery UI slider has built in ARIA -var initSliderAria = function (thumb, opts) { - var ariaDefaults = { - role: "slider", - "aria-valuenow": opts.value, - "aria-valuemin": opts.min, - "aria-valuemax": opts.max + // This will be removed once the jQuery UI slider has built in ARIA + var initSliderAria = function (thumb, opts) { + var ariaDefaults = { + role: "slider", + "aria-valuenow": opts.value, + "aria-valuemin": opts.min, + "aria-valuemax": opts.max + }; + thumb.attr(ariaDefaults); }; - thumb.attr(ariaDefaults); -}; -fluid.textfieldSlider.slider.init = function (that) { - // To support backwards compatability, the range data can still be store in the model. - var sliderOptions = $.extend(true, {}, that.options.sliderOptions, that.model, that.options.range); + fluid.textfieldSlider.slider.init = function (that) { + // To support backwards compatability, the range data can still be store in the model. + var sliderOptions = $.extend(true, {}, that.options.sliderOptions, that.model, that.options.range); - that.slider = that.container.slider(sliderOptions); - initSliderAria(that.locate("thumb"), sliderOptions); + that.slider = that.container.slider(sliderOptions); + initSliderAria(that.locate("thumb"), sliderOptions); - that.setSliderValue = function (value) { - that.slider.slider("value", value); - }; + that.setSliderValue = function (value) { + that.slider.slider("value", value); + }; - that.setSliderAria = function (value) { - that.locate("thumb").attr("aria-valuenow", value); - }; + that.setSliderAria = function (value) { + that.locate("thumb").attr("aria-valuenow", value); + }; - that.slider.bind("slide", function (e, ui) { - that.applier.requestChange("value", ui.value); - }); + that.slider.bind("slide", function (e, ui) { + that.applier.requestChange("value", ui.value); + }); - that.applier.modelChanged.addListener("value", function (newModel) { - that.setSliderValue(newModel.value); - that.setSliderAria(newModel.value); - that.events.modelChanged.fire(newModel.value); - }); + that.applier.modelChanged.addListener("value", function (newModel) { + that.setSliderValue(newModel.value); + that.setSliderAria(newModel.value); + that.events.modelChanged.fire(newModel.value); + }); + + }; -}; \ No newline at end of file +})(jQuery, fluid_1_5); From be913ee75e97e9cb4770f2c5a845649f6ec9dcd2 Mon Sep 17 00:00:00 2001 From: Justin Obara Date: Tue, 13 Aug 2013 14:56:07 -0400 Subject: [PATCH 7/7] FLUID-5110: renamed anchorIdentifier renamed anchorIdentifier to anchorClass --- src/components/tableOfContents/js/TableOfContents.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/tableOfContents/js/TableOfContents.js b/src/components/tableOfContents/js/TableOfContents.js index 13769d39fa..bbe04be7a2 100644 --- a/src/components/tableOfContents/js/TableOfContents.js +++ b/src/components/tableOfContents/js/TableOfContents.js @@ -26,12 +26,12 @@ var fluid_1_5 = fluid_1_5 || {}; fluid.registerNamespace("fluid.tableOfContents"); - fluid.tableOfContents.insertAnchor = function (name, element, anchorIdentifier) { + fluid.tableOfContents.insertAnchor = function (name, element, anchorClass) { // In order to resolve FLUID-4453, we need to make sure that the owner document is correctly // taken from the target element (the preview may be in an iframe) var anchor = $("", element.ownerDocument); anchor.prop({ - "class": anchorIdentifier, + "class": anchorClass, name: name, id: name }); @@ -57,7 +57,7 @@ var fluid_1_5 = fluid_1_5 || {}; that.anchorInfo = fluid.transform(headings, function (heading) { var info = that.headingTextToAnchorInfo(heading); - that.insertAnchor(info.id, heading, that.options.anchorIdentifier); + that.insertAnchor(info.id, heading, that.options.anchorClass); return info; }); @@ -117,7 +117,7 @@ var fluid_1_5 = fluid_1_5 || {}; tocContainer: ".flc-toc-tocContainer", tocAnchors: ".flc-toc-anchors" }, - anchorIdentifier: "flc-toc-anchors", + anchorClass: "flc-toc-anchors", events: { onRefresh: null, afterRender: null,