diff --git a/src/components/orator/js/Orator.js b/src/components/orator/js/Orator.js index b06e9e7ecc..39e0e75f3f 100644 --- a/src/components/orator/js/Orator.js +++ b/src/components/orator/js/Orator.js @@ -22,6 +22,8 @@ https://github.com/fluid-project/infusion/raw/main/Infusion-LICENSE.txt fluid.defaults("fluid.orator", { gradeNames: ["fluid.viewComponent"], selectors: { + // TODO: AMB 14/9/22 This selector appears unused. The controller instead needs a "parentContainer" literal element supplied - + // The enactor then reaches in and overrides this controller: ".flc-orator-controller", content: ".flc-orator-content" }, @@ -851,6 +853,8 @@ fluid.defaults("fluid.orator.selectionReader", { onToggleControl: null }, components: { + // This parser is only used within fluid.orator.selectionReader.parseElement by direct use of its "parse" function + // and it does not need configuration parser: { type: "fluid.textNodeParser" } diff --git a/src/framework/core/js/ResourceLoader.js b/src/framework/core/js/ResourceLoader.js index b7f8e8eb3d..e0830ae871 100644 --- a/src/framework/core/js/ResourceLoader.js +++ b/src/framework/core/js/ResourceLoader.js @@ -21,7 +21,7 @@ https://github.com/fluid-project/infusion/raw/main/Infusion-LICENSE.txt * This is similar to the algorithm specified for localised resources in Java, e.g. documented at * https://docs.oracle.com/javase/6/docs/api/java/util/ResourceBundle.html#getBundle%28java.lang.String,%20java.util.Locale,%20java.lang.ClassLoader%29 * @param {String} fileName - The base filename or URL to be exploded - * @param {String} locale - A locale name with respect to which to perform the explosion + * @param {String} [locale] - A locale name with respect to which to perform the explosion * @param {String} [defaultLocale] - An optional default locale to fall back on in the case none of the localised * variants could be located. * @return {String[]} An array of localised filenames to be fetched, in increasing order of specificity. In @@ -36,7 +36,7 @@ fluid.explodeLocalisedName = function (fileName, locale, defaultLocale) { var baseName = fileName.substring(0, lastDot); var extension = fileName.substring(lastDot); - var segs = locale.split("_"); + var segs = locale ? locale.split("_") : []; var exploded = fluid.transform(segs, function (seg, index) { var shortSegs = segs.slice(0, index + 1); @@ -127,33 +127,28 @@ fluid.fetchResources = function (resourceSpecs, callback, options) { */ fluid.fetchResources.explodeForLocales = function (resourceFetcher) { fluid.each(resourceFetcher.resourceSpecs, function (resourceSpec) { - if (!resourceSpec.launched) { - // If options.defaultLocale is set, it will replace any - // defaultLocale set on an individual resourceSpec - if (resourceFetcher.options.defaultLocale && resourceSpec.defaultLocale === undefined) { - resourceSpec.defaultLocale = resourceFetcher.options.defaultLocale; - } - if (resourceSpec.locale === undefined) { - resourceSpec.locale = resourceFetcher.options.locale || resourceSpec.defaultLocale; - } - resourceSpec.dataType = resourceSpec.dataType || resourceFetcher.options.dataType; - - resourceSpec.loader = fluid.resourceLoader.resolveResourceLoader(resourceSpec); - if (!resourceSpec.loader.loader.noPath) { - var pathKey = resourceSpec.loader.pathKey; - var path = resourceSpec[pathKey]; - var resolvedPath = resourceSpec[pathKey] = resourceFetcher.transformResourceURL(path); - if (resourceSpec.locale) { - resourceSpec.localeExploded = fluid.explodeLocalisedName(resolvedPath, resourceSpec.locale, resourceSpec.defaultLocale); - resourceSpec.localeExplodedSpecs = fluid.transform(resourceSpec.localeExploded, function (oneExploded) { - var togo = { - loader: resourceSpec.loader - }; - togo[pathKey] = oneExploded; - return togo; - }, fluid.fetchResources.prepareRequestOptions); - } - } + // If options.defaultLocale is set, it will be a default for defaultLocale set on an individual resourceSpec + resourceSpec.resolvedDefaultLocale = resourceSpec.defaultLocale || resourceFetcher.options.defaultLocale; + // A locale from the model takes priority over any locally set locale - should review this + resourceSpec.resolvedLocale = resourceFetcher.options.locale || resourceSpec.locale || resourceSpec.resolvedDefaultLocale; + + // Note: An important change from Infusion 4.3 - EVERY resourceSpec with a path now gets upgraded to localeExplodedSpecs, + // since we could never be sure that they wouldn't get localised as a result of a change to model.resourceLoader.locale + // and the "loader" gets established as part of the very first pass in resolveLoaderTask + // Fix for FLUID-6750 + if (!resourceSpec.loader.loader.noPath) { + var pathKey = resourceSpec.loader.pathKey; + var path = resourceSpec[pathKey]; + var resolvedPath = resourceSpec[pathKey] = resourceFetcher.transformResourceURL(path); + resourceSpec.localeExploded = fluid.explodeLocalisedName(resolvedPath, resourceSpec.resolvedLocale, resourceSpec.resolvedDefaultLocale); + resourceSpec.localeExplodedSpecs = fluid.transform(resourceSpec.localeExploded, function (oneExploded) { + var togo = { + loader: resourceSpec.loader, + options: resourceSpec.options + }; + togo[pathKey] = oneExploded; + return togo; + }, fluid.fetchResources.prepareRequestOptions); } }); }; @@ -413,6 +408,7 @@ fluid.initResourceFetcher = function (resourceFetcher) { */ resourceFetcher.completionPromise = fluid.promise(); fluid.fetchResources.explodeForLocales(resourceFetcher); + resourceFetcher.onInit.fire(resourceFetcher.completionPromise); }; @@ -500,6 +496,11 @@ fluid.makeResourceFetcher = function (sourceResourceSpecs, callback, options, tr that.onInit.addListener.apply(null, args); }); + fluid.each(that.resourceSpecs, function (resourceSpec) { + resourceSpec.dataType = resourceSpec.dataType || that.options.dataType; + resourceSpec.loader = fluid.resourceLoader.resolveResourceLoader(resourceSpec); + }); + fluid.initResourceFetcher(that); fluid.each(that.resourceSpecs, function (resourceSpec, key) { @@ -559,7 +560,7 @@ fluid.fetchResources.refetchOneResource = function (resourceSpec, resourceFetche fluid.fetchResources.initiateRefetch = function (resourceFetcher) { resourceFetcher.completionPromise.cancel(); delete resourceFetcher.completionPromise; - fluid.initResourceFetcher(resourceFetcher); + fluid.initResourceFetcher(resourceFetcher, true); fluid.fetchResources.fetchAll(resourceFetcher); resourceFetcher.fetchAll(); }; @@ -567,14 +568,13 @@ fluid.fetchResources.initiateRefetch = function (resourceFetcher) { /** Trigger the refetching of all resources managed by this `resourceFetcher`. By default, this will only fetch resources * which are localisable (that is, they have resource entries with a path field which can be interpolated for a locale) * @param {resourceFetcher} resourceFetcher - The fetcher for which all resources will be loaded - * @param {Boolean} [withoutLocales] - (Optional) Set to `true` if this should also fetch resources which are not localisable * @return {Promise} The `completionPromise` for the fetcher which will yield the full state of fetched `resourceSpecs` * in either success or failure */ -fluid.fetchResources.refetchAll = function (resourceFetcher, withoutLocales) { +fluid.fetchResources.refetchAll = function (resourceFetcher) { var anyLaunched = false; fluid.each(resourceFetcher.resourceSpecs, function (resourceSpec) { - if (resourceSpec.locale || withoutLocales) { + if (resourceSpec.resolvedLocale) { anyLaunched = true; fluid.fetchResources.refetchOneResource(resourceSpec, resourceFetcher, true); } @@ -650,6 +650,7 @@ fluid.registerNamespace("fluid.resourceLoader.loaders"); fluid.resourceLoader.modelUpdated = function (resourceFetcher, modelOptions, early) { resourceFetcher.options = $.extend(true, {}, resourceFetcher.options, modelOptions); if (early) { + fluid.fetchResources.explodeForLocales(resourceFetcher); resourceFetcher.optionsReady.resolve(); } else { resourceFetcher.refetchAll(); diff --git a/src/framework/core/js/TextNodeParser.js b/src/framework/core/js/TextNodeParser.js index 16d28f9213..545b3f190c 100644 --- a/src/framework/core/js/TextNodeParser.js +++ b/src/framework/core/js/TextNodeParser.js @@ -104,7 +104,7 @@ fluid.textNodeParser.getLang = function (elm) { /** * Recursively parses a DOM element and it's sub elements and fires the `onParsedTextNode` event for each text node * found. The event is fired with the text node, language and index of the text node in the list of its parent's - * child nodes.. + * child nodes. * * Note: elements that return `false` from `that.hasTextToRead` are ignored. * diff --git a/src/framework/preferences/js/LocalizationEnactor.js b/src/framework/preferences/js/LocalizationEnactor.js index c58cb06c78..279ee02aa1 100644 --- a/src/framework/preferences/js/LocalizationEnactor.js +++ b/src/framework/preferences/js/LocalizationEnactor.js @@ -37,6 +37,7 @@ fluid.defaults("fluid.prefs.enactor.localization", { // At the moment, all enactors are copied into the iframe to apply settings to the panel as well. // However, the strings for the panel will be localized through the prefsEditorLoader and do not // require the iframe URL to change. When in the panel, we do not run the urlPathLocale changes. + // TODO: This grade is undefined and this check should be removed, especially as we remove the iframe renderer inPanel: { contextValue: "{iframeRenderer}.id", // The following undefined grade is needed to prevent the `urlPath` check from supplying its diff --git a/tests/framework-tests/core/js/DataBindingTests.js b/tests/framework-tests/core/js/DataBindingTests.js index e59f34bbbe..233221bb23 100644 --- a/tests/framework-tests/core/js/DataBindingTests.js +++ b/tests/framework-tests/core/js/DataBindingTests.js @@ -903,23 +903,51 @@ fluid.tests.fluid6442.checkIt = function (component) { } }; +fluid.defaults("fluid.tests.fluid6442options", { + listeners: { + "onResourcesLoaded.checkIt": { + func: "fluid.tests.fluid6442.checkIt", + args: "{that}" + }, + "onResourceError.failTest": function (err) { + jqUnit.fail("Failure fetching resource: " + err); + } + } +}); + jqUnit.asyncTest("FLUID-6442: Modelised relocalisation should be accessible via resources", function () { jqUnit.expect(2); var component = fluid.tests.fluid6442({ - listeners: { - "onResourcesLoaded.checkIt": { - func: "fluid.tests.fluid6442.checkIt", - args: "{that}" - }, - "onResourceError.failTest": function (err) { - jqUnit.fail("Failure fetching resource: " + err); - } - } + gradeNames: "fluid.tests.fluid6442options" }); // Change the locale before the queued I/O can resolve. In addition this tests promise cancellation propagation. component.applier.change("resourceLoader.locale", "en_ZA"); }); +/** FLUID-6750: Modelised relocalisation should work on startup **/ + +fluid.defaults("fluid.tests.fluid6750", { + gradeNames: ["fluid.modelComponent", "fluid.resourceLoader"], + resources: { + messages: { + url: "../data/messages1.json", + dataType: "json" + } + }, + model: { + resourceLoader: { + locale: "en_ZA" + } + } +}); + +jqUnit.asyncTest("FLUID-6750: Modelised relocalisation should work on startup", function () { + jqUnit.expect(2); + fluid.tests.fluid6750({ + gradeNames: "fluid.tests.fluid6442options" + }); +}); + /** FLUID-6503: Two side-by-side modelised resources **/ fluid.defaults("fluid.tests.fluid6503", { diff --git a/tests/framework-tests/core/js/ResourceLoaderTests.js b/tests/framework-tests/core/js/ResourceLoaderTests.js index 681ee94dc2..7845d762e9 100644 --- a/tests/framework-tests/core/js/ResourceLoaderTests.js +++ b/tests/framework-tests/core/js/ResourceLoaderTests.js @@ -406,7 +406,7 @@ jqUnit.asyncTest("FLUID-4982: Overlapping creation of asynchronous components", jqUnit.assertEquals("First component model resolved", "second", first.model); jqUnit.assertEquals("Second component model resolved", "first", second.model); jqUnit.assertTrue("Failed component has been destroyed", fluid.isDestroyed(failed)); - jqUnit.assertEquals("Status code recoverable from failed component", 404, failed.creationPromise.value.status); + jqUnit.assertTrue("Status code recoverable from failed component", 404, failed.creationPromise.value.message.includes("/notfound")); jqUnit.assertTrue("onDestroy called for failed component", true, failed.onDestroyCalled); jqUnit.assertTrue("afterDestroy called for failed component", true, failed.onDestroyCalled); mocks.destroy(); @@ -430,7 +430,8 @@ fluid.defaults("fluid.tests.FLUID6460", { jqUnit.test("FLUID-6460: Transfer options to XHR", function () { var that = fluid.tests.FLUID6460(); - jqUnit.assertEquals("Should have transferred responseType option to XHR property", "arraybuffer", that.resourceFetcher.resourceSpecs.someResource.xhr.responseType); + var spec = that.resourceFetcher.resourceSpecs.someResource; + jqUnit.assertEquals("Should have transferred responseType option to XHR property", "arraybuffer", spec.localeExplodedSpecs[0].xhr.responseType); }); /** FLUID-4982: Partially filled out resource blocks **/