diff --git a/.travis.yml b/.travis.yml index fb5ed74f..3a5874f8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,5 +21,7 @@ addons: - google-chrome-stable sauce_connect: true script: - - wct --module-resolution=node --npm + - gulp patch-wct-browser-legacy # cp -vf test/browser.js node_modules/wct-browser-legacy/browser.js + - wct --npm --module-resolution=node --skip-plugin=sauce + - wct --npm --module-resolution=node --skip-plugin=istanbul - 'if [ "${TRAVIS_BRANCH}" != "" ]; then lcov-result-merger ''test/coverage-report*/lcov.info'' | ./node_modules/coveralls/bin/coveralls.js || echo success; fi' \ No newline at end of file diff --git a/define-element.js b/define-element.js index fbe83552..76587058 100644 --- a/define-element.js +++ b/define-element.js @@ -85,7 +85,7 @@ if (!window.Define) { } } - previous = current.previousSibling; + previous = current && current.previousSibling; while (previous && !previous.tagName) { previous = previous.previousSibling; } @@ -94,7 +94,7 @@ if (!window.Define) { } if (!previous) { // search for cousin template - if (current.parentNode.tagName === 'body'.toUpperCase()) { + if (current && current.parentNode.tagName === 'body'.toUpperCase()) { previous = current.parentNode.previousSibling; while (previous && !previous.tagName) { previous = previous.previousSibling; diff --git a/gulpfile.js b/gulpfile.js index 0ba40613..1a0ee9aa 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -791,5 +791,10 @@ gulp.task('pretest2', ['clean2'], function(cb) { cb); }); -require('web-component-tester').gulp.init(gulp, [ 'pretest2' ]); +//require('web-component-tester').gulp.init(gulp, [ 'pretest2' ]); + +gulp.task('patch-wct-browser-legacy', () => { + return gulp.src([ 'test/browser.js' ], { base: 'test' }) + .pipe(gulp.dest('node_modules/wct-browser-legacy')); +}); diff --git a/i18n-dom-bind.js b/i18n-dom-bind.js index ab41c91b..6d9b9905 100644 --- a/i18n-dom-bind.js +++ b/i18n-dom-bind.js @@ -18,7 +18,7 @@ import deepcopy from 'deepcopy/dist/deepcopy.js'; // i18n-dom-bind based on Polymer/polymer#2.0-preview src/templatizer/dom-bind.html class I18nDomBind extends Mixins.Localizable(LegacyElementMixin(HTMLElement)) { static get importMeta() { - return import.meta; + return { url: new URL(BehaviorsStore.I18nControllerBehavior.properties.startUrl.value, location.href).href }; } static get is() { return 'i18n-dom-bind'; } diff --git a/package.json b/package.json index eff02a7b..07001583 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ }, "homepage": "https://github.com/t2ym/i18n-element#readme", "name": "i18n-element", - "version": "3.0.0-pre.1-lit-html.8.4", + "version": "3.0.0-pre.4-lit-html.8.4", "main": "i18n-element.js", "directories": { "test": "test" diff --git a/test/browser.js b/test/browser.js new file mode 100644 index 00000000..150f32c9 --- /dev/null +++ b/test/browser.js @@ -0,0 +1,1897 @@ +/** + * @license + * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. + * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt + * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt + * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt + * Code distributed by Google as part of the polymer project is also + * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt + */ + +/** + * THIS FILE IS AUTOMATICALLY GENERATED! + * To make changes to browser.js, please edit the source files in the repo's `browser/` directory! + */ + +(function () { +'use strict'; + +window.__wctUseNpm = true; +/** + * @license + * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. + * This code may only be used under the BSD style license found at + * http://polymer.github.io/LICENSE.txt The complete set of authors may be found + * at http://polymer.github.io/AUTHORS.txt The complete set of contributors may + * be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by + * Google as part of the polymer project is also subject to an additional IP + * rights grant found at http://polymer.github.io/PATENTS.txt + */ +// Make sure that we use native timers, in case they're being stubbed out. +var nativeSetInterval = window.setInterval; +var nativeSetTimeout = window.setTimeout; +var nativeRequestAnimationFrame = window.requestAnimationFrame; +/** + * Runs `stepFn`, catching any error and passing it to `callback` (Node-style). + * Otherwise, calls `callback` with no arguments on success. + * + * @param {function()} callback + * @param {function()} stepFn + */ +function safeStep(callback, stepFn) { + var err; + try { + stepFn(); + } + catch (error) { + err = error; + } + callback(err); +} +/** + * Runs your test at declaration time (before Mocha has begun tests). Handy for + * when you need to test document initialization. + * + * Be aware that any errors thrown asynchronously cannot be tied to your test. + * You may want to catch them and pass them to the done event, instead. See + * `safeStep`. + * + * @param {string} name The name of the test. + * @param {function(?function())} testFn The test function. If an argument is + * accepted, the test will be treated as async, just like Mocha tests. + */ +function testImmediate(name, testFn) { + if (testFn.length > 0) { + return testImmediateAsync(name, testFn); + } + var err; + try { + testFn(); + } + catch (error) { + err = error; + } + test(name, function (done) { + done(err); + }); +} +/** + * An async-only variant of `testImmediate`. + * + * @param {string} name + * @param {function(?function())} testFn + */ +function testImmediateAsync(name, testFn) { + var testComplete = false; + var err; + test(name, function (done) { + var intervalId = nativeSetInterval(function () { + if (!testComplete) + return; + clearInterval(intervalId); + done(err); + }, 10); + }); + try { + testFn(function (error) { + if (error) + err = error; + testComplete = true; + }); + } + catch (error) { + err = error; + testComplete = true; + } +} +/** + * Triggers a flush of any pending events, observations, etc and calls you back + * after they have been processed. + * + * @param {function()} callback + */ +function flush(callback) { + // Ideally, this function would be a call to Polymer.dom.flush, but that + // doesn't support a callback yet + // (https://github.com/Polymer/polymer-dev/issues/851), + // ...and there's cross-browser flakiness to deal with. + // Make sure that we're invoking the callback with no arguments so that the + // caller can pass Mocha callbacks, etc. + var done = function done() { + callback(); + }; + // Because endOfMicrotask is flaky for IE, we perform microtask checkpoints + // ourselves (https://github.com/Polymer/polymer-dev/issues/114): + var isIE = navigator.appName === 'Microsoft Internet Explorer'; + if (isIE && window.Platform && window.Platform.performMicrotaskCheckpoint) { + var reallyDone_1 = done; + done = function doneIE() { + Platform.performMicrotaskCheckpoint(); + nativeSetTimeout(reallyDone_1, 0); + }; + } + // Everyone else gets a regular flush. + var scope; + if (window.Polymer && window.Polymer.dom && window.Polymer.dom.flush) { + scope = window.Polymer.dom; + } + else if (window.Polymer && window.Polymer.flush) { + scope = window.Polymer; + } + else if (window.WebComponents && window.WebComponents.flush) { + scope = window.WebComponents; + } + if (scope) { + scope.flush(); + } + // Ensure that we are creating a new _task_ to allow all active microtasks to + // finish (the code you're testing may be using endOfMicrotask, too). + nativeSetTimeout(done, 0); +} +/** + * Advances a single animation frame. + * + * Calls `flush`, `requestAnimationFrame`, `flush`, and `callback` sequentially + * @param {function()} callback + */ +function animationFrameFlush(callback) { + flush(function () { + nativeRequestAnimationFrame(function () { + flush(callback); + }); + }); +} +/** + * DEPRECATED: Use `flush`. + * @param {function} callback + */ +function asyncPlatformFlush(callback) { + console.warn('asyncPlatformFlush is deprecated in favor of the more terse flush()'); + return window.flush(callback); +} +/** + * + */ +function waitFor(fn, next, intervalOrMutationEl, timeout, timeoutTime) { + timeoutTime = timeoutTime || Date.now() + (timeout || 1000); + intervalOrMutationEl = intervalOrMutationEl || 32; + try { + fn(); + } + catch (e) { + if (Date.now() > timeoutTime) { + throw e; + } + else { + if (typeof intervalOrMutationEl !== 'number') { + intervalOrMutationEl.onMutation(intervalOrMutationEl, function () { + waitFor(fn, next, intervalOrMutationEl, timeout, timeoutTime); + }); + } + else { + nativeSetTimeout(function () { + waitFor(fn, next, intervalOrMutationEl, timeout, timeoutTime); + }, intervalOrMutationEl); + } + return; + } + } + next(); +} +window.safeStep = safeStep; +window.testImmediate = testImmediate; +window.testImmediateAsync = testImmediateAsync; +window.flush = flush; +window.animationFrameFlush = animationFrameFlush; +window.asyncPlatformFlush = asyncPlatformFlush; +window.waitFor = waitFor; + +/** + * The global configuration state for WCT's browser client. + */ +var _config = { + environmentScripts: !!window.__wctUseNpm ? + [ + 'stacky/browser.js', + 'async/lib/async.js', + 'wct-browser-legacy/node_modules/lodash/index.js', + 'mocha/mocha.js', + 'chai/chai.js', + '@polymer/sinonjs/sinon.js', + 'sinon-chai/lib/sinon-chai.js', + 'accessibility-developer-tools/dist/js/axs_testing.js', + '@polymer/test-fixture/test-fixture.js' + ] : + [ + 'stacky/browser.js', + 'async/lib/async.js', + 'lodash/lodash.js', + 'mocha/mocha.js', + 'chai/chai.js', + 'sinonjs/sinon.js', + 'sinon-chai/lib/sinon-chai.js', + 'accessibility-developer-tools/dist/js/axs_testing.js' + ], + environmentImports: !!window.__wctUseNpm ? [] : + ['test-fixture/test-fixture.html'], + root: null, + waitForFrameworks: true, + waitFor: null, + numConcurrentSuites: 1, + trackConsoleError: true, + mochaOptions: { timeout: 10 * 1000 }, + verbose: false, +}; +/** + * Merges initial `options` into WCT's global configuration. + * + * @param {Object} options The options to merge. See `browser/config.ts` for a + * reference. + */ +function setup(options) { + var childRunner = ChildRunner.current(); + if (childRunner) { + _deepMerge(_config, childRunner.parentScope.WCT._config); + // But do not force the mocha UI + delete _config.mochaOptions.ui; + } + if (options && typeof options === 'object') { + _deepMerge(_config, options); + } + if (!_config.root) { + // Sibling dependencies. + var root = scriptPrefix('browser.js'); + _config.root = basePath(root.substr(0, root.length - 1)); + if (!_config.root) { + throw new Error('Unable to detect root URL for WCT sources. Please set WCT.root before including browser.js'); + } + } +} +/** + * Retrieves a configuration value. + */ +function get(key) { + return _config[key]; +} +// Internal +function _deepMerge(target, source) { + Object.keys(source).forEach(function (key) { + if (target[key] !== null && typeof target[key] === 'object' && + !Array.isArray(target[key])) { + _deepMerge(target[key], source[key]); + } + else { + target[key] = source[key]; + } + }); +} + +/** + * @param {function()} callback A function to call when the active web component + * frameworks have loaded. + */ +function whenFrameworksReady(callback) { + debug('whenFrameworksReady'); + var done = function () { + debug('whenFrameworksReady done'); + callback(); + }; + // If webcomponents script is in the document, wait for WebComponentsReady. + if (window.WebComponents && !window.WebComponents.ready) { + debug('WebComponentsReady?'); + window.addEventListener('WebComponentsReady', function wcReady() { + window.removeEventListener('WebComponentsReady', wcReady); + debug('WebComponentsReady'); + done(); + }); + } + else { + done(); + } +} +/** + * @return {string} ' tests' or ' test'. + */ +function pluralizedStat(count, kind) { + if (count === 1) { + return count + ' ' + kind + ' test'; + } + else { + return count + ' ' + kind + ' tests'; + } +} +/** + * @param {string} path The URI of the script to load. + * @param {function} done + */ +function loadScript(path, done) { + var script = document.createElement('script'); + script.src = path; + if (done) { + script.onload = done.bind(null, null); + script.onerror = done.bind(null, 'Failed to load script ' + script.src); + } + document.head.appendChild(script); +} +/** + * @param {string} path The URI of the stylesheet to load. + * @param {function} done + */ +function loadStyle(path, done) { + var link = document.createElement('link'); + link.rel = 'stylesheet'; + link.href = path; + if (done) { + link.onload = done.bind(null, null); + link.onerror = done.bind(null, 'Failed to load stylesheet ' + link.href); + } + document.head.appendChild(link); +} +/** + * @param {...*} var_args Logs values to the console when the `debug` + * configuration option is true. + */ +function debug() { + var var_args = []; + for (var _i = 0; _i < arguments.length; _i++) { + var_args[_i] = arguments[_i]; + } + if (!get('verbose')) { + return; + } + var args = [window.location.pathname].concat(var_args); + (console.debug || console.log).apply(console, args); +} +// URL Processing +/** + * @param {string} url + * @return {{base: string, params: string}} + */ +function parseUrl(url) { + var parts = url.match(/^(.*?)(?:\?(.*))?$/); + return { + base: parts[1], + params: getParams(parts[2] || ''), + }; +} +/** + * Expands a URL that may or may not be relative to `base`. + * + * @param {string} url + * @param {string} base + * @return {string} + */ +function expandUrl(url, base) { + if (!base) + return url; + if (url.match(/^(\/|https?:\/\/)/)) + return url; + if (base.substr(base.length - 1) !== '/') { + base = base + '/'; + } + return base + url; +} +/** + * @param {string=} opt_query A query string to parse. + * @return {!Object>} All params on the URL's query. + */ +function getParams(query) { + query = typeof query === 'string' ? query : window.location.search; + if (query.substring(0, 1) === '?') { + query = query.substring(1); + } + // python's SimpleHTTPServer tacks a `/` on the end of query strings :( + if (query.slice(-1) === '/') { + query = query.substring(0, query.length - 1); + } + if (query === '') + return {}; + var result = {}; + query.split('&').forEach(function (part) { + var pair = part.split('='); + if (pair.length !== 2) { + console.warn('Invalid URL query part:', part); + return; + } + var key = decodeURIComponent(pair[0]); + var value = decodeURIComponent(pair[1]); + if (!result[key]) { + result[key] = []; + } + result[key].push(value); + }); + return result; +} +/** + * Merges params from `source` into `target` (mutating `target`). + * + * @param {!Object>} target + * @param {!Object>} source + */ +function mergeParams(target, source) { + Object.keys(source).forEach(function (key) { + if (!(key in target)) { + target[key] = []; + } + target[key] = target[key].concat(source[key]); + }); +} +/** + * @param {string} param The param to return a value for. + * @return {?string} The first value for `param`, if found. + */ +function getParam(param) { + var params = getParams(); + return params[param] ? params[param][0] : null; +} +/** + * @param {!Object>} params + * @return {string} `params` encoded as a URI query. + */ +function paramsToQuery(params) { + var pairs = []; + Object.keys(params).forEach(function (key) { + params[key].forEach(function (value) { + pairs.push(encodeURIComponent(key) + '=' + encodeURIComponent(value)); + }); + }); + return (pairs.length > 0) ? ('?' + pairs.join('&')) : ''; +} +function getPathName(location) { + return typeof location === 'string' ? location : location.pathname; +} +function basePath(location) { + return getPathName(location).match(/^.*\//)[0]; +} +function relativeLocation(location, basePath) { + var path = getPathName(location); + if (path.indexOf(basePath) === 0) { + path = path.substring(basePath.length); + } + return path; +} +function cleanLocation(location) { + var path = getPathName(location); + if (path.slice(-11) === '/index.html') { + path = path.slice(0, path.length - 10); + } + return path; +} +function parallel(runners, maybeLimit, done) { + var limit; + if (typeof maybeLimit !== 'number') { + done = maybeLimit; + limit = 0; + } + else { + limit = maybeLimit; + } + if (!runners.length) { + return done(); + } + var called = false; + var total = runners.length; + var numActive = 0; + var numDone = 0; + function runnerDone(error) { + if (called) { + return; + } + numDone = numDone + 1; + numActive = numActive - 1; + if (error || numDone >= total) { + called = true; + done(error); + } + else { + runOne(); + } + } + function runOne() { + if (limit && numActive >= limit) { + return; + } + if (!runners.length) { + return; + } + numActive = numActive + 1; + runners.shift()(runnerDone); + } + runners.forEach(runOne); +} +/** + * Finds the directory that a loaded script is hosted on. + * + * @param {string} filename + * @return {string?} + */ +function scriptPrefix(filename) { + var scripts = document.querySelectorAll('script[src*="' + filename + '"]'); + if (scripts.length !== 1) { + return null; + } + var script = scripts[0].src; + return script.substring(0, script.indexOf(filename)); +} + + +var util = Object.freeze({ + whenFrameworksReady: whenFrameworksReady, + pluralizedStat: pluralizedStat, + loadScript: loadScript, + loadStyle: loadStyle, + debug: debug, + parseUrl: parseUrl, + expandUrl: expandUrl, + getParams: getParams, + mergeParams: mergeParams, + getParam: getParam, + paramsToQuery: paramsToQuery, + basePath: basePath, + relativeLocation: relativeLocation, + cleanLocation: cleanLocation, + parallel: parallel, + scriptPrefix: scriptPrefix +}); + +/** + * A Mocha suite (or suites) run within a child iframe, but reported as if they + * are part of the current context. + */ +var ChildRunner = /** @class */ (function () { + function ChildRunner(url, parentScope) { + this.eventListenersToRemoveOnClean = []; + this.parentScope = parentScope; + var urlBits = parseUrl(url); + mergeParams(urlBits.params, getParams(parentScope.location.search)); + delete urlBits.params.cli_browser_id; + this.url = "" + urlBits.base + paramsToQuery(urlBits.params); + this.state = 'initializing'; + } + /** + * Listeners added using this method will be removed on done() + * + * @param type event type + * @param listener object which receives a notification + * @param target event target + */ + ChildRunner.prototype.addEventListener = function (type, listener, target) { + target.addEventListener(type, listener); + var descriptor = { target: target, type: type, listener: listener }; + this.eventListenersToRemoveOnClean.push(descriptor); + }; + /** + * Removes all event listeners added by a method addEventListener defined + * on an instance of ChildRunner. + */ + ChildRunner.prototype.removeAllEventListeners = function () { + this.eventListenersToRemoveOnClean.forEach(function (_a) { + var target = _a.target, type = _a.type, listener = _a.listener; + return target.removeEventListener(type, listener); + }); + }; + /** + * @return {ChildRunner} The `ChildRunner` that was registered for this + * window. + */ + ChildRunner.current = function () { + return ChildRunner.get(window); + }; + /** + * @param {!Window} target A window to find the ChildRunner of. + * @param {boolean} traversal Whether this is a traversal from a child window. + * @return {ChildRunner} The `ChildRunner` that was registered for `target`. + */ + ChildRunner.get = function (target, traversal) { + var childRunner = ChildRunner.byUrl[target.location.href]; + if (childRunner) { + return childRunner; + } + if (window.parent === window) { + // Top window. + if (traversal) { + console.warn('Subsuite loaded but was never registered. This most likely is due to wonky history behavior. Reloading...'); + window.location.reload(); + } + return null; + } + // Otherwise, traverse. + return window.parent.WCT._ChildRunner.get(target, true); + }; + /** + * Loads and runs the subsuite. + * + * @param {function} done Node-style callback. + */ + ChildRunner.prototype.run = function (done) { + var _this = this; + debug('ChildRunner#run', this.url); + this.state = 'loading'; + this.onRunComplete = done; + this.container = document.getElementById('subsuites'); + if (!this.container) { + var container_1 = (this.container = document.createElement('div')); + container_1.id = 'subsuites'; + document.body.appendChild(container_1); + } + var container = this.container; + var iframe = (this.iframe = document.createElement('iframe')); + iframe.classList.add('subsuite'); + iframe.src = this.url; + // Let the iframe expand the URL for us. + var url = (this.url = iframe.src); + container.appendChild(iframe); + ChildRunner.byUrl[url] = this; + this.timeoutId = setTimeout(function () { return _this.loaded(new Error('Timed out loading ' + url)); }, ChildRunner.loadTimeout); + this.addEventListener('error', function () { return _this.loaded(new Error('Failed to load document ' + _this.url)); }, iframe); + this.addEventListener('DOMContentLoaded', function () { return _this.loaded(); }, iframe.contentWindow); + }; + /** + * Called when the sub suite's iframe has loaded (or errored during load). + * + * @param {*} error The error that occured, if any. + */ + ChildRunner.prototype.loaded = function (error) { + debug('ChildRunner#loaded', this.url, error); + if (this.iframe.contentWindow == null && error) { + this.signalRunComplete(error); + this.done(); + return; + } + // Not all targets have WCT loaded (compatiblity mode) + if (this.iframe.contentWindow.WCT) { + this.share = this.iframe.contentWindow.WCT.share; + } + if (error) { + this.signalRunComplete(error); + this.done(); + } + }; + /** + * Called in mocha/run.js when all dependencies have loaded, and the child is + * ready to start running tests + * + * @param {*} error The error that occured, if any. + */ + ChildRunner.prototype.ready = function (error) { + debug('ChildRunner#ready', this.url, error); + if (this.timeoutId) { + clearTimeout(this.timeoutId); + } + if (error) { + this.signalRunComplete(error); + this.done(); + } + }; + /** + * Called when the sub suite's tests are complete, so that it can clean up. + */ + ChildRunner.prototype.done = function () { + var _this = this; + debug('ChildRunner#done', this.url, arguments); + // Make sure to clear that timeout. + this.ready(); + this.signalRunComplete(); + if (this.iframe) { + // Be safe and avoid potential browser crashes when logic attempts to + // interact with the removed iframe. + setTimeout(function () { + _this.removeAllEventListeners(); + _this.container.removeChild(_this.iframe); + _this.iframe = undefined; + _this.share = null; + }, 0); + } + }; + ChildRunner.prototype.signalRunComplete = function (error) { + if (this.onRunComplete) { + this.state = 'complete'; + this.onRunComplete(error); + this.onRunComplete = null; + } + }; + // ChildRunners get a pretty generous load timeout by default. + ChildRunner.loadTimeout = 60000; + // We can't maintain properties on iframe elements in Firefox/Safari/???, so + // we track childRunners by URL. + ChildRunner.byUrl = {}; + return ChildRunner; +}()); + +var SOCKETIO_ENDPOINT = window.location.protocol + '//' + window.location.host; +var SOCKETIO_LIBRARY = SOCKETIO_ENDPOINT + '/socket.io/socket.io.js'; +/** + * A socket for communication between the CLI and browser runners. + * + * @param {string} browserId An ID generated by the CLI runner. + * @param {!io.Socket} socket The socket.io `Socket` to communicate over. + */ +var CLISocket = /** @class */ (function () { + function CLISocket(browserId, socket) { + this.browserId = browserId; + this.socket = socket; + } + /** + * @param {!Mocha.Runner} runner The Mocha `Runner` to observe, reporting + * interesting events back to the CLI runner. + */ + CLISocket.prototype.observe = function (runner) { + var _this = this; + this.emitEvent('browser-start', { + url: window.location.toString(), + }); + // We only emit a subset of events that we care about, and follow a more + // general event format that is hopefully applicable to test runners beyond + // mocha. + // + // For all possible mocha events, see: + // https://github.com/visionmedia/mocha/blob/master/lib/runner.js#L36 + runner.on('test', function (test) { + _this.emitEvent('test-start', { test: getTitles(test) }); + }); + runner.on('test end', function (test) { + _this.emitEvent('test-end', { + state: getState(test), + test: getTitles(test), + duration: test.duration, + error: test.err, + }); + }); + runner.on('fail', function (test, err) { + // fail the test run if we catch errors outside of a test function + if (test.type !== 'test') { + _this.emitEvent('browser-fail', 'Error thrown outside of test function: ' + err.stack); + } + }); + runner.on('childRunner start', function (childRunner) { + _this.emitEvent('sub-suite-start', childRunner.share); + }); + runner.on('childRunner end', function (childRunner) { + _this.emitEvent('sub-suite-end', childRunner.share); + }); + runner.on('end', function () { + _this.emitEvent('browser-end'); + }); + }; + /** + * @param {string} event The name of the event to fire. + * @param {*} data Additional data to pass with the event. + */ + CLISocket.prototype.emitEvent = function (event, data) { + this.socket.emit('client-event', { + browserId: this.browserId, + event: event, + data: data, + }); + }; + /** + * Builds a `CLISocket` if we are within a CLI-run environment; short-circuits + * otherwise. + * + * @param {function(*, CLISocket)} done Node-style callback. + */ + CLISocket.init = function (done) { + var browserId = getParam('cli_browser_id'); + if (!browserId) + return done(); + // Only fire up the socket for root runners. + if (ChildRunner.current()) + return done(); + loadScript(SOCKETIO_LIBRARY, function (error) { + if (error) + return done(error); + var socket = io(SOCKETIO_ENDPOINT); + socket.on('error', function (error) { + socket.off(); + done(error); + }); + socket.on('connect', function () { + socket.off(); + done(null, new CLISocket(browserId, socket)); + }); + }); + }; + return CLISocket; +}()); +// Misc Utility +/** + * @param {!Mocha.Runnable} runnable The test or suite to extract titles from. + * @return {!Array.} The titles of the runnable and its parents. + */ +function getTitles(runnable) { + var titles = []; + while (runnable && !runnable.root && runnable.title) { + titles.unshift(runnable.title); + runnable = runnable.parent; + } + return titles; +} +/** + * @param {!Mocha.Runnable} runnable + * @return {string} + */ +function getState(runnable) { + if (runnable.state === 'passed') { + return 'passing'; + } + else if (runnable.state === 'failed') { + return 'failing'; + } + else if (runnable.pending) { + return 'pending'; + } + else { + return 'unknown'; + } +} + +// We capture console events when running tests; so make sure we have a +// reference to the original one. +var console$1 = window.console; +var FONT = ';font: normal 13px "Roboto", "Helvetica Neue", "Helvetica", sans-serif;'; +var STYLES = { + plain: FONT, + suite: 'color: #5c6bc0' + FONT, + test: FONT, + passing: 'color: #259b24' + FONT, + pending: 'color: #e65100' + FONT, + failing: 'color: #c41411' + FONT, + stack: 'color: #c41411', + results: FONT + 'font-size: 16px', +}; +// I don't think we can feature detect this one... +var userAgent = navigator.userAgent.toLowerCase(); +var CAN_STYLE_LOG = userAgent.match('firefox') || userAgent.match('webkit'); +var CAN_STYLE_GROUP = userAgent.match('webkit'); +// Track the indent for faked `console.group` +var logIndent = ''; +function log(text, style) { + text = text.split('\n') + .map(function (l) { + return logIndent + l; + }) + .join('\n'); + if (CAN_STYLE_LOG) { + console$1.log('%c' + text, STYLES[style] || STYLES.plain); + } + else { + console$1.log(text); + } +} +function logGroup(text, style) { + if (CAN_STYLE_GROUP) { + console$1.group('%c' + text, STYLES[style] || STYLES.plain); + } + else if (console$1.group) { + console$1.group(text); + } + else { + logIndent = logIndent + ' '; + log(text, style); + } +} +function logGroupEnd() { + if (console$1.groupEnd) { + console$1.groupEnd(); + } + else { + logIndent = logIndent.substr(0, logIndent.length - 2); + } +} +function logException(error) { + log(error.stack || error.message || (error + ''), 'stack'); +} +/** + * A Mocha reporter that logs results out to the web `console`. + */ +var Console = /** @class */ (function () { + /** + * @param runner The runner that is being reported on. + */ + function Console(runner) { + Mocha.reporters.Base.call(this, runner); + runner.on('suite', function (suite) { + if (suite.root) { + return; + } + logGroup(suite.title, 'suite'); + }.bind(this)); + runner.on('suite end', function (suite) { + if (suite.root) { + return; + } + logGroupEnd(); + }.bind(this)); + runner.on('test', function (test) { + logGroup(test.title, 'test'); + }.bind(this)); + runner.on('pending', function (test) { + logGroup(test.title, 'pending'); + }.bind(this)); + runner.on('fail', function (_test, error) { + logException(error); + }.bind(this)); + runner.on('test end', function (_test) { + logGroupEnd(); + }.bind(this)); + runner.on('end', this.logSummary.bind(this)); + } + /** Prints out a final summary of test results. */ + Console.prototype.logSummary = function () { + logGroup('Test Results', 'results'); + if (this.stats.failures > 0) { + log(pluralizedStat(this.stats.failures, 'failing'), 'failing'); + } + if (this.stats.pending > 0) { + log(pluralizedStat(this.stats.pending, 'pending'), 'pending'); + } + log(pluralizedStat(this.stats.passes, 'passing')); + if (!this.stats.failures) { + log('test suite passed', 'passing'); + } + log('Evaluated ' + this.stats.tests + ' tests in ' + + this.stats.duration + 'ms.'); + logGroupEnd(); + }; + return Console; +}()); + +/** + * @license + * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. + * This code may only be used under the BSD style license found at + * http://polymer.github.io/LICENSE.txt The complete set of authors may be found + * at http://polymer.github.io/AUTHORS.txt The complete set of contributors may + * be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by + * Google as part of the polymer project is also subject to an additional IP + * rights grant found at http://polymer.github.io/PATENTS.txt + */ +/** + * WCT-specific behavior on top of Mocha's default HTML reporter. + * + * @param {!Mocha.Runner} runner The runner that is being reported on. + */ +function HTML(runner) { + var output = document.createElement('div'); + output.id = 'mocha'; + document.body.appendChild(output); + runner.on('suite', function (_test) { + this.total = runner.total; + }.bind(this)); + Mocha.reporters.HTML.call(this, runner); +} +// Woo! What a hack. This just saves us from adding a bunch of complexity around +// style loading. +var style = document.createElement('style'); +style.textContent = "\n html, body {\n position: relative;\n height: 100%;\n width: 100%;\n min-width: 900px;\n }\n #mocha, #subsuites {\n height: 100%;\n position: absolute;\n top: 0;\n }\n #mocha {\n box-sizing: border-box;\n margin: 0 !important;\n padding: 60px 20px;\n right: 0;\n left: 500px;\n }\n #subsuites {\n -ms-flex-direction: column;\n -webkit-flex-direction: column;\n display: -ms-flexbox;\n display: -webkit-flex;\n display: flex;\n flex-direction: column;\n left: 0;\n width: 500px;\n }\n #subsuites .subsuite {\n border: 0;\n width: 100%;\n height: 100%;\n }\n #mocha .test.pass .duration {\n color: #555 !important;\n }\n"; +document.head.appendChild(style); + +var STACKY_CONFIG = { + indent: ' ', + locationStrip: [ + /^https?:\/\/[^\/]+/, + /\?.*$/, + ], + filter: function (line) { + return !!line.location.match(/\/web-component-tester\/[^\/]+(\?.*)?$/); + }, +}; +// https://github.com/visionmedia/mocha/blob/master/lib/runner.js#L36-46 +var MOCHA_EVENTS = [ + 'start', + 'end', + 'suite', + 'suite end', + 'test', + 'test end', + 'hook', + 'hook end', + 'pass', + 'fail', + 'pending', + 'childRunner end' +]; +// Until a suite has loaded, we assume this many tests in it. +var ESTIMATED_TESTS_PER_SUITE = 3; +/** + * A Mocha-like reporter that combines the output of multiple Mocha suites. + */ +var MultiReporter = /** @class */ (function () { + /** + * @param numSuites The number of suites that will be run, in order to + * estimate the total number of tests that will be performed. + * @param reporters The set of reporters that + * should receive the unified event stream. + * @param parent The parent reporter, if present. + */ + function MultiReporter(numSuites, reporters, parent) { + var _this = this; + this.reporters = reporters.map(function (reporter) { + return new reporter(_this); + }); + this.parent = parent; + this.basePath = parent && parent.basePath || basePath(window.location); + this.total = numSuites * ESTIMATED_TESTS_PER_SUITE; + // Mocha reporters assume a stream of events, so we have to be careful to + // only report on one runner at a time... + this.currentRunner = null; + // ...while we buffer events for any other active runners. + this.pendingEvents = []; + this.emit('start'); + } + /** + * @param location The location this reporter represents. + * @return A reporter-like "class" for each child suite + * that should be passed to `mocha.run`. + */ + MultiReporter.prototype.childReporter = function (location) { + var _a; + var name = this.suiteTitle(location); + // The reporter is used as a constructor, so we can't depend on `this` being + // properly bound. + var self = this; + return _a = /** @class */ (function () { + function ChildReporter(runner) { + runner.name = window.name; + self.bindChildRunner(runner); + } + return ChildReporter; + }()), + _a.title = window.name, + _a; + }; + /** Must be called once all runners have finished. */ + MultiReporter.prototype.done = function () { + this.complete = true; + this.flushPendingEvents(); + this.emit('end'); + }; + /** + * Emit a top level test that is not part of any suite managed by this + * reporter. + * + * Helpful for reporting on global errors, loading issues, etc. + * + * @param title The title of the test. + * @param error An error associated with this test. If falsy, test is + * considered to be passing. + * @param suiteTitle Title for the suite that's wrapping the test. + * @param estimated If this test was included in the original + * estimate of `numSuites`. + */ + MultiReporter.prototype.emitOutOfBandTest = function (title, error, suiteTitle, estimated) { + debug('MultiReporter#emitOutOfBandTest(', arguments, ')'); + var root = new Mocha.Suite(suiteTitle || ''); + var test = new Mocha.Test(title, function () { }); + test.parent = root; + test.state = error ? 'failed' : 'passed'; + test.err = error; + if (!estimated) { + this.total = this.total + ESTIMATED_TESTS_PER_SUITE; + } + var runner = { total: 1 }; + this.proxyEvent('start', runner); + this.proxyEvent('suite', runner, root); + this.proxyEvent('test', runner, test); + if (error) { + this.proxyEvent('fail', runner, test, error); + } + else { + this.proxyEvent('pass', runner, test); + } + this.proxyEvent('test end', runner, test); + this.proxyEvent('suite end', runner, root); + this.proxyEvent('end', runner); + }; + /** + * @param {!Location|string} location + * @return {string} + */ + MultiReporter.prototype.suiteTitle = function (location) { + var path = relativeLocation(location, this.basePath); + path = cleanLocation(path); + return path; + }; + // Internal Interface + /** @param {!Mocha.runners.Base} runner The runner to listen to events for. */ + MultiReporter.prototype.bindChildRunner = function (runner) { + var _this = this; + MOCHA_EVENTS.forEach(function (eventName) { + runner.on(eventName, _this.proxyEvent.bind(_this, eventName, runner)); + }); + }; + /** + * Evaluates an event fired by `runner`, proxying it forward or buffering it. + * + * @param {string} eventName + * @param {!Mocha.runners.Base} runner The runner that emitted this event. + * @param {...*} var_args Any additional data passed as part of the event. + */ + MultiReporter.prototype.proxyEvent = function (eventName, runner) { + var _args = []; + for (var _i = 2; _i < arguments.length; _i++) { + _args[_i - 2] = arguments[_i]; + } + var extraArgs = Array.prototype.slice.call(arguments, 2); + if (this.complete) { + console.warn('out of order Mocha event for ' + runner.name + ':', eventName, extraArgs); + return; + } + if (this.currentRunner && runner !== this.currentRunner) { + this.pendingEvents.push(Array.prototype.slice.call(arguments)); + return; + } + debug('MultiReporter#proxyEvent(', arguments, ')'); + // This appears to be a Mocha bug: Tests failed by passing an error to their + // done function don't set `err` properly. + // + // TODO(nevir): Track down. + if (eventName === 'fail' && !extraArgs[0].err) { + extraArgs[0].err = extraArgs[1]; + } + if (eventName === 'start') { + this.onRunnerStart(runner); + } + else if (eventName === 'end') { + this.onRunnerEnd(runner); + } + else { + this.cleanEvent(eventName, runner, extraArgs); + this.emit.apply(this, [eventName].concat(extraArgs)); + } + }; + /** + * Cleans or modifies an event if needed. + * + * @param eventName + * @param runner The runner that emitted this event. + * @param extraArgs + */ + MultiReporter.prototype.cleanEvent = function (eventName, _runner, extraArgs) { + // Suite hierarchy + if (extraArgs[0]) { + extraArgs[0] = this.showRootSuite(extraArgs[0]); + } + // Normalize errors + if (eventName === 'fail') { + extraArgs[1] = Stacky.normalize(extraArgs[1], STACKY_CONFIG); + } + if (extraArgs[0] && extraArgs[0].err) { + extraArgs[0].err = Stacky.normalize(extraArgs[0].err, STACKY_CONFIG); + } + }; + /** + * We like to show the root suite's title, which requires a little bit of + * trickery in the suite hierarchy. + * + * @param {!Mocha.Runnable} node + */ + MultiReporter.prototype.showRootSuite = function (node) { + var leaf = node = Object.create(node); + while (node && node.parent) { + var wrappedParent = Object.create(node.parent); + node.parent = wrappedParent; + node = wrappedParent; + } + node.root = false; + return leaf; + }; + /** @param {!Mocha.runners.Base} runner */ + MultiReporter.prototype.onRunnerStart = function (runner) { + debug('MultiReporter#onRunnerStart:', runner.name); + this.total = this.total - ESTIMATED_TESTS_PER_SUITE + runner.total; + this.currentRunner = runner; + }; + /** @param {!Mocha.runners.Base} runner */ + MultiReporter.prototype.onRunnerEnd = function (runner) { + debug('MultiReporter#onRunnerEnd:', runner.name); + this.currentRunner = null; + this.flushPendingEvents(); + }; + /** + * Flushes any buffered events and runs them through `proxyEvent`. This will + * loop until all buffered runners are complete, or we have run out of + * buffered events. + */ + MultiReporter.prototype.flushPendingEvents = function () { + var _this = this; + var events = this.pendingEvents; + this.pendingEvents = []; + events.forEach(function (eventArgs) { + _this.proxyEvent.apply(_this, eventArgs); + }); + }; + return MultiReporter; +}()); + +var ARC_OFFSET = 0; // start at the right. +var ARC_WIDTH = 6; +/** + * A Mocha reporter that updates the document's title and favicon with + * at-a-glance stats. + * + * @param {!Mocha.Runner} runner The runner that is being reported on. + */ +var Title = /** @class */ (function () { + function Title(runner) { + Mocha.reporters.Base.call(this, runner); + runner.on('test end', this.report.bind(this)); + } + /** Reports current stats via the page title and favicon. */ + Title.prototype.report = function () { + this.updateTitle(); + this.updateFavicon(); + }; + /** Updates the document title with a summary of current stats. */ + Title.prototype.updateTitle = function () { + if (this.stats.failures > 0) { + document.title = pluralizedStat(this.stats.failures, 'failing'); + } + else { + document.title = pluralizedStat(this.stats.passes, 'passing'); + } + }; + /** Updates the document's favicon w/ a summary of current stats. */ + Title.prototype.updateFavicon = function () { + var canvas = document.createElement('canvas'); + canvas.height = canvas.width = 32; + var context = canvas.getContext('2d'); + var passing = this.stats.passes; + var pending = this.stats.pending; + var failing = this.stats.failures; + var total = Math.max(this.runner.total, passing + pending + failing); + drawFaviconArc(context, total, 0, passing, '#0e9c57'); + drawFaviconArc(context, total, passing, pending, '#f3b300'); + drawFaviconArc(context, total, pending + passing, failing, '#ff5621'); + this.setFavicon(canvas.toDataURL()); + }; + /** Sets the current favicon by URL. */ + Title.prototype.setFavicon = function (url) { + var current = document.head.querySelector('link[rel="icon"]'); + if (current) { + document.head.removeChild(current); + } + var link = document.createElement('link'); + link.rel = 'icon'; + link.type = 'image/x-icon'; + link.href = url; + link.setAttribute('sizes', '32x32'); + document.head.appendChild(link); + }; + return Title; +}()); +/** + * Draws an arc for the favicon status, relative to the total number of tests. + */ +function drawFaviconArc(context, total, start, length, color) { + var arcStart = ARC_OFFSET + Math.PI * 2 * (start / total); + var arcEnd = ARC_OFFSET + Math.PI * 2 * ((start + length) / total); + context.beginPath(); + context.strokeStyle = color; + context.lineWidth = ARC_WIDTH; + context.arc(16, 16, 16 - ARC_WIDTH / 2, arcStart, arcEnd); + context.stroke(); +} + +var htmlSuites$1 = []; +var jsSuites$1 = []; +// We process grep ourselves to avoid loading suites that will be filtered. +var GREP = getParam('grep'); +// work around mocha bug (https://github.com/mochajs/mocha/issues/2070) +if (GREP) { + GREP = GREP.replace(/\\\./g, '.'); +} +/** + * Loads suites of tests, supporting both `.js` and `.html` files. + * + * @param files The files to load. + */ +function loadSuites(files) { + files.forEach(function (file) { + if (/\.js(\?.*)?$/.test(file)) { + jsSuites$1.push(file); + } + else if (/\.html(\?.*)?$/.test(file)) { + htmlSuites$1.push(file); + } + else { + throw new Error('Unknown resource type: ' + file); + } + }); +} +/** + * @return The child suites that should be loaded, ignoring + * those that would not match `GREP`. + */ +function activeChildSuites() { + var subsuites = htmlSuites$1; + if (GREP) { + var cleanSubsuites = []; + for (var i = 0, subsuite = void 0; subsuite = subsuites[i]; i++) { + if (GREP.indexOf(cleanLocation(subsuite)) !== -1) { + cleanSubsuites.push(subsuite); + } + } + subsuites = cleanSubsuites; + } + return subsuites; +} +/** + * Loads all `.js` sources requested by the current suite. + */ +function loadJsSuites(_reporter, done) { + debug('loadJsSuites', jsSuites$1); + var loaders = jsSuites$1.map(function (file) { + // We only support `.js` dependencies for now. + return loadScript.bind(util, file); + }); + parallel(loaders, done); +} +function runSuites(reporter, childSuites, done) { + debug('runSuites'); + var suiteRunners = [ + // Run the local tests (if any) first, not stopping on error; + _runMocha.bind(null, reporter), + ]; + // As well as any sub suites. Again, don't stop on error. + childSuites.forEach(function (file) { + suiteRunners.push(function (next) { + var childRunner = new ChildRunner(file, window); + reporter.emit('childRunner start', childRunner); + childRunner.run(function (error) { + reporter.emit('childRunner end', childRunner); + if (error) + reporter.emitOutOfBandTest(file, error); + next(); + }); + }); + }); + parallel(suiteRunners, get('numConcurrentSuites'), function (error) { + reporter.done(); + done(error); + }); +} +/** + * Kicks off a mocha run, waiting for frameworks to load if necessary. + * + * @param {!MultiReporter} reporter Where to send Mocha's events. + * @param {function} done A callback fired, _no error is passed_. + */ +function _runMocha(reporter, done, waited) { + if (get('waitForFrameworks') && !waited) { + var waitFor = (get('waitFor') || whenFrameworksReady).bind(window); + waitFor(_runMocha.bind(null, reporter, done, true)); + return; + } + debug('_runMocha'); + var mocha = window.mocha; + var Mocha = window.Mocha; + mocha.reporter(reporter.childReporter(window.location)); + mocha.suite.title = reporter.suiteTitle(window.location); + mocha.grep(GREP); + // We can't use `mocha.run` because it bashes over grep, invert, and friends. + // See https://github.com/visionmedia/mocha/blob/master/support/tail.js#L137 + var runner = Mocha.prototype.run.call(mocha, function (_error) { + if (document.getElementById('mocha')) { + Mocha.utils.highlightTags('code'); + } + done(); // We ignore the Mocha failure count. + }); + // Mocha's default `onerror` handling strips the stack (to support really old + // browsers). We upgrade this to get better stacks for async errors. + // + // TODO(nevir): Can we expand support to other browsers? + if (navigator.userAgent.match(/chrome/i)) { + window.onerror = null; + window.addEventListener('error', function (event) { + if (!event.error) + return; + if (event.error.ignore) + return; + if (window.uncaughtErrorFilter && window.uncaughtErrorFilter(event)) { + event.preventDefault(); + return; + } + runner.uncaught(event.error); + }); + } + else { + window.onerror = null; + window.addEventListener('error', function (event) { + if (window.uncaughtErrorFilter && window.uncaughtErrorFilter(event)) { + event.preventDefault(); + return; + } + runner.uncaught(event.error); + }); + } +} + +/** + * @param {CLISocket} socket The CLI socket, if present. + * @param {MultiReporter} parent The parent reporter, if present. + * @return {!Array. 0 || jsSuites$1.length > 0) { + reporters.push(HTML); + } + return reporters; +} +/** + * Yeah, hideous, but this allows us to be loaded before Mocha, which is handy. + */ +function injectMocha(Mocha) { + _injectPrototype(Console, Mocha.reporters.Base.prototype); + _injectPrototype(HTML, Mocha.reporters.HTML.prototype); + // Mocha doesn't expose its `EventEmitter` shim directly, so: + _injectPrototype(MultiReporter, Object.getPrototypeOf(Mocha.Runner.prototype)); +} +function _injectPrototype(klass, prototype) { + var newPrototype = Object.create(prototype); + // Only support + Object.keys(klass.prototype).forEach(function (key) { + newPrototype[key] = klass.prototype[key]; + }); + klass.prototype = newPrototype; +} + +/** + * Loads all environment scripts ...synchronously ...after us. + */ +function loadSync() { + debug('Loading environment scripts:'); + var a11ySuiteScriptPath = 'web-component-tester/data/a11ySuite.js'; + var scripts = get('environmentScripts'); + var a11ySuiteWillBeLoaded = window.__generatedByWct || scripts.indexOf(a11ySuiteScriptPath) > -1; + // We can't inject a11ySuite when running the npm version because it is a + // module-based script that needs `'); // jshint ignore:line + }); + debug('Environment scripts loaded'); + var imports = get('environmentImports'); + imports.forEach(function (path) { + var url = expandUrl(path, get('root')); + debug('Loading environment import:', url); + // Synchronous load. + document.write(''); // jshint ignore:line + }); + debug('Environment imports loaded'); +} +/** + * We have some hard dependencies on things that should be loaded via + * `environmentScripts`, so we assert that they're present here; and do any + * post-facto setup. + */ +function ensureDependenciesPresent() { + _ensureMocha(); + _checkChai(); +} +function _ensureMocha() { + var Mocha = window.Mocha; + if (!Mocha) { + throw new Error('WCT requires Mocha. Please ensure that it is present in WCT.environmentScripts, or that you load it before loading web-component-tester/browser.js'); + } + injectMocha(Mocha); + // Magic loading of mocha's stylesheet + var mochaPrefix = scriptPrefix('mocha.js'); + // only load mocha stylesheet for the test runner output + // Not the end of the world, if it doesn't load. + if (mochaPrefix && window.top === window.self) { + loadStyle(mochaPrefix + 'mocha.css'); + } +} +function _checkChai() { + if (!window.chai) { + debug('Chai not present; not registering shorthands'); + return; + } + window.assert = window.chai.assert; + window.expect = window.chai.expect; +} + +// We may encounter errors during initialization (for example, syntax errors in +// a test file). Hang onto those (and more) until we are ready to report them. +var globalErrors = []; +/** + * Hook the environment to pick up on global errors. + */ +function listenForErrors() { + window.addEventListener('error', function (event) { + globalErrors.push(event.error); + }); + // Also, we treat `console.error` as a test failure. Unless you prefer not. + var origConsole = console; + var origError = console.error; + console.error = function wctShimmedError() { + origError.apply(origConsole, arguments); + if (get('trackConsoleError')) { + throw 'console.error: ' + Array.prototype.join.call(arguments, ' '); + } + }; +} + +var interfaceExtensions = []; +/** + * Registers an extension that extends the global `Mocha` implementation + * with new helper methods. These helper methods will be added to the `window` + * when tests run for both BDD and TDD interfaces. + */ +function extendInterfaces(helperName, helperFactory) { + interfaceExtensions.push(function () { + var Mocha = window.Mocha; + // For all Mocha interfaces (probably just TDD and BDD): + Object.keys(Mocha.interfaces) + .forEach(function (interfaceName) { + // This is the original callback that defines the interface (TDD or + // BDD): + var originalInterface = Mocha.interfaces[interfaceName]; + // This is the name of the "teardown" or "afterEach" property for the + // current interface: + var teardownProperty = interfaceName === 'tdd' ? 'teardown' : 'afterEach'; + // The original callback is monkey patched with a new one that appends + // to the global context however we want it to: + Mocha.interfaces[interfaceName] = function (suite) { + // Call back to the original callback so that we get the base + // interface: + originalInterface.apply(this, arguments); + // Register a listener so that we can further extend the base + // interface: + suite.on('pre-require', function (context, _file, _mocha) { + // Capture a bound reference to the teardown function as a + // convenience: + var teardown = context[teardownProperty].bind(context); + // Add our new helper to the testing context. The helper is + // generated by a factory method that receives the context, + // the teardown function and the interface name and returns + // the new method to be added to that context: + context[helperName] = + helperFactory(context, teardown, interfaceName); + }); + }; + }); + }); +} +/** + * Applies any registered interface extensions. The extensions will be applied + * as many times as this function is called, so don't call it more than once. + */ +function applyExtensions() { + interfaceExtensions.forEach(function (applyExtension) { + applyExtension(); + }); +} + +extendInterfaces('fixture', function (context, teardown) { + // Return context.fixture if it is already a thing, for backwards + // compatibility with `test-fixture-mocha.js`: + return context.fixture || function fixture(fixtureId, model) { + // Automatically register a teardown callback that will restore the + // test-fixture: + teardown(function () { + document.getElementById(fixtureId).restore(); + }); + // Find the test-fixture with the provided ID and create it, returning + // the results: + return document.getElementById(fixtureId).create(model); + }; +}); + +/** + * stub + * + * The stub addon allows the tester to partially replace the implementation of + * an element with some custom implementation. Usage example: + * + * beforeEach(function() { + * stub('x-foo', { + * attached: function() { + * // Custom implementation of the `attached` method of element `x-foo`.. + * }, + * otherMethod: function() { + * // More custom implementation.. + * }, + * getterSetterProperty: { + * get: function() { + * // Custom getter implementation.. + * }, + * set: function() { + * // Custom setter implementation.. + * } + * }, + * // etc.. + * }); + * }); + */ +extendInterfaces('stub', function (_context, teardown) { + return function stub(tagName, implementation) { + // Find the prototype of the element being stubbed: + var proto = document.createElement(tagName).constructor.prototype; + // For all keys in the implementation to stub with.. + var stubs = Object.keys(implementation).map(function (key) { + // Stub the method on the element prototype with Sinon: + return sinon.stub(proto, key, implementation[key]); + }); + // After all tests.. + teardown(function () { + stubs.forEach(function (stub) { + stub.restore(); + }); + }); + }; +}); + +// replacement map stores what should be +var replacements = {}; +var replaceTeardownAttached = false; +/** + * replace + * + * The replace addon allows the tester to replace all usages of one element with + * another element within all Polymer elements created within the time span of + * the test. Usage example: + * + * beforeEach(function() { + * replace('x-foo').with('x-fake-foo'); + * }); + * + * All annotations and attributes will be set on the placement element the way + * they were set for the original element. + */ +extendInterfaces('replace', function (_context, teardown) { + return function replace(oldTagName) { + return { + with: function (tagName) { + // Standardizes our replacements map + oldTagName = oldTagName.toLowerCase(); + tagName = tagName.toLowerCase(); + replacements[oldTagName] = tagName; + // If the function is already a stub, restore it to original + if (document.importNode.isSinonProxy) { + return; + } + if (window.Polymer && !window.Polymer.Element) { + window.Polymer.Element = function () { }; + window.Polymer.Element.prototype._stampTemplate = function () { }; + } + // Keep a reference to the original `document.importNode` + // implementation for later: + var originalImportNode = document.importNode; + // Use Sinon to stub `document.ImportNode`: + sinon.stub(document, 'importNode', function (origContent, deep) { + var templateClone = document.createElement('template'); + var content = templateClone.content; + var inertDoc = content.ownerDocument; + // imports node from inertDoc which holds inert nodes. + templateClone.content.appendChild(inertDoc.importNode(origContent, true)); + // optional arguments are not optional on IE. + var nodeIterator = document.createNodeIterator(content, NodeFilter.SHOW_ELEMENT, null, true); + var node; + // Traverses the tree. A recently-replaced node will be put next, + // so if a node is replaced, it will be checked if it needs to be + // replaced again. + while (node = nodeIterator.nextNode()) { + if (!node.tagName) { + continue; + } + var currentTagName = node.tagName.toLowerCase(); + if (replacements.hasOwnProperty(currentTagName)) { + currentTagName = replacements[currentTagName]; + // find the final tag name. + while (replacements[currentTagName]) { + currentTagName = replacements[currentTagName]; + } + // Create a replacement: + var replacement = document.createElement(currentTagName); + // For all attributes in the original node.. + for (var index = 0; index < node.attributes.length; ++index) { + // Set that attribute on the replacement: + replacement.setAttribute(node.attributes[index].name, node.attributes[index].value); + } + // Replace the original node with the replacement node: + node.parentNode.replaceChild(replacement, node); + } + } + return originalImportNode.call(this, content, deep); + }); + if (!replaceTeardownAttached) { + // After each test... + teardown(function () { + replaceTeardownAttached = true; + // Restore the stubbed version of `document.importNode`: + var documentImportNode = document.importNode; + if (documentImportNode.isSinonProxy) { + documentImportNode.restore(); + } + // Empty the replacement map + replacements = {}; + }); + } + } + }; + }; +}); + +// Mocha global helpers, broken out by testing method. +// +// Keys are the method for a particular interface; values are their analog in +// the opposite interface. +var MOCHA_EXPORTS = { + // https://github.com/visionmedia/mocha/blob/master/lib/interfaces/tdd.js + tdd: { + 'setup': '"before"', + 'teardown': '"after"', + 'suiteSetup': '"beforeEach"', + 'suiteTeardown': '"afterEach"', + 'suite': '"describe" or "context"', + 'test': '"it" or "specify"', + }, + // https://github.com/visionmedia/mocha/blob/master/lib/interfaces/bdd.js + bdd: { + 'before': '"setup"', + 'after': '"teardown"', + 'beforeEach': '"suiteSetup"', + 'afterEach': '"suiteTeardown"', + 'describe': '"suite"', + 'context': '"suite"', + 'xdescribe': '"suite.skip"', + 'xcontext': '"suite.skip"', + 'it': '"test"', + 'xit': '"test.skip"', + 'specify': '"test"', + 'xspecify': '"test.skip"', + }, +}; +/** + * Exposes all Mocha methods up front, configuring and running mocha + * automatically when you call them. + * + * The assumption is that it is a one-off (sub-)suite of tests being run. + */ +function stubInterfaces() { + var keys = Object.keys(MOCHA_EXPORTS); + keys.forEach(function (ui) { + Object.keys(MOCHA_EXPORTS[ui]).forEach(function (key) { + window[key] = function wrappedMochaFunction() { + _setupMocha(ui, key, MOCHA_EXPORTS[ui][key]); + if (!window[key] || window[key] === wrappedMochaFunction) { + throw new Error('Expected mocha.setup to define ' + key); + } + window[key].apply(window, arguments); + }; + }); + }); +} +// Whether we've called `mocha.setup` +var _mochaIsSetup = false; +/** + * @param {string} ui Sets up mocha to run `ui`-style tests. + * @param {string} key The method called that triggered this. + * @param {string} alternate The matching method in the opposite interface. + */ +function _setupMocha(ui, key, alternate) { + var mochaOptions = get('mochaOptions'); + if (mochaOptions.ui && mochaOptions.ui !== ui) { + var message = 'Mixing ' + mochaOptions.ui + ' and ' + ui + + ' Mocha styles is not supported. ' + + 'You called "' + key + '". Did you mean ' + alternate + '?'; + throw new Error(message); + } + if (_mochaIsSetup) { + return; + } + applyExtensions(); + mochaOptions.ui = ui; + mocha.setup(mochaOptions); // Note that the reporter is configured in run.js. +} + +// You can configure WCT before it has loaded by assigning your custom +// configuration to the global `WCT`. +setup(window.WCT); +// Maybe some day we'll expose WCT as a module to whatever module registry you +// are using (aka the UMD approach), or as an es6 module. +var WCT = window.WCT = { + // A generic place to hang data about the current suite. This object is + // reported + // back via the `sub-suite-start` and `sub-suite-end` events. + share: {}, + // Until then, we get to rely on it to expose parent runners to their + // children. + _ChildRunner: ChildRunner, + _reporter: undefined, + _config: _config, + // Public API + /** + * Loads suites of tests, supporting both `.js` and `.html` files. + * + * @param {!Array.} files The files to load. + */ + loadSuites: loadSuites, +}; +// Load Process +listenForErrors(); +stubInterfaces(); +loadSync(); +// Give any scripts on the page a chance to declare tests and muck with things. +document.addEventListener('DOMContentLoaded', function () { + debug('DOMContentLoaded'); + ensureDependenciesPresent(); + // We need the socket built prior to building its reporter. + CLISocket.init(function (error, socket) { + if (error) + throw error; + // Are we a child of another run? + var current = ChildRunner.current(); + var parent = current && current.parentScope.WCT._reporter; + debug('parentReporter:', parent); + var childSuites = activeChildSuites(); + var reportersToUse = determineReporters(socket, parent); + // +1 for any local tests. + var reporter = new MultiReporter(childSuites.length + 1, reportersToUse, parent); + WCT._reporter = reporter; // For environment/compatibility.js + // We need the reporter so that we can report errors during load. + loadJsSuites(reporter, function (error) { + // Let our parent know that we're about to start the tests. + if (current) + current.ready(error); + if (error) + throw error; + // Emit any errors we've encountered up til now + globalErrors.forEach(function onError(error) { + reporter.emitOutOfBandTest('Test Suite Initialization', error); + }); + runSuites(reporter, childSuites, function (error) { + // Make sure to let our parent know that we're done. + if (current) + current.done(); + if (error) + throw error; + }); + }); + }); +}); + +}()); +//# sourceMappingURL=browser.js.map \ No newline at end of file diff --git a/test/bundled.sh b/test/bundled.sh new file mode 100755 index 00000000..9e5b4c05 --- /dev/null +++ b/test/bundled.sh @@ -0,0 +1,14 @@ +#!/bin/sh + +rm -rf bundled +cp -rvf preprocess3 bundled + +for i in basic edge-case multiple-case template-default-lang preference no-persist; do { + echo Building bundled $i; + sed -e "s/test-name/${i}/g" base-polymer.json >polymer.json + polymer build --root=.. + cp -vf build/esm-bundled/bundled/${i}-test-imports.js bundled/${i}-test-imports.js +} done + +rm -vf polymer.json +rm -rvf build diff --git a/test/gulpfile.js b/test/gulpfile.js new file mode 100644 index 00000000..46c7ebf5 --- /dev/null +++ b/test/gulpfile.js @@ -0,0 +1,401 @@ +'use strict'; + +const gulp = require('gulp'); +const gulpif = require('gulp-if'); +const babel = require('gulp-babel'); +const crisper = require('gulp-crisper'); +const sourcemaps = require('gulp-sourcemaps'); +const vulcanize = require('gulp-vulcanize'); +const debug = require('gulp-debug'); +const replace = require('gulp-replace'); +const uglify = require('gulp-uglify'); +const runSequence = require('run-sequence'); +const del = require('del'); +const gutil = require('gulp-util'); +const gulpignore = require('gulp-ignore'); +const gulpmatch = require('gulp-match'); +const sort = require('gulp-sort'); +const grepContents = require('gulp-grep-contents'); +const size = require('gulp-size'); +const merge = require('gulp-merge'); +const through = require('through2'); +const path = require('path'); +const stripBom = require('strip-bom'); +const JSONstringify = require('json-stringify-safe'); +const i18nPreprocess = require('gulp-i18n-preprocess'); +const i18nLeverage = require('gulp-i18n-leverage'); +const XliffConv = require('xliff-conv'); +const i18nAddLocales = require('gulp-i18n-add-locales'); + +//const logging = require('plylog'); +const mergeStream = require('merge-stream'); + +// Global object to store localizable attributes repository +var attributesRepository = {}; + +// Bundles object +var prevBundles = {}; +var bundles = {}; + +// Preprocessed templates +var preprocessedTemplates = {}; + +var title = 'I18N transform'; +var srcDir = 'src3'; +var tmpDir = 'tmp'; +var destDir = 'preprocess3'; + +var xliffOptions = {}; + +gulp.task('clean', function() { + return del([ + tmpDir, + destDir + ], { force: true }); +}); + +var indexHTML = gulpif([ '**/index.html' ], gulp.dest(destDir)); + +function btoa(str) { + let buf = Buffer.from(str); + return buf.toString('base64'); +} + +function atob(base64) { + let buf = Buffer.from(base64, 'base64'); + return buf.toString(); +} + +var unmodulize = gulpif(['**/*.js'], through.obj(function (file, enc, callback) { + let htmlTemplate = ``; + let code = stripBom(String(file.contents)); + let template = code.match(/html`([^`]*)`/); + let innerHTML = code.match(/[.]innerHTML = `([^`]*)`/); + let name = file.path.split('/').pop().replace(/[.]js$/,''); + let original = ''; + if (template || innerHTML) { + let html = htmlTemplate; + if (template) { + original = btoa(template[1]); + if (atob(original) !== template[1]) { + console.error('atob(btoa(template[1])) !== template[1]'); + } + html = html.replace('', + `\n`); + } + else { + html = html.replace('', ''); + } + if (innerHTML) { + /* + original = btoa(innerHTML[1]); + if (atob(original) !== innerHTML[1]) { + console.error('atob(btoa(innerHTML[1])) !== innerHTML[1]'); + } + */ + html = html.replace('', innerHTML[1].replace(/\\[$]/g, '$')); + } + else { + html = html.replace('', ''); + } + //console.log('original', original); + html = html.replace('', `${original}`); + let htmlFile = new gutil.File({ + cwd: file.cwd, + base: file.base, + path: file.path.substring(0, file.path.length - 3) + '.html', + contents: new Buffer(html) + }); + console.log('unmodulize: htmlFile.path = ', htmlFile.path, ' name = ', name /*, 'html = ', html */); + this.push(htmlFile); + } + callback(null, file); +})); + +var barrier = function (title) { + var files = []; + return through.obj(function (file, enc, callback) { + files.push(file); + callback(); + }, function (callback) { + files.forEach(function (file) { + this.push(file); + }, this) + callback(); + console.log(`barrier ======== ${title} ========`); + }); +}; + +// Scan HTMLs and construct localizable attributes repository +var scan = gulpif('*.html', i18nPreprocess({ + constructAttributesRepository: true, // construct attributes repository + attributesRepository: attributesRepository, // output object + srcPath: srcDir, // path to source root + targetVersion: 2, // target Polymer version + attributesRepositoryPath: + path.join(tmpDir, 'i18n-attr-repo.html'), // path to i18n-attr-repo.html + dropHtml: false // do not drop HTMLs +})); + +var basenameSort = sort({ + comparator: function(file1, file2) { + var base1 = path.basename(file1.path).replace(/^bundle[.]/, ' bundle.'); + var base2 = path.basename(file2.path).replace(/^bundle[.]/, ' bundle.'); + return base1.localeCompare(base2); + } +}); + +var dropDefaultJSON = gulpignore([ '**/*.json', '!**/locales/*.json' ]); + +/* TODO: Feedback preprocessed HTMLs into templates in JavaScript components */ +var preprocess = gulpif('**/*.html', i18nPreprocess({ + replacingText: true, // replace UI texts with {{annotations}} + jsonSpace: 2, // JSON format with 2 spaces + srcPath: srcDir, // path to source root + targetVersion: 2, // target Polymer version + force: true, + attributesRepository: attributesRepository // input attributes repository +})); + +var tmpHTML = gulpif([ '**/*.html' ], gulp.dest(tmpDir)); + +var dropDummyHTML = gulpif('**/*.html', through.obj(function (file, enc, callback) { + let temporaryHTML = ''; + let code = stripBom(String(file.contents)); + if (code.indexOf(temporaryHTML) >= 0 || file.path.match(/\/index[.]html$/)) { + let match1 = code.match(/(.*)<[/]encoded-original>/); + let match2 = code.match(/ + + + + diff --git a/test/src3/edge-case/advanced-binding-element.js b/test/src3/edge-case/advanced-binding-element.js new file mode 100644 index 00000000..319643d5 --- /dev/null +++ b/test/src3/edge-case/advanced-binding-element.js @@ -0,0 +1,457 @@ +/** +@license https://github.com/t2ym/i18n-behavior/blob/master/LICENSE.md +Copyright (c) 2016, Tetsuya Mori . All rights reserved. +*/ +import 'i18n-behavior/i18n-behavior.js'; + +import '@polymer/iron-input/iron-input.js'; +import { html } from '@polymer/polymer/lib/utils/html-tag.js'; +import { Polymer } from '@polymer/polymer/lib/legacy/polymer-fn.js'; +import { LegacyElementMixin } from '@polymer/polymer/lib/legacy/legacy-element-mixin.js'; +import { dom } from '@polymer/polymer/lib/legacy/polymer.dom.js'; +import deepcopy from 'deepcopy/dist/deepcopy.js'; +const $_documentContainer = document.createElement('template'); + +$_documentContainer.innerHTML = ``; + +document.head.appendChild($_documentContainer.content); +switch (syntax) { +default: +case 'mixin': + { + class AdvancedBindingElement extends Mixins.Localizable(LegacyElementMixin(HTMLElement)) { + static get importMeta() { + return import.meta; + } + + static get template() { + return html` + {{tr(status,text.statusMessages)}} + + {{or(value,text.defaultValue)}} + + + {{tr(status,text.statusMessageFormats)}} + {{parameter}} + string parameter + + + + + {{tr('key',text.nodefault)}} + {{text.defaultValue}} {{text.defaultValue}} + + +`; + } + + static get is() { return 'advanced-binding-element' } + static get properties () { + return { + status: { + type: String, + value: 'ok' + }, + value: { + type: String + }, + parameter: { + type: String + } + } + } + + ready() { + this.addEventListener('lang-updated', this._langUpdated); + this.addEventListener('rendered', this._rendered); + super.ready(); + } + + connectedCallback() { + //console.log('advanced-binding-element: connected'); + super.connectedCallback(); + } + + disconnectedCallback() { + super.disconnectedCallback(); + //console.log('advanced-binding-element: disconnected'); + } + + _langUpdated(e) { + console.log('lang-updated', e.composedPath()[0], e.target, e.detail, 'lang = ' + this.lang, 'effectiveLang = ' + this.effectiveLang); + if (e.composedPath()[0] === this /*&& + this.effectiveLang === this.lang*/) { + this.model = deepcopy(this.text.model); + this._checkLang(); + } + } + + _rendered(e) { + console.log('rendered', e.composedPath()[0], e.target, e.detail, 'lang = ' + this.lang, 'effectiveLang = ' + this.effectiveLang, 'e.target.lang = ' + e.target.lang); + this._checkLang(); + } + + _checkLang() { + var i18nFormats = this.root.querySelectorAll('i18n-format'); + var allLangUpdated = (this.lang === this.effectiveLang); + Array.prototype.forEach.call(i18nFormats, function (el) { + if (el.lang !== this.lang) { + allLangUpdated = false; + } + else { + el.render(); + } + }.bind(this)); + if (allLangUpdated) { + console.log(this.is + ' local-dom-ready'); + this.fire('local-dom-ready'); + } + } + } + customElements.define(AdvancedBindingElement.is, AdvancedBindingElement); + } + break; +case 'base-element': + { + class AdvancedBindingElement extends BaseElements.I18nElement { + static get importMeta() { + return import.meta; + } + + static get template() { + return html` + {{tr(status,text.statusMessages)}} + + {{or(value,text.defaultValue)}} + + + {{tr(status,text.statusMessageFormats)}} + {{parameter}} + string parameter + + + + + {{tr('key',text.nodefault)}} + {{text.defaultValue}} {{text.defaultValue}} + + +`; + } + + static get is() { return 'advanced-binding-element' } + static get properties () { + return { + status: { + type: String, + value: 'ok' + }, + value: { + type: String + }, + parameter: { + type: String + } + } + } + + ready() { + this.addEventListener('lang-updated', this._langUpdated); + this.addEventListener('rendered', this._rendered); + super.ready(); + } + + connectedCallback() { + //console.log('advanced-binding-element: connected'); + super.connectedCallback(); + } + + disconnectedCallback() { + super.disconnectedCallback(); + //console.log('advanced-binding-element: disconnected'); + } + + _langUpdated(e) { + console.log('lang-updated', e.composedPath()[0], e.target, e.detail, 'lang = ' + this.lang, 'effectiveLang = ' + this.effectiveLang); + if (e.composedPath()[0] === this /*&& + this.effectiveLang === this.lang*/) { + this.model = deepcopy(this.text.model); + this._checkLang(); + } + } + + _rendered(e) { + console.log('rendered', e.composedPath()[0], e.target, e.detail, 'lang = ' + this.lang, 'effectiveLang = ' + this.effectiveLang, 'e.target.lang = ' + e.target.lang); + this._checkLang(); + } + + _checkLang() { + var i18nFormats = this.root.querySelectorAll('i18n-format'); + var allLangUpdated = (this.lang === this.effectiveLang); + Array.prototype.forEach.call(i18nFormats, function (el) { + if (el.lang !== this.lang) { + allLangUpdated = false; + } + else { + el.render(); + } + }.bind(this)); + if (allLangUpdated) { + this.fire('local-dom-ready'); + } + } + } + customElements.define(AdvancedBindingElement.is, AdvancedBindingElement); + } + break; +case 'thin': + { + Define = class AdvancedBindingElement extends BaseElements.I18nElement { + + static get importMeta() { + return import.meta; + } + + static get properties () { + return { + status: { + type: String, + value: 'ok' + }, + value: { + type: String + }, + parameter: { + type: String + } + } + } + + ready() { + this.addEventListener('lang-updated', this._langUpdated); + this.addEventListener('rendered', this._rendered); + super.ready(); + } + + connectedCallback() { + //console.log('advanced-binding-element: connected'); + super.connectedCallback(); + } + + disconnectedCallback() { + super.disconnectedCallback(); + //console.log('advanced-binding-element: disconnected'); + } + + _langUpdated(e) { + console.log('lang-updated', e.composedPath()[0], e.target, e.detail, 'lang = ' + this.lang, 'effectiveLang = ' + this.effectiveLang); + if (e.composedPath()[0] === this /*&& + this.effectiveLang === this.lang*/) { + this.model = deepcopy(this.text.model); + this._checkLang(); + } + } + + _rendered(e) { + console.log('rendered', e.composedPath()[0], e.target, e.detail, 'lang = ' + this.lang, 'effectiveLang = ' + this.effectiveLang, 'e.target.lang = ' + e.target.lang); + this._checkLang(); + } + + _checkLang() { + var i18nFormats = this.root.querySelectorAll('i18n-format'); + var allLangUpdated = (this.lang === this.effectiveLang); + Array.prototype.forEach.call(i18nFormats, function (el) { + if (el.lang !== this.lang) { + allLangUpdated = false; + } + else { + el.render(); + } + }.bind(this)); + if (allLangUpdated) { + this.fire('local-dom-ready'); + } + } + } + } + break; +case 'legacy': + { + Polymer({ + importMeta: import.meta, + + _template: html` + {{tr(status,text.statusMessages)}} + + {{or(value,text.defaultValue)}} + + + {{tr(status,text.statusMessageFormats)}} + {{parameter}} + string parameter + + + + + {{tr('key',text.nodefault)}} + {{text.defaultValue}} {{text.defaultValue}} + + +`, + + is: 'advanced-binding-element', + + behaviors: [ + BehaviorsStore.I18nBehavior + ], + + properties: { + status: { + type: String, + value: 'ok' + }, + value: { + type: String + }, + parameter: { + type: String + } + }, + + observers: [ + ], + + listeners: { + 'lang-updated': '_langUpdated', + 'rendered': '_rendered' + }, + + ready: function () { + //this.observeHtmlLang = false; + }, + + attached: function () { + //console.log('advanced-binding-element: attached'); + }, + + detached: function () { + //console.log('advanced-binding-element: detached'); + }, + + _langUpdated: function (e) { + console.log('lang-updated', e.composedPath()[0], e.target, e.detail, 'lang = ' + this.lang, 'effectiveLang = ' + this.effectiveLang); + if (dom(e).rootTarget === this) { + this.model = deepcopy(this.text.model); + this._checkLang(); + } + }, + + _rendered: function (e) { + console.log('rendered', e.composedPath()[0], e.target, e.detail, 'lang = ' + this.lang, 'effectiveLang = ' + this.effectiveLang, 'e.target.lang = ' + e.target.lang); + this._checkLang(); + }, + + _checkLang: function () { + var i18nFormats = this.root.querySelectorAll('i18n-format'); + var allLangUpdated = (this.lang === this.effectiveLang); + Array.prototype.forEach.call(i18nFormats, function (el) { + if (el.lang !== this.lang) { + allLangUpdated = false; + } + else { + el.render(); + } + }.bind(this)); + if (allLangUpdated) { + this.fire('local-dom-ready'); + } + } + }); + } + break; +} diff --git a/test/src3/edge-case/advanced-binding-element.json b/test/src3/edge-case/advanced-binding-element.json new file mode 100755 index 00000000..d9510d4c --- /dev/null +++ b/test/src3/edge-case/advanced-binding-element.json @@ -0,0 +1,36 @@ +{ + "meta": {}, + "model": { + "aria-attributes": { + "title": "tooltip text", + "aria-label": "aria label text", + "aria-valuetext": "aria value text" + } + }, + "annotated-format": [ + "{{tr(status,text.statusMessageFormats)}}", + "{{parameter}}", + "string parameter" + ], + "span_5": [ + "{1} {2}", + "{{text.defaultValue}}", + "{{text.defaultValue}}" + ], + "statusMessages": { + "ok": "healthy status", + "busy": "busy status", + "error": "error status", + "default": "unknown status" + }, + "defaultValue": "default value", + "statusMessageFormats": { + "ok": "healthy status", + "busy": "busy status with {2}", + "error": "error status with {1} and {2}", + "default": "unknown status" + }, + "nodefault": { + "ok": "ok status" + } +} \ No newline at end of file diff --git a/test/src3/edge-case/complex-compound-binding-element.js b/test/src3/edge-case/complex-compound-binding-element.js new file mode 100644 index 00000000..9b78fafe --- /dev/null +++ b/test/src3/edge-case/complex-compound-binding-element.js @@ -0,0 +1,465 @@ +/** +@license https://github.com/t2ym/i18n-behavior/blob/master/LICENSE.md +Copyright (c) 2016, Tetsuya Mori . All rights reserved. +*/ +import 'i18n-behavior/i18n-behavior.js'; + +import { html } from '@polymer/polymer/lib/utils/html-tag.js'; +import { Polymer } from '@polymer/polymer/lib/legacy/polymer-fn.js'; +import { LegacyElementMixin } from '@polymer/polymer/lib/legacy/legacy-element-mixin.js'; +const $_documentContainer = document.createElement('template'); + +$_documentContainer.innerHTML = ``; + +document.head.appendChild($_documentContainer.content); +switch (syntax) { +default: +case 'mixin': + { + class ComplexCompoundBindingElement extends Mixins.Localizable(LegacyElementMixin(HTMLElement)) { + static get importMeta() { + return import.meta; + } + + static get template() { + return html` +
updated: {{text.updated}}, by: + + xxx + + abc + + hello +
+
updated: {{text.updated}}, by: + + xxx + + abc + + hello + + + +
+
updated: {{text.updated}}, by: + + xxx + + abc + + hello +
+
updated: {{text.updated}}, by: + + xxx + + abc + + hello +
+

A paragraph with + + is converted to + <i18n-format>. +

+

A paragraph with deep + + is not converted to + <i18n-format>. + + + +

+ +`; + } + + static get is() { return 'complex-compound-binding-element' } + + ready() { + this.addEventListener('lang-updated', this._langUpdated); + super.ready(); + } + + _langUpdated(e) { + console.log('complex-compound-binding-element: lang-updated lang = ' + this.lang + ' effectiveLang = ' + this.effectiveLang); + window.setTimeout(function () { + Array.prototype.forEach.call(this.root.querySelectorAll('i18n-format'), function (node) { + if (!node.elements) { + console.log('elements are missing', node); + node.ready(); + } + node.render(); + }); + console.log(this.is + ' local-dom-ready '); + this.fire('local-dom-ready'); + }.bind(this), 1); + } + } + customElements.define(ComplexCompoundBindingElement.is, ComplexCompoundBindingElement); + } + break; +case 'base-element': + { + class ComplexCompoundBindingElement extends BaseElements.I18nElement { + static get importMeta() { + return import.meta; + } + + static get template() { + return html` +
updated: {{text.updated}}, by: + + xxx + + abc + + hello +
+
updated: {{text.updated}}, by: + + xxx + + abc + + hello + + + +
+
updated: {{text.updated}}, by: + + xxx + + abc + + hello +
+
updated: {{text.updated}}, by: + + xxx + + abc + + hello +
+

A paragraph with + + is converted to + <i18n-format>. +

+

A paragraph with deep + + is not converted to + <i18n-format>. + + + +

+ +`; + } + + static get is() { return 'complex-compound-binding-element' } + + ready() { + this.addEventListener('lang-updated', this._langUpdated); + super.ready(); + } + + _langUpdated(e) { + console.log('complex-compound-binding-element: lang-updated lang = ' + this.lang + ' effectiveLang = ' + this.effectiveLang); + window.setTimeout(function () { + Array.prototype.forEach.call(this.root.querySelectorAll('i18n-format'), function (node) { + if (!node.elements) { + console.log('elements are missing', node); + node.ready(); + } + node.render(); + }); + console.log(this.is + ' local-dom-ready '); + this.fire('local-dom-ready'); + }.bind(this), 1); + } + } + customElements.define(ComplexCompoundBindingElement.is, ComplexCompoundBindingElement); + } + break; +case 'thin': + { + Define = class ComplexCompoundBindingElement extends BaseElements.I18nElement { + + static get importMeta() { + return import.meta; + } + + ready() { + this.addEventListener('lang-updated', this._langUpdated); + super.ready(); + } + + _langUpdated(e) { + console.log('complex-compound-binding-element: lang-updated lang = ' + this.lang + ' effectiveLang = ' + this.effectiveLang); + window.setTimeout(function () { + Array.prototype.forEach.call(this.root.querySelectorAll('i18n-format'), function (node) { + if (!node.elements) { + console.log('elements are missing', node); + node.ready(); + } + node.render(); + }); + console.log(this.is + ' local-dom-ready '); + this.fire('local-dom-ready'); + }.bind(this), 1); + } + }; + } + break; +case 'legacy': + { + Polymer({ + importMeta: import.meta, + + _template: html` +
updated: {{text.updated}}, by: + + xxx + + abc + + hello +
+
updated: {{text.updated}}, by: + + xxx + + abc + + hello + + + +
+
updated: {{text.updated}}, by: + + xxx + + abc + + hello +
+
updated: {{text.updated}}, by: + + xxx + + abc + + hello +
+

A paragraph with + + is converted to + <i18n-format>. +

+

A paragraph with deep + + is not converted to + <i18n-format>. + + + +

+ +`, + + is: 'complex-compound-binding-element', + + behaviors: [ + BehaviorsStore.I18nBehavior + ], + + listeners: { + 'lang-updated': '_langUpdated' + }, + + _langUpdated: function (e) { + console.log('complex-compound-binding-element: lang-updated lang = ' + this.lang + ' effectiveLang = ' + this.effectiveLang); + window.setTimeout(function () { + Array.prototype.forEach.call(this.root.querySelectorAll('i18n-format'), function (node) { + if (!node.elements) { + console.log('elements are missing', node); + node.ready(); + } + node.render(); + }); + console.log(this.is + ' local-dom-ready '); + this.fire('local-dom-ready'); + }.bind(this), 1); + } + }); + } + break; +} diff --git a/test/src3/edge-case/complex-compound-binding-element.json b/test/src3/edge-case/complex-compound-binding-element.json new file mode 100755 index 00000000..7951181c --- /dev/null +++ b/test/src3/edge-case/complex-compound-binding-element.json @@ -0,0 +1,69 @@ +{ + "meta": {}, + "model": {}, + "item-update2:text": [ + "updated: {1}, by: ", + "{{text.updated}}" + ], + "item-update2:text_2": " xxx ", + "item-update2:dom-if_3:template:span:b": "IF CONTENT", + "item-update2:b_4": "abc", + "item-update2:dom-if_5:template:text": "IF CONTENT 2", + "item-update2:text_6": " hello ", + "item-update:text": [ + "updated: {1}, by: ", + "{{text.updated}}" + ], + "item-update:text_2": " xxx ", + "item-update:dom-if_3:template:b": "IF CONTENT", + "item-update:b_4": "abc", + "item-update:dom-if_5:template:text": "IF CONTENT 2", + "item-update:text_6": " hello ", + "item-update3:text": [ + "updated: {1}, by: ", + "{{text.updated}}" + ], + "item-update3:text_2": " xxx ", + "item-update3:dom-if_3:template:b": "IF", + "item-update3:dom-if_3:template:b_1": "CONTENT", + "item-update3:b_4": "abc", + "item-update3:dom-if_5:template:text": "IF CONTENT 2", + "item-update3:text_6": " hello ", + "item-update4:text": [ + "updated: {1}, by: ", + "{{text.updated}}" + ], + "item-update4:dom-repeat_1:template:text": [ + " {1} = {2} ", + "{{item.name}}", + "{{text.updated}}" + ], + "item-update4:text_2": " xxx ", + "item-update4:dom-if_3:template:b": "IF CONTENT", + "item-update4:b_4": "abc", + "item-update4:dom-if_5:template:text": "IF CONTENT 2", + "item-update4:text_6": " hello ", + "paragraph:text": "A paragraph with ", + "paragraph:text_2": " is converted to ", + "paragraph:code_3": "", + "paragraph:text_4": ". ", + "paragraph2:text": "A paragraph with deep ", + "paragraph2:text_2": " is ", + "paragraph2:b_3": "not", + "paragraph2:text_4": " converted to ", + "paragraph2:code_5": "", + "paragraph2:text_6": ". ", + "authors": [ + { + "name": "Joe" + }, + { + "name": "Alice" + } + ], + "updated": "Jan 1st, 2016", + "parameters": [ + "parameter 1", + "parameter 2" + ] +} \ No newline at end of file diff --git a/test/src3/edge-case/empty-element.js b/test/src3/edge-case/empty-element.js new file mode 100644 index 00000000..8fb50c8a --- /dev/null +++ b/test/src3/edge-case/empty-element.js @@ -0,0 +1,82 @@ +/** +@license https://github.com/t2ym/i18n-behavior/blob/master/LICENSE.md +Copyright (c) 2016, Tetsuya Mori . All rights reserved. +*/ +import 'i18n-behavior/i18n-behavior.js'; + +import { html } from '@polymer/polymer/lib/utils/html-tag.js'; +import { Polymer } from '@polymer/polymer/lib/legacy/polymer-fn.js'; +import { LegacyElementMixin } from '@polymer/polymer/lib/legacy/legacy-element-mixin.js'; +const $_documentContainer = document.createElement('template'); + +$_documentContainer.innerHTML = ``; + +document.head.appendChild($_documentContainer.content); +switch (syntax) { +default: +case 'mixin': + { + class EmptyElement extends Mixins.Localizable(LegacyElementMixin(HTMLElement)) { + static get importMeta() { + return import.meta; + } + + static get template() { + return html` + +`; + } + + static get is() { return 'empty-element' } + } + customElements.define(EmptyElement.is, EmptyElement); + } + break; +case 'base-element': + { + class EmptyElement extends BaseElements.I18nElement { + static get importMeta() { + return import.meta; + } + + static get template() { + return html` + +`; + } + + static get is() { return 'empty-element' } + } + customElements.define(EmptyElement.is, EmptyElement); + } + break; +case 'thin': + { + Define = class EmptyElement extends BaseElements.I18nElement { + + static get importMeta() { + return import.meta; + } + + } + } + break; +case 'legacy': + { + Polymer({ + importMeta: import.meta, + + _template: html` + +`, + + is: 'empty-element', + + behaviors: [ + BehaviorsStore.I18nBehavior + ] + }); + } + break; +} diff --git a/test/src3/edge-case/empty-element.json b/test/src3/edge-case/empty-element.json new file mode 100755 index 00000000..9e26dfee --- /dev/null +++ b/test/src3/edge-case/empty-element.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/test/src3/edge-case/locales/advanced-binding-element.fr.json b/test/src3/edge-case/locales/advanced-binding-element.fr.json new file mode 100755 index 00000000..da0baae1 --- /dev/null +++ b/test/src3/edge-case/locales/advanced-binding-element.fr.json @@ -0,0 +1,36 @@ +{ + "meta": {}, + "model": { + "aria-attributes": { + "title": "fr tooltip text", + "aria-label": "fr aria label text", + "aria-valuetext": "fr aria value text" + } + }, + "annotated-format": [ + "{{tr(status,text.statusMessageFormats)}}", + "{{parameter}}", + "fr string parameter" + ], + "span_5": [ + "fr {1} {2}", + "{{text.defaultValue}}", + "{{text.defaultValue}}" + ], + "statusMessages": { + "ok": "fr healthy status", + "busy": "fr busy status", + "error": "fr error status", + "default": "fr unknown status" + }, + "defaultValue": "fr default value", + "statusMessageFormats": { + "ok": "fr healthy status", + "busy": "fr busy status with {2}", + "error": "fr error status with {1} and {2}", + "default": "fr unknown status" + }, + "nodefault": { + "ok": "fr ok status" + } +} \ No newline at end of file diff --git a/test/src3/edge-case/locales/complex-compound-binding-element.fr.json b/test/src3/edge-case/locales/complex-compound-binding-element.fr.json new file mode 100755 index 00000000..18095e03 --- /dev/null +++ b/test/src3/edge-case/locales/complex-compound-binding-element.fr.json @@ -0,0 +1,69 @@ +{ + "meta": {}, + "model": {}, + "item-update2:text": [ + "fr updated: {1}, by: ", + "{{text.updated}}" + ], + "item-update2:text_2": " fr xxx ", + "item-update2:dom-if_3:template:span:b": "fr IF CONTENT", + "item-update2:b_4": "fr abc", + "item-update2:dom-if_5:template:text": "fr IF CONTENT 2", + "item-update2:text_6": " fr hello ", + "item-update:text": [ + "fr updated: {1}, by: ", + "{{text.updated}}" + ], + "item-update:text_2": " fr xxx ", + "item-update:dom-if_3:template:b": "fr IF CONTENT", + "item-update:b_4": "fr abc", + "item-update:dom-if_5:template:text": "fr IF CONTENT 2", + "item-update:text_6": " fr hello ", + "item-update3:text": [ + "fr updated: {1}, by: ", + "{{text.updated}}" + ], + "item-update3:text_2": " fr xxx ", + "item-update3:dom-if_3:template:b": "fr IF", + "item-update3:dom-if_3:template:b_1": "fr CONTENT", + "item-update3:b_4": "fr abc", + "item-update3:dom-if_5:template:text": "fr IF CONTENT 2", + "item-update3:text_6": " fr hello ", + "item-update4:text": [ + "fr updated: {1}, by: ", + "{{text.updated}}" + ], + "item-update4:dom-repeat_1:template:text": [ + " fr {1} = {2} ", + "{{item.name}}", + "{{text.updated}}" + ], + "item-update4:text_2": " fr xxx ", + "item-update4:dom-if_3:template:b": "fr IF CONTENT", + "item-update4:b_4": "fr abc", + "item-update4:dom-if_5:template:text": "fr IF CONTENT 2", + "item-update4:text_6": " fr hello ", + "paragraph:text": "fr A paragraph with ", + "paragraph:text_2": " fr is converted to ", + "paragraph:code_3": "fr ", + "paragraph:text_4": "fr . ", + "paragraph2:text": "fr A paragraph with deep ", + "paragraph2:text_2": " fr is ", + "paragraph2:b_3": "fr not", + "paragraph2:text_4": " fr converted to ", + "paragraph2:code_5": "fr ", + "paragraph2:text_6": "fr . ", + "authors": [ + { + "name": "fr Joe" + }, + { + "name": "fr Alice" + } + ], + "updated": "fr Jan 1st, 2016", + "parameters": [ + "fr parameter 1", + "fr parameter 2" + ] +} \ No newline at end of file diff --git a/test/src3/edge-case/locales/empty-element.fr.json b/test/src3/edge-case/locales/empty-element.fr.json new file mode 100755 index 00000000..9e26dfee --- /dev/null +++ b/test/src3/edge-case/locales/empty-element.fr.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/test/src3/edge-case/no-template-element.js b/test/src3/edge-case/no-template-element.js new file mode 100644 index 00000000..775a9f2a --- /dev/null +++ b/test/src3/edge-case/no-template-element.js @@ -0,0 +1,58 @@ +/** +@license https://github.com/t2ym/i18n-behavior/blob/master/LICENSE.md +Copyright (c) 2016, Tetsuya Mori . All rights reserved. +*/ +import 'i18n-behavior/i18n-behavior.js'; + +import { Polymer } from '@polymer/polymer/lib/legacy/polymer-fn.js'; +import { LegacyElementMixin } from '@polymer/polymer/lib/legacy/legacy-element-mixin.js'; +switch (syntax) { +default: +case 'mixin': + { + class NoTemplateElement extends Mixins.Localizable(LegacyElementMixin(HTMLElement)) { + static get importMeta() { + return import.meta; + } + + static get is() { return 'no-template-element' } + } + customElements.define(NoTemplateElement.is, NoTemplateElement); + } + break; +case 'base-element': + { + class NoTemplateElement extends BaseElements.I18nElement { + static get importMeta() { + return import.meta; + } + + static get is() { return 'no-template-element' } + } + customElements.define(NoTemplateElement.is, NoTemplateElement); + } + break; +case 'thin': + { + Define = class NoTemplateElement extends BaseElements.I18nElement { + + static get importMeta() { + return import.meta; + } + + } + } + break; +case 'legacy': + { + Polymer({ + importMeta: import.meta, + is: 'no-template-element', + + behaviors: [ + BehaviorsStore.I18nBehavior + ] + }); + } + break; +} diff --git a/test/src3/fake-server.js b/test/src3/fake-server.js new file mode 100644 index 00000000..9e789947 --- /dev/null +++ b/test/src3/fake-server.js @@ -0,0 +1,45 @@ +"use strict"; +var fakeServerContents = +{ + "/commented-simple-text-element/commented-simple-text-element.json": "{\n \"meta\": {},\n \"model\": {},\n \"text\": \" outermost text at the beginning \",\n \"h1_3\": \"outermost header 1\",\n \"text_4\": \" outermost text in the middle \",\n \"span_5\": \"simple text without id\",\n \"span_6\": \"simple text without id 2\",\n \"label-1\": \"simple text with id\",\n \"label-2\": \"simple text with id 2\",\n \"div_9:span\": \"simple text within div\",\n \"div_9:span_1\": \"simple text within div 2\",\n \"div_9:div_2:div\": \"great grandchild text within div\",\n \"div_10:text\": \" simple text as the first element in div \",\n \"div_10:span_1\": \"simple text within div\",\n \"div_10:text_2\": \" simple text in the middle of div \",\n \"div_10:span_3\": \"simple text within div 2\",\n \"div_10:div_4:div\": \"great grandchild text within div\",\n \"div_10:text_5\": \" simple text at the last element in div \",\n \"toplevel-div:span\": \"simple text within div\",\n \"toplevel-div:span_1\": \"simple text within div 2\",\n \"third-level-div\": \"great grandchild text within div\",\n \"second-level-div:div_1\": \"great grandchild text within div without id\",\n \"div_12:ul:li\": \"line item without id 1\",\n \"div_12:ul:li_1\": \"line item without id 2\",\n \"div_12:ul:li_2\": \"line item without id 3\",\n \"line-items:li\": \"line item with id 1\",\n \"line-items:li_1\": \"line item with id 2\",\n \"line-items:li_2\": \"line item with id 3\",\n \"p_13\": [\n \"A paragraph with {1} is converted to {2}.\",\n \"parameters\",\n \"\"\n ],\n \"paragraph\": [\n \"A paragraph with {1} is converted to {2}.\",\n \"id\",\n \"\"\n ],\n \"text_15\": \" outermost text at the end \"\n}", + "/commented-simple-text-element/locales/commented-simple-text-element.fr.json": "{\n \"model\": {},\n \"text\": \" fr outermost text at the beginning \",\n \"h1_3\": \"fr outermost header 1\",\n \"text_4\": \" fr outermost text in the middle \",\n \"span_5\": \"fr simple text without id\",\n \"span_6\": \"fr simple text without id 2\",\n \"label-1\": \"fr simple text with id\",\n \"label-2\": \"fr simple text with id 2\",\n \"div_9:span\": \"fr simple text within div\",\n \"div_9:span_1\": \"fr simple text within div 2\",\n \"div_9:div_2:div\": \"fr great grandchild text within div\",\n \"div_10:text\": \" fr simple text as the first element in div \",\n \"div_10:span_1\": \"fr simple text within div\",\n \"div_10:text_2\": \" fr simple text in the middle of div \",\n \"div_10:span_3\": \"fr simple text within div 2\",\n \"div_10:div_4:div\": \"fr great grandchild text within div\",\n \"div_10:text_5\": \" fr simple text at the last element in div \",\n \"toplevel-div:span\": \"fr simple text within div\",\n \"toplevel-div:span_1\": \"fr simple text within div 2\",\n \"third-level-div\": \"fr great grandchild text within div\",\n \"second-level-div:div_1\": \"fr great grandchild text within div without id\",\n \"div_12:ul:li\": \"fr line item without id 1\",\n \"div_12:ul:li_1\": \"fr line item without id 2\",\n \"div_12:ul:li_2\": \"fr line item without id 3\",\n \"line-items:li\": \"fr line item with id 1\",\n \"line-items:li_1\": \"fr line item with id 2\",\n \"line-items:li_2\": \"fr line item with id 3\",\n \"p_13\": [\n \"fr A paragraph with {1} is converted to {2}.\",\n \"fr parameters\",\n \"fr \"\n ],\n \"paragraph\": [\n \"fr A paragraph with {1} is converted to {2}.\",\n \"fr id\",\n \"fr \"\n ],\n \"text_15\": \" fr outermost text at the end \"\n}\n", + "/compound-binding-dom-bind.json": "{\n \"meta\": {},\n \"model\": {},\n \"text\": [\n \" outermost text at the beginning with compound {1} and {2} variables \",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"h1_3\": [\n \"outermost header 1 with {1} and {2} variables\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"text_4\": [\n \" outermost text in the middle with {1} and {2} variables \",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"span_5\": [\n \"simple text without id with {1} and {2} variables\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"span_6\": [\n \"simple text without id 2 with {1} and {2} variables\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"label-1\": [\n \"simple text with id and {1} and {2} variables\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"label-2\": [\n \"simple text with id and {1} and {2} variables 2\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"div_9:span\": [\n \"simple text within div with {1} and {2} variables\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"div_9:span_1\": [\n \"simple text within div with {1} and {2} variables 2\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"div_9:div_2:div\": [\n \"great grandchild text within div with {1} and {2} variables\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"div_10:text\": [\n \" simple text as the first element in div with {1} and {2} variables \",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"div_10:span_1\": [\n \"simple text within div with {1} and {2} variables\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"div_10:text_2\": [\n \" simple text in the middle of div with {1} and {2} variables \",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"div_10:span_3\": [\n \"simple text within div with {1} and {2} variables 2\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"div_10:div_4:div\": [\n \"great grandchild text within div with {1} and {2} variables\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"div_10:text_5\": [\n \" simple text at the last element in div with {1} and {2} variables \",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"toplevel-div:span\": [\n \"simple text within div with {1} and {2} variables\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"toplevel-div:span_1\": [\n \"simple text within div 2 with {1} and {2} variables\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"third-level-div\": [\n \"great grandchild text within div with {1} and {2} variables\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"second-level-div:div_1\": [\n \"great grandchild text within div without id with {1} and {2} variables\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"div_12:ul:li\": [\n \"line item without id 1 with {1} and {2} variables\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"div_12:ul:li_1\": [\n \"line item without id 2 with {1} and {2} variables\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"div_12:ul:li_2\": [\n \"line item without id 3 with {1} and {2} variables\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"line-items:li\": [\n \"line item with id 1 with {1} and {2} variables\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"line-items:li_1\": [\n \"line item with id 2 with {1} and {2} variables\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"line-items:li_2\": [\n \"line item with id 3 with {1} and {2} variables\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"p_13\": [\n \"A paragraph with {1} is converted to {2}.\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"paragraph\": [\n \"A paragraph with {1}, {2}, and {3} is converted to {4}.\",\n \"id\",\n \"{{param1}}\",\n \"{{param2}}\",\n \"\"\n ],\n \"text_15\": [\n \" outermost text at the end with {1} and {2} variables \",\n \"{{param1}}\",\n \"{{param2}}\"\n ]\n}", + "/compound-binding-element/compound-binding-element.json": "{\n \"meta\": {},\n \"model\": {},\n \"text\": [\n \" outermost text at the beginning with compound {1} and {2} variables \",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"h1_3\": [\n \"outermost header 1 with {1} and {2} variables\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"text_4\": [\n \" outermost text in the middle with {1} and {2} variables \",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"span_5\": [\n \"simple text without id with {1} and {2} variables\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"span_6\": [\n \"simple text without id 2 with {1} and {2} variables\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"label-1\": [\n \"simple text with id and {1} and {2} variables\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"label-2\": [\n \"simple text with id and {1} and {2} variables 2\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"div_9:span\": [\n \"simple text within div with {1} and {2} variables\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"div_9:span_1\": [\n \"simple text within div with {1} and {2} variables 2\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"div_9:div_2:div\": [\n \"great grandchild text within div with {1} and {2} variables\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"div_10:text\": [\n \" simple text as the first element in div with {1} and {2} variables \",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"div_10:span_1\": [\n \"simple text within div with {1} and {2} variables\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"div_10:text_2\": [\n \" simple text in the middle of div with {1} and {2} variables \",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"div_10:span_3\": [\n \"simple text within div with {1} and {2} variables 2\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"div_10:div_4:div\": [\n \"great grandchild text within div with {1} and {2} variables\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"div_10:text_5\": [\n \" simple text at the last element in div with {1} and {2} variables \",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"toplevel-div:span\": [\n \"simple text within div with {1} and {2} variables\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"toplevel-div:span_1\": [\n \"simple text within div 2 with {1} and {2} variables\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"third-level-div\": [\n \"great grandchild text within div with {1} and {2} variables\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"second-level-div:div_1\": [\n \"great grandchild text within div without id with {1} and {2} variables\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"div_12:ul:li\": [\n \"line item without id 1 with {1} and {2} variables\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"div_12:ul:li_1\": [\n \"line item without id 2 with {1} and {2} variables\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"div_12:ul:li_2\": [\n \"line item without id 3 with {1} and {2} variables\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"line-items:li\": [\n \"line item with id 1 with {1} and {2} variables\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"line-items:li_1\": [\n \"line item with id 2 with {1} and {2} variables\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"line-items:li_2\": [\n \"line item with id 3 with {1} and {2} variables\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"p_13\": [\n \"A paragraph with {1} is converted to {2}.\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"paragraph\": [\n \"A paragraph with {1}, {2}, and {3} is converted to {4}.\",\n \"id\",\n \"{{param1}}\",\n \"{{param2}}\",\n \"\"\n ],\n \"text_15\": [\n \" outermost text at the end with {1} and {2} variables \",\n \"{{param1}}\",\n \"{{param2}}\"\n ]\n}", + "/compound-binding-element/locales/compound-binding-element.fr.json": "{\n \"model\": {},\n \"text\": [\n \" fr outermost text at the beginning with compound {1} and {2} variables \",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"h1_3\": [\n \"fr outermost header 1 with {1} and {2} variables\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"text_4\": [\n \" fr outermost text in the middle with {1} and {2} variables \",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"span_5\": [\n \"fr simple text without id with {1} and {2} variables\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"span_6\": [\n \"fr simple text without id 2 with {1} and {2} variables\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"label-1\": [\n \"fr simple text with id and {1} and {2} variables\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"label-2\": [\n \"fr simple text with id and {1} and {2} variables 2\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"div_9:span\": [\n \"fr simple text within div with {1} and {2} variables\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"div_9:span_1\": [\n \"fr simple text within div with {1} and {2} variables 2\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"div_9:div_2:div\": [\n \"fr great grandchild text within div with {1} and {2} variables\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"div_10:text\": [\n \" fr simple text as the first element in div with {1} and {2} variables \",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"div_10:span_1\": [\n \"fr simple text within div with {1} and {2} variables\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"div_10:text_2\": [\n \" fr simple text in the middle of div with {1} and {2} variables \",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"div_10:span_3\": [\n \"fr simple text within div with {1} and {2} variables 2\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"div_10:div_4:div\": [\n \"fr great grandchild text within div with {1} and {2} variables\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"div_10:text_5\": [\n \" fr simple text at the last element in div with {1} and {2} variables \",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"toplevel-div:span\": [\n \"fr simple text within div with {1} and {2} variables\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"toplevel-div:span_1\": [\n \"fr simple text within div 2 with {1} and {2} variables\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"third-level-div\": [\n \"fr great grandchild text within div with {1} and {2} variables\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"second-level-div:div_1\": [\n \"fr great grandchild text within div without id with {1} and {2} variables\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"div_12:ul:li\": [\n \"fr line item without id 1 with {1} and {2} variables\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"div_12:ul:li_1\": [\n \"fr line item without id 2 with {1} and {2} variables\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"div_12:ul:li_2\": [\n \"fr line item without id 3 with {1} and {2} variables\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"line-items:li\": [\n \"fr line item with id 1 with {1} and {2} variables\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"line-items:li_1\": [\n \"fr line item with id 2 with {1} and {2} variables\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"line-items:li_2\": [\n \"fr line item with id 3 with {1} and {2} variables\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"p_13\": [\n \"fr A paragraph with {1} is converted to {2}.\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"paragraph\": [\n \"fr A paragraph with {1}, {2}, and {3} is converted to {4}.\",\n \"fr id\",\n \"{{param1}}\",\n \"{{param2}}\",\n \"fr \"\n ],\n \"text_15\": [\n \" fr outermost text at the end with {1} and {2} variables \",\n \"{{param1}}\",\n \"{{param2}}\"\n ]\n}\n", + "/edge-case-dom-bind.json": "{\n \"meta\": {},\n \"model\": {},\n \"text\": [\n \" name = {1} \",\n \"{{text.name}}\"\n ],\n \"i18n-number_1\": \"1\",\n \"i18n-format_2\": [\n \"{{text.format}}\",\n \"1\"\n ],\n \"i18n-format_3\": [\n \"format\",\n \"\"\n ],\n \"p_8\": [\n \"hello {1}{2} {3} world\",\n \"
\",\n \"\",\n \"\"\n ],\n \"p_9\": [\n \"hello{1}world\",\n \"
\"\n ],\n \"text_10\": \" hello \",\n \"text_14\": \" world \"\n}", + "/edge-case/advanced-binding-element.json": "{\n \"meta\": {},\n \"model\": {\n \"aria-attributes\": {\n \"title\": \"tooltip text\",\n \"aria-label\": \"aria label text\",\n \"aria-valuetext\": \"aria value text\"\n }\n },\n \"annotated-format\": [\n \"{{tr(status,text.statusMessageFormats)}}\",\n \"{{parameter}}\",\n \"string parameter\"\n ],\n \"span_5\": [\n \"{1} {2}\",\n \"{{text.defaultValue}}\",\n \"{{text.defaultValue}}\"\n ],\n \"statusMessages\": {\n \"ok\": \"healthy status\",\n \"busy\": \"busy status\",\n \"error\": \"error status\",\n \"default\": \"unknown status\"\n },\n \"defaultValue\": \"default value\",\n \"statusMessageFormats\": {\n \"ok\": \"healthy status\",\n \"busy\": \"busy status with {2}\",\n \"error\": \"error status with {1} and {2}\",\n \"default\": \"unknown status\"\n },\n \"nodefault\": {\n \"ok\": \"ok status\"\n }\n}", + "/edge-case/complex-compound-binding-element.json": "{\n \"meta\": {},\n \"model\": {},\n \"item-update2:text\": [\n \"updated: {1}, by: \",\n \"{{text.updated}}\"\n ],\n \"item-update2:text_2\": \" xxx \",\n \"item-update2:dom-if_3:template:span:b\": \"IF CONTENT\",\n \"item-update2:b_4\": \"abc\",\n \"item-update2:dom-if_5:template:text\": \"IF CONTENT 2\",\n \"item-update2:text_6\": \" hello \",\n \"item-update:text\": [\n \"updated: {1}, by: \",\n \"{{text.updated}}\"\n ],\n \"item-update:text_2\": \" xxx \",\n \"item-update:dom-if_3:template:b\": \"IF CONTENT\",\n \"item-update:b_4\": \"abc\",\n \"item-update:dom-if_5:template:text\": \"IF CONTENT 2\",\n \"item-update:text_6\": \" hello \",\n \"item-update3:text\": [\n \"updated: {1}, by: \",\n \"{{text.updated}}\"\n ],\n \"item-update3:text_2\": \" xxx \",\n \"item-update3:dom-if_3:template:b\": \"IF\",\n \"item-update3:dom-if_3:template:b_1\": \"CONTENT\",\n \"item-update3:b_4\": \"abc\",\n \"item-update3:dom-if_5:template:text\": \"IF CONTENT 2\",\n \"item-update3:text_6\": \" hello \",\n \"item-update4:text\": [\n \"updated: {1}, by: \",\n \"{{text.updated}}\"\n ],\n \"item-update4:dom-repeat_1:template:text\": [\n \" {1} = {2} \",\n \"{{item.name}}\",\n \"{{text.updated}}\"\n ],\n \"item-update4:text_2\": \" xxx \",\n \"item-update4:dom-if_3:template:b\": \"IF CONTENT\",\n \"item-update4:b_4\": \"abc\",\n \"item-update4:dom-if_5:template:text\": \"IF CONTENT 2\",\n \"item-update4:text_6\": \" hello \",\n \"paragraph:text\": \"A paragraph with \",\n \"paragraph:text_2\": \" is converted to \",\n \"paragraph:code_3\": \"\",\n \"paragraph:text_4\": \". \",\n \"paragraph2:text\": \"A paragraph with deep \",\n \"paragraph2:text_2\": \" is \",\n \"paragraph2:b_3\": \"not\",\n \"paragraph2:text_4\": \" converted to \",\n \"paragraph2:code_5\": \"\",\n \"paragraph2:text_6\": \". \",\n \"authors\": [\n {\n \"name\": \"Joe\"\n },\n {\n \"name\": \"Alice\"\n }\n ],\n \"updated\": \"Jan 1st, 2016\",\n \"parameters\": [\n \"parameter 1\",\n \"parameter 2\"\n ]\n}", + "/edge-case/empty-element.json": "{}", + "/edge-case/locales/advanced-binding-element.fr.json": "{\n \"meta\": {},\n \"model\": {\n \"aria-attributes\": {\n \"title\": \"fr tooltip text\",\n \"aria-label\": \"fr aria label text\",\n \"aria-valuetext\": \"fr aria value text\"\n }\n },\n \"annotated-format\": [\n \"{{tr(status,text.statusMessageFormats)}}\",\n \"{{parameter}}\",\n \"fr string parameter\"\n ],\n \"span_5\": [\n \"fr {1} {2}\",\n \"{{text.defaultValue}}\",\n \"{{text.defaultValue}}\"\n ],\n \"statusMessages\": {\n \"ok\": \"fr healthy status\",\n \"busy\": \"fr busy status\",\n \"error\": \"fr error status\",\n \"default\": \"fr unknown status\"\n },\n \"defaultValue\": \"fr default value\",\n \"statusMessageFormats\": {\n \"ok\": \"fr healthy status\",\n \"busy\": \"fr busy status with {2}\",\n \"error\": \"fr error status with {1} and {2}\",\n \"default\": \"fr unknown status\"\n },\n \"nodefault\": {\n \"ok\": \"fr ok status\"\n }\n}", + "/edge-case/locales/complex-compound-binding-element.fr.json": "{\n \"meta\": {},\n \"model\": {},\n \"item-update2:text\": [\n \"fr updated: {1}, by: \",\n \"{{text.updated}}\"\n ],\n \"item-update2:text_2\": \" fr xxx \",\n \"item-update2:dom-if_3:template:span:b\": \"fr IF CONTENT\",\n \"item-update2:b_4\": \"fr abc\",\n \"item-update2:dom-if_5:template:text\": \"fr IF CONTENT 2\",\n \"item-update2:text_6\": \" fr hello \",\n \"item-update:text\": [\n \"fr updated: {1}, by: \",\n \"{{text.updated}}\"\n ],\n \"item-update:text_2\": \" fr xxx \",\n \"item-update:dom-if_3:template:b\": \"fr IF CONTENT\",\n \"item-update:b_4\": \"fr abc\",\n \"item-update:dom-if_5:template:text\": \"fr IF CONTENT 2\",\n \"item-update:text_6\": \" fr hello \",\n \"item-update3:text\": [\n \"fr updated: {1}, by: \",\n \"{{text.updated}}\"\n ],\n \"item-update3:text_2\": \" fr xxx \",\n \"item-update3:dom-if_3:template:b\": \"fr IF\",\n \"item-update3:dom-if_3:template:b_1\": \"fr CONTENT\",\n \"item-update3:b_4\": \"fr abc\",\n \"item-update3:dom-if_5:template:text\": \"fr IF CONTENT 2\",\n \"item-update3:text_6\": \" fr hello \",\n \"item-update4:text\": [\n \"fr updated: {1}, by: \",\n \"{{text.updated}}\"\n ],\n \"item-update4:dom-repeat_1:template:text\": [\n \" fr {1} = {2} \",\n \"{{item.name}}\",\n \"{{text.updated}}\"\n ],\n \"item-update4:text_2\": \" fr xxx \",\n \"item-update4:dom-if_3:template:b\": \"fr IF CONTENT\",\n \"item-update4:b_4\": \"fr abc\",\n \"item-update4:dom-if_5:template:text\": \"fr IF CONTENT 2\",\n \"item-update4:text_6\": \" fr hello \",\n \"paragraph:text\": \"fr A paragraph with \",\n \"paragraph:text_2\": \" fr is converted to \",\n \"paragraph:code_3\": \"fr \",\n \"paragraph:text_4\": \"fr . \",\n \"paragraph2:text\": \"fr A paragraph with deep \",\n \"paragraph2:text_2\": \" fr is \",\n \"paragraph2:b_3\": \"fr not\",\n \"paragraph2:text_4\": \" fr converted to \",\n \"paragraph2:code_5\": \"fr \",\n \"paragraph2:text_6\": \"fr . \",\n \"authors\": [\n {\n \"name\": \"fr Joe\"\n },\n {\n \"name\": \"fr Alice\"\n }\n ],\n \"updated\": \"fr Jan 1st, 2016\",\n \"parameters\": [\n \"fr parameter 1\",\n \"fr parameter 2\"\n ]\n}", + "/edge-case/locales/empty-element.fr.json": "{}", + "/fallback-text-element/fallback-text-element.json": "{\n \"meta\": {},\n \"model\": {},\n \"text\": \" outermost text at the beginning \",\n \"h1_3\": \"outermost header 1\",\n \"text_4\": \" outermost text in the middle \",\n \"span_5\": \"simple text without id\",\n \"span_6\": \"simple text without id 2\",\n \"label-1\": \"simple text with id\",\n \"label-2\": \"simple text with id 2\",\n \"div_9:span\": \"simple text within div\",\n \"div_9:span_1\": \"simple text within div 2\",\n \"div_9:div_2:div\": \"great grandchild text within div\",\n \"div_10:text\": \" simple text as the first element in div \",\n \"div_10:span_1\": \"simple text within div\",\n \"div_10:text_2\": \" simple text in the middle of div \",\n \"div_10:span_3\": \"simple text within div 2\",\n \"div_10:div_4:div\": \"great grandchild text within div\",\n \"div_10:text_5\": \" simple text at the last element in div \",\n \"toplevel-div:span\": \"simple text within div\",\n \"toplevel-div:span_1\": \"simple text within div 2\",\n \"third-level-div\": \"great grandchild text within div\",\n \"second-level-div:div_1\": \"great grandchild text within div without id\",\n \"div_12:ul:li\": \"line item without id 1\",\n \"div_12:ul:li_1\": \"line item without id 2\",\n \"div_12:ul:li_2\": \"line item without id 3\",\n \"line-items:li\": \"line item with id 1\",\n \"line-items:li_1\": \"line item with id 2\",\n \"line-items:li_2\": \"line item with id 3\",\n \"p_13\": [\n \"A paragraph with {1} is converted to {2}.\",\n \"parameters\",\n \"\"\n ],\n \"paragraph\": [\n \"A paragraph with {1} is converted to {2}.\",\n \"id\",\n \"\"\n ],\n \"text_15\": \" outermost text at the end \"\n}", + "/fallback-text-element/locales/fallback-text-element.fr-CA.json": "{\n \"model\": {},\n \"text\": \"fr-CA outermost text at the beginning \",\n \"h1_3\": \"fr-CA outermost header 1\",\n \"text_4\": \"fr-CA outermost text in the middle \",\n \"span_5\": \"fr-CA simple text without id\",\n \"span_6\": \"fr-CA simple text without id 2\",\n \"label-1\": \"fr-CA simple text with id\",\n \"label-2\": \"fr-CA simple text with id 2\",\n \"div_10:span_1\": \"fr-CA simple text within div\",\n \"toplevel-div:span\": \"fr-CA simple text within div\",\n \"toplevel-div:span_1\": \"fr-CA simple text within div 2\",\n \"third-level-div\": \"fr-CA great grandchild text within div\",\n \"second-level-div:div_1\": \"fr-CA great grandchild text within div without id\",\n \"p_13\": [\n \"fr-CA A paragraph with {1} is converted to {2}.\",\n \"fr-CA parameters\",\n \"fr-CA \"\n ],\n \"paragraph\": [\n \"fr-CA A paragraph with {1} is converted to {2}.\",\n \"fr-CA id\",\n \"fr-CA \"\n ],\n \"text_15\": \"fr-CA outermost text at the end \"\n}\n", + "/fallback-text-element/locales/fallback-text-element.fr.json": "{\n \"model\": {},\n \"text\": \"fr outermost text at the beginning \",\n \"h1_3\": \"fr outermost header 1\",\n \"text_4\": \"fr outermost text in the middle \",\n \"span_5\": \"fr simple text without id\",\n \"span_6\": \"fr simple text without id 2\",\n \"label-1\": \"fr simple text with id\",\n \"label-2\": \"fr simple text with id 2\",\n \"toplevel-div:span\": \"fr simple text within div\",\n \"toplevel-div:span_1\": \"fr simple text within div 2\",\n \"third-level-div\": \"fr great grandchild text within div\",\n \"second-level-div:div_1\": \"fr great grandchild text within div without id\",\n \"div_12:ul:li\": \"fr line item without id 1\",\n \"div_12:ul:li_1\": \"fr line item without id 2\",\n \"div_12:ul:li_2\": \"fr line item without id 3\",\n \"line-items:li\": \"fr line item with id 1\",\n \"line-items:li_1\": \"fr line item with id 2\",\n \"line-items:li_2\": \"fr line item with id 3\",\n \"p_13\": [\n \"fr A paragraph with {1} is converted to {2}.\",\n \"fr parameters\",\n \"fr \"\n ],\n \"paragraph\": [\n \"fr A paragraph with {1} is converted to {2}.\",\n \"fr id\",\n \"fr \"\n ],\n \"text_15\": \"fr outermost text at the end \"\n}\n", + "/locales/compound-binding-dom-bind.fr.json": "{\n \"model\": {},\n \"text\": [\n \" fr outermost text at the beginning with compound {1} and {2} variables \",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"h1_3\": [\n \"fr outermost header 1 with {1} and {2} variables\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"text_4\": [\n \" fr outermost text in the middle with {1} and {2} variables \",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"span_5\": [\n \"fr simple text without id with {1} and {2} variables\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"span_6\": [\n \"fr simple text without id 2 with {1} and {2} variables\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"label-1\": [\n \"fr simple text with id and {1} and {2} variables\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"label-2\": [\n \"fr simple text with id and {1} and {2} variables 2\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"div_9:span\": [\n \"fr simple text within div with {1} and {2} variables\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"div_9:span_1\": [\n \"fr simple text within div with {1} and {2} variables 2\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"div_9:div_2:div\": [\n \"fr great grandchild text within div with {1} and {2} variables\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"div_10:text\": [\n \" fr simple text as the first element in div with {1} and {2} variables \",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"div_10:span_1\": [\n \"fr simple text within div with {1} and {2} variables\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"div_10:text_2\": [\n \" fr simple text in the middle of div with {1} and {2} variables \",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"div_10:span_3\": [\n \"fr simple text within div with {1} and {2} variables 2\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"div_10:div_4:div\": [\n \"fr great grandchild text within div with {1} and {2} variables\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"div_10:text_5\": [\n \" fr simple text at the last element in div with {1} and {2} variables \",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"toplevel-div:span\": [\n \"fr simple text within div with {1} and {2} variables\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"toplevel-div:span_1\": [\n \"fr simple text within div 2 with {1} and {2} variables\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"third-level-div\": [\n \"fr great grandchild text within div with {1} and {2} variables\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"second-level-div:div_1\": [\n \"fr great grandchild text within div without id with {1} and {2} variables\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"div_12:ul:li\": [\n \"fr line item without id 1 with {1} and {2} variables\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"div_12:ul:li_1\": [\n \"fr line item without id 2 with {1} and {2} variables\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"div_12:ul:li_2\": [\n \"fr line item without id 3 with {1} and {2} variables\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"line-items:li\": [\n \"fr line item with id 1 with {1} and {2} variables\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"line-items:li_1\": [\n \"fr line item with id 2 with {1} and {2} variables\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"line-items:li_2\": [\n \"fr line item with id 3 with {1} and {2} variables\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"p_13\": [\n \"fr A paragraph with {1} is converted to {2}.\",\n \"{{param1}}\",\n \"{{param2}}\"\n ],\n \"paragraph\": [\n \"fr A paragraph with {1}, {2}, and {3} is converted to {4}.\",\n \"fr id\",\n \"{{param1}}\",\n \"{{param2}}\",\n \"fr \"\n ],\n \"text_15\": [\n \" fr outermost text at the end with {1} and {2} variables \",\n \"{{param1}}\",\n \"{{param2}}\"\n ]\n}\n", + "/locales/simple-attribute-dom-bind.fr.json": "{\n \"model\": {\n \"standard-input\": {\n \"placeholder\": \"fr standard HTML5 attribute\"\n },\n \"outer-div:input_2\": {\n \"placeholder\": \"fr standard HTML5 attribute without id\"\n },\n \"paper-input-element\": {\n \"label\": \"fr paper-input label\",\n \"error-message\": \"fr paper-input error message\",\n \"placeholder\": \"fr paper-input placeholder\"\n },\n \"outer-div:paper-input_4\": {\n \"label\": \"fr paper-input label without id\",\n \"error-message\": \"fr paper-input error message without id\",\n \"placeholder\": \"fr paper-input placeholder without id\"\n },\n \"pie-chart\": {\n \"options\": {\n \"title\": \"fr Distribution of days in 2001H1\"\n },\n \"cols\": [\n {\n \"label\": \"fr Month\",\n \"type\": \"string\"\n },\n {\n \"label\": \"fr Days\",\n \"type\": \"number\"\n }\n ],\n \"rows\": [\n [\n \"fr Jan\",\n 31\n ],\n [\n \"fr Feb\",\n 28\n ],\n [\n \"fr Mar\",\n 31\n ],\n [\n \"fr Apr\",\n 30\n ],\n [\n \"fr May\",\n 31\n ],\n [\n \"fr Jun\",\n 30\n ]\n ]\n },\n \"column-chart\": {\n \"options\": {\n \"title\": \"fr Inventory\"\n },\n \"data\": [\n [\n \"fr Year\",\n \"fr Things\",\n \"fr Stuff\"\n ],\n [\n \"2004\",\n 1000,\n 400\n ],\n [\n \"2005\",\n 1170,\n 460\n ],\n [\n \"2006\",\n 660,\n 1120\n ],\n [\n \"2007\",\n 1030,\n 540\n ]\n ]\n },\n \"custom-attr\": {\n \"custom-text-attr1\": \"fr custom text attribute 1\",\n \"custom-text-attr2\": \"fr custom text attribute 2\",\n \"custom-text-attr3\": \"fr custom text attribute 3\"\n },\n \"selective-attr\": {\n \"custom-text-attr4\": [\n \"fr {1} custom-text-attr4 attribute with param {2} and param {3} {4}\",\n \"{{text.ordinary-div}}\",\n \"{{text.ordinary-div}}\",\n \"[[text.ordinary-div]]\",\n \"{{text.ordinary-div}}\"\n ],\n \"custom-text-attr5\": [\n \"[[text.ordinary-div]]\",\n \" fr custom-text-attr5 attribute with param \",\n \"{{or('',text.ordinary-div)}}\",\n \" fr and param \",\n \"[[text.ordinary-div]]\"\n ],\n \"i18n-target\": [\n \"fr i18n-target attribute with param {1} and param {2}\",\n \"{{text.ordinary-div}}\",\n \"[[text.ordinary-div]]\"\n ],\n \"i18n-target2\": [\n \"fr i18n-target2 attribute with param \",\n \"{{or('',text.ordinary-div)}}\",\n \" fr and param \",\n \"[[text.ordinary-div]]\"\n ]\n },\n \"selective-attr2\": {\n \"i18n-target\": \"fr i18n-target attribute 2\"\n },\n \"selective-attr3\": {\n \"i18n-target6\": \"fr i18n-target6 attribute 2\"\n },\n \"selective-attr4\": {\n \"i18n-target6\": \"fr i18n-target6 attribute 3\"\n },\n \"json-data-id\": {\n \"attr1\": \"fr this attr1 is extracted\",\n \"i18n-target-attr\": \"fr this attribute is also extracted\"\n },\n \"template_2:json-data_1\": {\n \"attr1\": \"fr this attr1 without id is extracted\",\n \"i18n-target-attr\": \"fr this attribute without id is also extracted\"\n }\n },\n \"ordinary-div\": \"fr text 1\"\n}\n", + "/locales/simple-text-dom-bind.fr.json": "{\n \"model\": {},\n \"text\": \" fr outermost text at the beginning \",\n \"h1_3\": \"fr outermost header 1\",\n \"text_4\": \" fr outermost text in the middle \",\n \"span_5\": \"fr simple text without id\",\n \"span_6\": \"fr simple text without id 2\",\n \"label-1\": \"fr simple text with id\",\n \"label-2\": \"fr simple text with id 2\",\n \"div_9:span\": \"fr simple text within div\",\n \"div_9:span_1\": \"fr simple text within div 2\",\n \"div_9:div_2:div\": \"fr great grandchild text within div\",\n \"div_10:text\": \" fr simple text as the first element in div \",\n \"div_10:span_1\": \"fr simple text within div\",\n \"div_10:text_2\": \" fr simple text in the middle of div \",\n \"div_10:span_3\": \"fr simple text within div 2\",\n \"div_10:div_4:div\": \"fr great grandchild text within div\",\n \"div_10:text_5\": \" fr simple text at the last element in div \",\n \"toplevel-div:span\": \"fr simple text within div\",\n \"toplevel-div:span_1\": \"fr simple text within div 2\",\n \"third-level-div\": \"fr great grandchild text within div\",\n \"second-level-div:div_1\": \"fr great grandchild text within div without id\",\n \"div_12:ul:li\": \"fr line item without id 1\",\n \"div_12:ul:li_1\": \"fr line item without id 2\",\n \"div_12:ul:li_2\": \"fr line item without id 3\",\n \"line-items:li\": \"fr line item with id 1\",\n \"line-items:li_1\": \"fr line item with id 2\",\n \"line-items:li_2\": \"fr line item with id 3\",\n \"p_13\": [\n \"fr A paragraph with {1} is converted to {2}.\",\n \"fr parameters\",\n \"fr \"\n ],\n \"paragraph\": [\n \"fr A paragraph with {1} is converted to {2}.\",\n \"fr id\",\n \"fr \"\n ],\n \"text_15\": \" fr outermost text at the end \"\n}\n", + "/multiple-case/item-element.json": "{\n \"meta\": {},\n \"model\": {},\n \"label\": \"A\"\n}", + "/multiple-case/locales/item-element.fr.json": "{\n \"meta\": {},\n \"model\": {},\n \"label\": \"fr A\"\n}", + "/multiple-case/locales/multiple-element.fr.json": "{\n \"meta\": {},\n \"model\": {}\n}", + "/multiple-case/multiple-element.json": "{\n \"meta\": {},\n \"model\": {}\n}", + "/plural-gender-element/locales/plural-gender-element.fr.json": "{\n \"model\": {},\n \"compound-format-text\": [\n {\n \"0\": \"fr You ({3}) gave no gifts.\",\n \"1\": {\n \"male\": \"fr You ({3}) gave him ({4}) {5}.\",\n \"female\": \"fr You ({3}) gave her ({4}) {5}.\",\n \"other\": \"fr You ({3}) gave them ({4}) {5}.\"\n },\n \"one\": {\n \"male\": \"fr You ({3}) gave him ({4}) and one other person {5}.\",\n \"female\": \"fr You ({3}) gave her ({4}) and one other person {5}.\",\n \"other\": \"fr You ({3}) gave them ({4}) and one other person {5}.\"\n },\n \"other\": \"fr You ({3}) gave them ({4}) and {1} other people gifts.\"\n },\n \"{{recipients.length - 1}}\",\n \"{{recipients.0.gender}}\",\n \"{{sender.name}}\",\n \"{{recipients.0.name}}\",\n \"fr a gift\"\n ]\n}\n", + "/plural-gender-element/plural-gender-element.json": "{\n \"meta\": {},\n \"model\": {},\n \"compound-format-text\": [\n {\n \"0\": \"You ({3}) gave no gifts.\",\n \"1\": {\n \"male\": \"You ({3}) gave him ({4}) {5}.\",\n \"female\": \"You ({3}) gave her ({4}) {5}.\",\n \"other\": \"You ({3}) gave them ({4}) {5}.\"\n },\n \"one\": {\n \"male\": \"You ({3}) gave him ({4}) and one other person {5}.\",\n \"female\": \"You ({3}) gave her ({4}) and one other person {5}.\",\n \"other\": \"You ({3}) gave them ({4}) and one other person {5}.\"\n },\n \"other\": \"You ({3}) gave them ({4}) and {1} other people gifts.\"\n },\n \"{{recipients.length - 1}}\",\n \"{{recipients.0.gender}}\",\n \"{{sender.name}}\",\n \"{{recipients.0.name}}\",\n \"a gift\"\n ]\n}", + "/preference/preference-element.json": "{\n \"meta\": {},\n \"model\": {}\n}", + "/simple-attribute-dom-bind.json": "{\n \"meta\": {},\n \"model\": {\n \"standard-input\": {\n \"placeholder\": \"standard HTML5 attribute\"\n },\n \"outer-div:input_2\": {\n \"placeholder\": \"standard HTML5 attribute without id\"\n },\n \"paper-input-element\": {\n \"label\": \"paper-input label\",\n \"error-message\": \"paper-input error message\",\n \"placeholder\": \"paper-input placeholder\"\n },\n \"outer-div:paper-input_4\": {\n \"label\": \"paper-input label without id\",\n \"error-message\": \"paper-input error message without id\",\n \"placeholder\": \"paper-input placeholder without id\"\n },\n \"pie-chart\": {\n \"options\": {\n \"title\": \"Distribution of days in 2001H1\"\n },\n \"cols\": [\n {\n \"label\": \"Month\",\n \"type\": \"string\"\n },\n {\n \"label\": \"Days\",\n \"type\": \"number\"\n }\n ],\n \"rows\": [\n [\n \"Jan\",\n 31\n ],\n [\n \"Feb\",\n 28\n ],\n [\n \"Mar\",\n 31\n ],\n [\n \"Apr\",\n 30\n ],\n [\n \"May\",\n 31\n ],\n [\n \"Jun\",\n 30\n ]\n ]\n },\n \"column-chart\": {\n \"options\": {\n \"title\": \"Inventory\"\n },\n \"data\": [\n [\n \"Year\",\n \"Things\",\n \"Stuff\"\n ],\n [\n \"2004\",\n 1000,\n 400\n ],\n [\n \"2005\",\n 1170,\n 460\n ],\n [\n \"2006\",\n 660,\n 1120\n ],\n [\n \"2007\",\n 1030,\n 540\n ]\n ]\n },\n \"custom-attr\": {\n \"custom-text-attr1\": \"custom text attribute 1\",\n \"custom-text-attr2\": \"custom text attribute 2\",\n \"custom-text-attr3\": \"custom text attribute 3\"\n },\n \"selective-attr\": {\n \"custom-text-attr4\": [\n \"{1} custom-text-attr4 attribute with param {2} and param {3} {4}\",\n \"{{text.ordinary-div}}\",\n \"{{text.ordinary-div}}\",\n \"[[text.ordinary-div]]\",\n \"{{text.ordinary-div}}\"\n ],\n \"custom-text-attr5\": [\n \"[[text.ordinary-div]]\",\n \" custom-text-attr5 attribute with param \",\n \"{{or('',text.ordinary-div)}}\",\n \" and param \",\n \"[[text.ordinary-div]]\"\n ],\n \"i18n-target\": [\n \"i18n-target attribute with param {1} and param {2}\",\n \"{{text.ordinary-div}}\",\n \"[[text.ordinary-div]]\"\n ],\n \"i18n-target2\": [\n \"i18n-target2 attribute with param \",\n \"{{or('',text.ordinary-div)}}\",\n \" and param \",\n \"[[text.ordinary-div]]\"\n ]\n },\n \"selective-attr2\": {\n \"i18n-target\": \"i18n-target attribute 2\"\n },\n \"selective-attr3\": {\n \"i18n-target6\": \"i18n-target6 attribute 2\"\n },\n \"selective-attr4\": {\n \"i18n-target6\": \"i18n-target6 attribute 3\"\n },\n \"json-data-id\": {\n \"attr1\": \"this attr1 is extracted\",\n \"i18n-target-attr\": \"this attribute is also extracted\"\n },\n \"template_2:json-data_1\": {\n \"attr1\": \"this attr1 without id is extracted\",\n \"i18n-target-attr\": \"this attribute without id is also extracted\"\n }\n },\n \"ordinary-div\": \"text 1\"\n}", + "/simple-attribute-element/locales/simple-attribute-element.fr.json": "{\n \"model\": {\n \"standard-input\": {\n \"placeholder\": \"fr standard HTML5 attribute\"\n },\n \"outer-div:input_2\": {\n \"placeholder\": \"fr standard HTML5 attribute without id\"\n },\n \"paper-input-element\": {\n \"label\": \"fr paper-input label\",\n \"error-message\": \"fr paper-input error message\",\n \"placeholder\": \"fr paper-input placeholder\"\n },\n \"outer-div:paper-input_4\": {\n \"label\": \"fr paper-input label without id\",\n \"error-message\": \"fr paper-input error message without id\",\n \"placeholder\": \"fr paper-input placeholder without id\"\n },\n \"pie-chart\": {\n \"options\": {\n \"title\": \"fr Distribution of days in 2001H1\"\n },\n \"cols\": [\n {\n \"label\": \"fr Month\",\n \"type\": \"string\"\n },\n {\n \"label\": \"fr Days\",\n \"type\": \"number\"\n }\n ],\n \"rows\": [\n [\n \"fr Jan\",\n 31\n ],\n [\n \"fr Feb\",\n 28\n ],\n [\n \"fr Mar\",\n 31\n ],\n [\n \"fr Apr\",\n 30\n ],\n [\n \"fr May\",\n 31\n ],\n [\n \"fr Jun\",\n 30\n ]\n ]\n },\n \"column-chart\": {\n \"options\": {\n \"title\": \"fr Inventory\"\n },\n \"data\": [\n [\n \"fr Year\",\n \"fr Things\",\n \"fr Stuff\"\n ],\n [\n \"2004\",\n 1000,\n 400\n ],\n [\n \"2005\",\n 1170,\n 460\n ],\n [\n \"2006\",\n 660,\n 1120\n ],\n [\n \"2007\",\n 1030,\n 540\n ]\n ]\n },\n \"custom-attr\": {\n \"custom-text-attr1\": \"fr custom text attribute 1\",\n \"custom-text-attr2\": \"fr custom text attribute 2\",\n \"custom-text-attr3\": \"fr custom text attribute 3\"\n },\n \"selective-attr\": {\n \"custom-text-attr4\": [\n \"fr {1} custom-text-attr4 attribute with param {2} and param {3} {4}\",\n \"{{text.ordinary-div}}\",\n \"{{text.ordinary-div}}\",\n \"[[text.ordinary-div]]\",\n \"{{text.ordinary-div}}\"\n ],\n \"custom-text-attr5\": [\n \"[[text.ordinary-div]]\",\n \" fr custom-text-attr5 attribute with param \",\n \"{{or('',text.ordinary-div)}}\",\n \" fr and param \",\n \"[[text.ordinary-div]]\"\n ],\n \"i18n-target\": [\n \"fr i18n-target attribute with param {1} and param {2}\",\n \"{{text.ordinary-div}}\",\n \"[[text.ordinary-div]]\"\n ],\n \"i18n-target2\": [\n \"fr i18n-target2 attribute with param \",\n \"{{or('',text.ordinary-div)}}\",\n \" fr and param \",\n \"[[text.ordinary-div]]\"\n ]\n },\n \"selective-attr2\": {\n \"i18n-target\": \"fr i18n-target attribute 2\"\n },\n \"selective-attr3\": {\n \"i18n-target6\": \"fr i18n-target6 attribute 2\"\n },\n \"selective-attr4\": {\n \"i18n-target6\": \"fr i18n-target6 attribute 3\"\n },\n \"json-data-id\": {\n \"attr1\": \"fr this attr1 is extracted\",\n \"i18n-target-attr\": \"fr this attribute is also extracted\"\n },\n \"template_2:json-data_1\": {\n \"attr1\": \"fr this attr1 without id is extracted\",\n \"i18n-target-attr\": \"fr this attribute without id is also extracted\"\n }\n },\n \"ordinary-div\": \"fr text 1\"\n}\n", + "/simple-attribute-element/locales/text-attribute-element.fr.json": "{\n \"meta\": {},\n \"model\": {},\n \"span_4\": \"fr text\"\n}\n", + "/simple-attribute-element/simple-attribute-element.json": "{\n \"meta\": {},\n \"model\": {\n \"standard-input\": {\n \"placeholder\": \"standard HTML5 attribute\"\n },\n \"outer-div:input_2\": {\n \"placeholder\": \"standard HTML5 attribute without id\"\n },\n \"paper-input-element\": {\n \"label\": \"paper-input label\",\n \"error-message\": \"paper-input error message\",\n \"placeholder\": \"paper-input placeholder\"\n },\n \"outer-div:paper-input_4\": {\n \"label\": \"paper-input label without id\",\n \"error-message\": \"paper-input error message without id\",\n \"placeholder\": \"paper-input placeholder without id\"\n },\n \"pie-chart\": {\n \"options\": {\n \"title\": \"Distribution of days in 2001H1\"\n },\n \"cols\": [\n {\n \"label\": \"Month\",\n \"type\": \"string\"\n },\n {\n \"label\": \"Days\",\n \"type\": \"number\"\n }\n ],\n \"rows\": [\n [\n \"Jan\",\n 31\n ],\n [\n \"Feb\",\n 28\n ],\n [\n \"Mar\",\n 31\n ],\n [\n \"Apr\",\n 30\n ],\n [\n \"May\",\n 31\n ],\n [\n \"Jun\",\n 30\n ]\n ]\n },\n \"column-chart\": {\n \"options\": {\n \"title\": \"Inventory\"\n },\n \"data\": [\n [\n \"Year\",\n \"Things\",\n \"Stuff\"\n ],\n [\n \"2004\",\n 1000,\n 400\n ],\n [\n \"2005\",\n 1170,\n 460\n ],\n [\n \"2006\",\n 660,\n 1120\n ],\n [\n \"2007\",\n 1030,\n 540\n ]\n ]\n },\n \"custom-attr\": {\n \"custom-text-attr1\": \"custom text attribute 1\",\n \"custom-text-attr2\": \"custom text attribute 2\",\n \"custom-text-attr3\": \"custom text attribute 3\"\n },\n \"selective-attr\": {\n \"custom-text-attr4\": [\n \"{1} custom-text-attr4 attribute with param {2} and param {3} {4}\",\n \"{{text.ordinary-div}}\",\n \"{{text.ordinary-div}}\",\n \"[[text.ordinary-div]]\",\n \"{{text.ordinary-div}}\"\n ],\n \"custom-text-attr5\": [\n \"[[text.ordinary-div]]\",\n \" custom-text-attr5 attribute with param \",\n \"{{or('',text.ordinary-div)}}\",\n \" and param \",\n \"[[text.ordinary-div]]\"\n ],\n \"i18n-target\": [\n \"i18n-target attribute with param {1} and param {2}\",\n \"{{text.ordinary-div}}\",\n \"[[text.ordinary-div]]\"\n ],\n \"i18n-target2\": [\n \"i18n-target2 attribute with param \",\n \"{{or('',text.ordinary-div)}}\",\n \" and param \",\n \"[[text.ordinary-div]]\"\n ]\n },\n \"selective-attr2\": {\n \"i18n-target\": \"i18n-target attribute 2\"\n },\n \"selective-attr3\": {\n \"i18n-target6\": \"i18n-target6 attribute 2\"\n },\n \"selective-attr4\": {\n \"i18n-target6\": \"i18n-target6 attribute 3\"\n },\n \"json-data-id\": {\n \"attr1\": \"this attr1 is extracted\",\n \"i18n-target-attr\": \"this attribute is also extracted\"\n },\n \"template_2:json-data_1\": {\n \"attr1\": \"this attr1 without id is extracted\",\n \"i18n-target-attr\": \"this attribute without id is also extracted\"\n }\n },\n \"ordinary-div\": \"text 1\"\n}", + "/simple-attribute-element/text-attribute-element.json": "{\n \"meta\": {},\n \"model\": {},\n \"span_4\": \"text\"\n}", + "/simple-text-dom-bind.json": "{\n \"meta\": {},\n \"model\": {},\n \"text\": \" outermost text at the beginning \",\n \"h1_3\": \"outermost header 1\",\n \"text_4\": \" outermost text in the middle \",\n \"span_5\": \"simple text without id\",\n \"span_6\": \"simple text without id 2\",\n \"label-1\": \"simple text with id\",\n \"label-2\": \"simple text with id 2\",\n \"div_9:span\": \"simple text within div\",\n \"div_9:span_1\": \"simple text within div 2\",\n \"div_9:div_2:div\": \"great grandchild text within div\",\n \"div_10:text\": \" simple text as the first element in div \",\n \"div_10:span_1\": \"simple text within div\",\n \"div_10:text_2\": \" simple text in the middle of div \",\n \"div_10:span_3\": \"simple text within div 2\",\n \"div_10:div_4:div\": \"great grandchild text within div\",\n \"div_10:text_5\": \" simple text at the last element in div \",\n \"toplevel-div:span\": \"simple text within div\",\n \"toplevel-div:span_1\": \"simple text within div 2\",\n \"third-level-div\": \"great grandchild text within div\",\n \"second-level-div:div_1\": \"great grandchild text within div without id\",\n \"div_12:ul:li\": \"line item without id 1\",\n \"div_12:ul:li_1\": \"line item without id 2\",\n \"div_12:ul:li_2\": \"line item without id 3\",\n \"line-items:li\": \"line item with id 1\",\n \"line-items:li_1\": \"line item with id 2\",\n \"line-items:li_2\": \"line item with id 3\",\n \"p_13\": [\n \"A paragraph with {1} is converted to {2}.\",\n \"parameters\",\n \"\"\n ],\n \"paragraph\": [\n \"A paragraph with {1} is converted to {2}.\",\n \"id\",\n \"\"\n ],\n \"text_15\": \" outermost text at the end \"\n}", + "/simple-text-element/locales/simple-text-element.fr.json": "{\n \"model\": {},\n \"text\": \" fr outermost text at the beginning \",\n \"h1_3\": \"fr outermost header 1\",\n \"text_4\": \" fr outermost text in the middle \",\n \"span_5\": \"fr simple text without id\",\n \"span_6\": \"fr simple text without id 2\",\n \"label-1\": \"fr simple text with id\",\n \"label-2\": \"fr simple text with id 2\",\n \"div_9:span\": \"fr simple text within div\",\n \"div_9:span_1\": \"fr simple text within div 2\",\n \"div_9:div_2:div\": \"fr great grandchild text within div\",\n \"div_10:text\": \" fr simple text as the first element in div \",\n \"div_10:span_1\": \"fr simple text within div\",\n \"div_10:text_2\": \" fr simple text in the middle of div \",\n \"div_10:span_3\": \"fr simple text within div 2\",\n \"div_10:div_4:div\": \"fr great grandchild text within div\",\n \"div_10:text_5\": \" fr simple text at the last element in div \",\n \"toplevel-div:span\": \"fr simple text within div\",\n \"toplevel-div:span_1\": \"fr simple text within div 2\",\n \"third-level-div\": \"fr great grandchild text within div\",\n \"second-level-div:div_1\": \"fr great grandchild text within div without id\",\n \"div_12:ul:li\": \"fr line item without id 1\",\n \"div_12:ul:li_1\": \"fr line item without id 2\",\n \"div_12:ul:li_2\": \"fr line item without id 3\",\n \"line-items:li\": \"fr line item with id 1\",\n \"line-items:li_1\": \"fr line item with id 2\",\n \"line-items:li_2\": \"fr line item with id 3\",\n \"p_13\": [\n \"fr A paragraph with {1} is converted to {2}.\",\n \"fr parameters\",\n \"fr \"\n ],\n \"paragraph\": [\n \"fr A paragraph with {1} is converted to {2}.\",\n \"fr id\",\n \"fr \"\n ],\n \"text_15\": \" fr outermost text at the end \"\n}\n", + "/simple-text-element/locales/simple-text-element.ru.json": "{\n \"model\": {},\n \"text\": \" ru outermost text at the beginning \",\n \"h1_3\": \"ru outermost header 1\",\n \"text_4\": \" ru outermost text in the middle \",\n \"span_5\": \"ru simple text without id\",\n \"span_6\": \"ru simple text without id 2\",\n \"label-1\": \"ru simple text with id\",\n \"label-2\": \"ru simple text with id 2\",\n \"div_9:span\": \"ru simple text within div\",\n \"div_9:span_1\": \"ru simple text within div 2\",\n \"div_9:div_2:div\": \"ru great grandchild text within div\",\n \"div_10:text\": \" ru simple text as the first element in div \",\n \"div_10:span_1\": \"ru simple text within div\",\n \"div_10:text_2\": \" ru simple text in the middle of div \",\n \"div_10:span_3\": \"ru simple text within div 2\",\n \"div_10:div_4:div\": \"ru great grandchild text within div\",\n \"div_10:text_5\": \" ru simple text at the last element in div \",\n \"toplevel-div:span\": \"ru simple text within div\",\n \"toplevel-div:span_1\": \"ru simple text within div 2\",\n \"third-level-div\": \"ru great grandchild text within div\",\n \"second-level-div:div_1\": \"ru great grandchild text within div without id\",\n \"div_12:ul:li\": \"ru line item without id 1\",\n \"div_12:ul:li_1\": \"ru line item without id 2\",\n \"div_12:ul:li_2\": \"ru line item without id 3\",\n \"line-items:li\": \"ru line item with id 1\",\n \"line-items:li_1\": \"ru line item with id 2\",\n \"line-items:li_2\": \"ru line item with id 3\",\n \"p_13\": [\n \"ru A paragraph with {1} is converted to {2}.\",\n \"ru parameters\",\n \"ru \"\n ],\n \"paragraph\": [\n \"ru A paragraph with {1} is converted to {2}.\",\n \"ru id\",\n \"ru \"\n ],\n \"text_15\": \" ru outermost text at the end \"\n}\n", + "/simple-text-element/simple-text-element.json": "{\n \"meta\": {},\n \"model\": {},\n \"text\": \" outermost text at the beginning \",\n \"h1_3\": \"outermost header 1\",\n \"text_4\": \" outermost text in the middle \",\n \"span_5\": \"simple text without id\",\n \"span_6\": \"simple text without id 2\",\n \"label-1\": \"simple text with id\",\n \"label-2\": \"simple text with id 2\",\n \"div_9:span\": \"simple text within div\",\n \"div_9:span_1\": \"simple text within div 2\",\n \"div_9:div_2:div\": \"great grandchild text within div\",\n \"div_10:text\": \" simple text as the first element in div \",\n \"div_10:span_1\": \"simple text within div\",\n \"div_10:text_2\": \" simple text in the middle of div \",\n \"div_10:span_3\": \"simple text within div 2\",\n \"div_10:div_4:div\": \"great grandchild text within div\",\n \"div_10:text_5\": \" simple text at the last element in div \",\n \"toplevel-div:span\": \"simple text within div\",\n \"toplevel-div:span_1\": \"simple text within div 2\",\n \"third-level-div\": \"great grandchild text within div\",\n \"second-level-div:div_1\": \"great grandchild text within div without id\",\n \"div_12:ul:li\": \"line item without id 1\",\n \"div_12:ul:li_1\": \"line item without id 2\",\n \"div_12:ul:li_2\": \"line item without id 3\",\n \"line-items:li\": \"line item with id 1\",\n \"line-items:li_1\": \"line item with id 2\",\n \"line-items:li_2\": \"line item with id 3\",\n \"p_13\": [\n \"A paragraph with {1} is converted to {2}.\",\n \"parameters\",\n \"\"\n ],\n \"paragraph\": [\n \"A paragraph with {1} is converted to {2}.\",\n \"id\",\n \"\"\n ],\n \"text_15\": \" outermost text at the end \"\n}", + "/simple-text-id-element/locales/simple-text-id-element.fr.json": "{\n \"model\": {},\n \"text\": \" fr outermost text at the beginning \",\n \"h1_3\": \"fr outermost header 1\",\n \"text_4\": \" fr outermost text in the middle \",\n \"span_5\": \"fr simple text without id\",\n \"span_6\": \"fr simple text without id 2\",\n \"label-1\": \"fr simple text with id\",\n \"label-2\": \"fr simple text with id 2\",\n \"div_9:span\": \"fr simple text within div\",\n \"div_9:span_1\": \"fr simple text within div 2\",\n \"div_9:div_2:div\": \"fr great grandchild text within div\",\n \"div_10:text\": \" fr simple text as the first element in div \",\n \"div_10:span_1\": \"fr simple text within div\",\n \"div_10:text_2\": \" fr simple text in the middle of div \",\n \"div_10:span_3\": \"fr simple text within div 2\",\n \"div_10:div_4:div\": \"fr great grandchild text within div\",\n \"div_10:text_5\": \" fr simple text at the last element in div \",\n \"toplevel-div:span\": \"fr simple text within div\",\n \"toplevel-div:span_1\": \"fr simple text within div 2\",\n \"second-level-div\": [\n \" fr {1}\\n {2} \",\n \"fr great grandchild text within div\",\n \"fr great grandchild text within div without id\"\n ],\n \"div_12:ul:li\": \"fr line item without id 1\",\n \"div_12:ul:li_1\": \"fr line item without id 2\",\n \"div_12:ul:li_2\": \"fr line item without id 3\",\n \"line-items\": [\n \" fr {1}\\n {2}\\n {3} \",\n \"fr line item with id 1\",\n \"fr line item with id 2\",\n \"fr line item with id 3\"\n ],\n \"p_13\": [\n \"fr A paragraph with {1} is converted to {2}.\",\n \"fr parameters\",\n \"fr \"\n ],\n \"paragraph\": [\n \"fr A paragraph with {1} is converted to {2}.\",\n \"fr id\",\n \"fr \"\n ],\n \"text_15\": \" fr outermost text at the end \"\n}\n", + "/simple-text-id-element/simple-text-id-element.json": "{\n \"meta\": {},\n \"model\": {},\n \"text\": \" outermost text at the beginning \",\n \"h1_3\": \"outermost header 1\",\n \"text_4\": \" outermost text in the middle \",\n \"span_5\": \"simple text without id\",\n \"span_6\": \"simple text without id 2\",\n \"label-1\": \"simple text with id\",\n \"label-2\": \"simple text with id 2\",\n \"div_9:span\": \"simple text within div\",\n \"div_9:span_1\": \"simple text within div 2\",\n \"div_9:div_2:div\": \"great grandchild text within div\",\n \"div_10:text\": \" simple text as the first element in div \",\n \"div_10:span_1\": \"simple text within div\",\n \"div_10:text_2\": \" simple text in the middle of div \",\n \"div_10:span_3\": \"simple text within div 2\",\n \"div_10:div_4:div\": \"great grandchild text within div\",\n \"div_10:text_5\": \" simple text at the last element in div \",\n \"toplevel-div:span\": \"simple text within div\",\n \"toplevel-div:span_1\": \"simple text within div 2\",\n \"second-level-div\": [\n \" {1}\\n {2} \",\n \"great grandchild text within div\",\n \"great grandchild text within div without id\"\n ],\n \"div_12:ul:li\": \"line item without id 1\",\n \"div_12:ul:li_1\": \"line item without id 2\",\n \"div_12:ul:li_2\": \"line item without id 3\",\n \"line-items\": [\n \" {1}\\n {2}\\n {3} \",\n \"line item with id 1\",\n \"line item with id 2\",\n \"line item with id 3\"\n ],\n \"p_13\": [\n \"A paragraph with {1} is converted to {2}.\",\n \"parameters\",\n \"\"\n ],\n \"paragraph\": [\n \"A paragraph with {1} is converted to {2}.\",\n \"id\",\n \"\"\n ],\n \"text_15\": \" outermost text at the end \"\n}", + "/template-default-lang/locales/null-template-default-lang-element.ja.json": "", + "/template-default-lang/locales/null-template-default-lang-element.zh-Hans-CN.json": "{\n \"meta\": {},\n \"model\": {},\n \"text\": \" zh-Hans-CN outermost text at the beginning \",\n \"h1_3\": \"zh-Hans-CN outermost header 1\",\n \"text_4\": \" zh-Hans-CN outermost text in the middle \",\n \"span_5\": \"zh-Hans-CN simple text without id\",\n \"span_6\": \"zh-Hans-CN simple text without id 2\",\n \"label-1\": \"zh-Hans-CN simple text with id\",\n \"label-2\": \"zh-Hans-CN simple text with id 2\",\n \"div_9:span\": \"zh-Hans-CN simple text within div\",\n \"div_9:span_1\": \"zh-Hans-CN simple text within div 2\",\n \"div_9:div_2:div\": \"zh-Hans-CN great grandchild text within div\",\n \"div_10:text\": \" zh-Hans-CN simple text as the first element in div \",\n \"div_10:span_1\": \"zh-Hans-CN simple text within div\",\n \"div_10:text_2\": \" zh-Hans-CN simple text in the middle of div \",\n \"div_10:span_3\": \"zh-Hans-CN simple text within div 2\",\n \"div_10:div_4:div\": \"zh-Hans-CN great grandchild text within div\",\n \"div_10:text_5\": \" zh-Hans-CN simple text at the last element in div \",\n \"toplevel-div:span\": \"zh-Hans-CN simple text within div\",\n \"toplevel-div:span_1\": \"zh-Hans-CN simple text within div 2\",\n \"third-level-div\": \"zh-Hans-CN great grandchild text within div\",\n \"second-level-div:div_1\": \"zh-Hans-CN great grandchild text within div without id\",\n \"div_12:ul:li\": \"zh-Hans-CN line item without id 1\",\n \"div_12:ul:li_1\": \"zh-Hans-CN line item without id 2\",\n \"div_12:ul:li_2\": \"zh-Hans-CN line item without id 3\",\n \"line-items:li\": \"zh-Hans-CN line item with id 1\",\n \"line-items:li_1\": \"zh-Hans-CN line item with id 2\",\n \"line-items:li_2\": \"zh-Hans-CN line item with id 3\",\n \"p_13\": [\n \"zh-Hans-CN A paragraph with {1} is converted to {2}.\",\n \"zh-Hans-CN parameters\",\n \"zh-Hans-CN \"\n ],\n \"paragraph\": [\n \"zh-Hans-CN A paragraph with {1} is converted to {2}.\",\n \"zh-Hans-CN id\",\n \"zh-Hans-CN \"\n ],\n \"text_15\": \" zh-Hans-CN outermost text at the end \"\n}", + "/template-default-lang/locales/template-default-lang-element.zh-Hans-CN.json": "{\n \"meta\": {},\n \"model\": {},\n \"text\": \" zh-Hans-CN outermost text at the beginning \",\n \"h1_3\": \"zh-Hans-CN outermost header 1\",\n \"text_4\": \" zh-Hans-CN outermost text in the middle \",\n \"span_5\": \"zh-Hans-CN simple text without id\",\n \"span_6\": \"zh-Hans-CN simple text without id 2\",\n \"label-1\": \"zh-Hans-CN simple text with id\",\n \"label-2\": \"zh-Hans-CN simple text with id 2\",\n \"div_9:span\": \"zh-Hans-CN simple text within div\",\n \"div_9:span_1\": \"zh-Hans-CN simple text within div 2\",\n \"div_9:div_2:div\": \"zh-Hans-CN great grandchild text within div\",\n \"div_10:text\": \" zh-Hans-CN simple text as the first element in div \",\n \"div_10:span_1\": \"zh-Hans-CN simple text within div\",\n \"div_10:text_2\": \" zh-Hans-CN simple text in the middle of div \",\n \"div_10:span_3\": \"zh-Hans-CN simple text within div 2\",\n \"div_10:div_4:div\": \"zh-Hans-CN great grandchild text within div\",\n \"div_10:text_5\": \" zh-Hans-CN simple text at the last element in div \",\n \"toplevel-div:span\": \"zh-Hans-CN simple text within div\",\n \"toplevel-div:span_1\": \"zh-Hans-CN simple text within div 2\",\n \"third-level-div\": \"zh-Hans-CN great grandchild text within div\",\n \"second-level-div:div_1\": \"zh-Hans-CN great grandchild text within div without id\",\n \"div_12:ul:li\": \"zh-Hans-CN line item without id 1\",\n \"div_12:ul:li_1\": \"zh-Hans-CN line item without id 2\",\n \"div_12:ul:li_2\": \"zh-Hans-CN line item without id 3\",\n \"line-items:li\": \"zh-Hans-CN line item with id 1\",\n \"line-items:li_1\": \"zh-Hans-CN line item with id 2\",\n \"line-items:li_2\": \"zh-Hans-CN line item with id 3\",\n \"p_13\": [\n \"zh-Hans-CN A paragraph with {1} is converted to {2}.\",\n \"zh-Hans-CN parameters\",\n \"zh-Hans-CN \"\n ],\n \"paragraph\": [\n \"zh-Hans-CN A paragraph with {1} is converted to {2}.\",\n \"zh-Hans-CN id\",\n \"zh-Hans-CN \"\n ],\n \"text_15\": \" zh-Hans-CN outermost text at the end \"\n}", + "/template-default-lang/null-template-default-lang-element.json": "{\n \"meta\": {},\n \"model\": {},\n \"text\": \" outermost text at the beginning \",\n \"h1_3\": \"outermost header 1\",\n \"text_4\": \" outermost text in the middle \",\n \"span_5\": \"simple text without id\",\n \"span_6\": \"simple text without id 2\",\n \"label-1\": \"simple text with id\",\n \"label-2\": \"simple text with id 2\",\n \"div_9:span\": \"simple text within div\",\n \"div_9:span_1\": \"simple text within div 2\",\n \"div_9:div_2:div\": \"great grandchild text within div\",\n \"div_10:text\": \" simple text as the first element in div \",\n \"div_10:span_1\": \"simple text within div\",\n \"div_10:text_2\": \" simple text in the middle of div \",\n \"div_10:span_3\": \"simple text within div 2\",\n \"div_10:div_4:div\": \"great grandchild text within div\",\n \"div_10:text_5\": \" simple text at the last element in div \",\n \"toplevel-div:span\": \"simple text within div\",\n \"toplevel-div:span_1\": \"simple text within div 2\",\n \"third-level-div\": \"great grandchild text within div\",\n \"second-level-div:div_1\": \"great grandchild text within div without id\",\n \"div_12:ul:li\": \"line item without id 1\",\n \"div_12:ul:li_1\": \"line item without id 2\",\n \"div_12:ul:li_2\": \"line item without id 3\",\n \"line-items:li\": \"line item with id 1\",\n \"line-items:li_1\": \"line item with id 2\",\n \"line-items:li_2\": \"line item with id 3\",\n \"p_13\": [\n \"A paragraph with {1} is converted to {2}.\",\n \"parameters\",\n \"\"\n ],\n \"paragraph\": [\n \"A paragraph with {1} is converted to {2}.\",\n \"id\",\n \"\"\n ],\n \"text_15\": \" outermost text at the end \"\n}", + "/template-default-lang/template-default-lang-element.json": "{\n \"meta\": {},\n \"model\": {},\n \"text\": \" fr outermost text at the beginning \",\n \"h1_3\": \"fr outermost header 1\",\n \"text_4\": \" fr outermost text in the middle \",\n \"span_5\": \"fr simple text without id\",\n \"span_6\": \"fr simple text without id 2\",\n \"label-1\": \"fr simple text with id\",\n \"label-2\": \"fr simple text with id 2\",\n \"div_9:span\": \"fr simple text within div\",\n \"div_9:span_1\": \"fr simple text within div 2\",\n \"div_9:div_2:div\": \"fr great grandchild text within div\",\n \"div_10:text\": \" fr simple text as the first element in div \",\n \"div_10:span_1\": \"fr simple text within div\",\n \"div_10:text_2\": \" fr simple text in the middle of div \",\n \"div_10:span_3\": \"fr simple text within div 2\",\n \"div_10:div_4:div\": \"fr great grandchild text within div\",\n \"div_10:text_5\": \" fr simple text at the last element in div \",\n \"toplevel-div:span\": \"fr simple text within div\",\n \"toplevel-div:span_1\": \"fr simple text within div 2\",\n \"third-level-div\": \"fr great grandchild text within div\",\n \"second-level-div:div_1\": \"fr great grandchild text within div without id\",\n \"div_12:ul:li\": \"fr line item without id 1\",\n \"div_12:ul:li_1\": \"fr line item without id 2\",\n \"div_12:ul:li_2\": \"fr line item without id 3\",\n \"line-items:li\": \"fr line item with id 1\",\n \"line-items:li_1\": \"fr line item with id 2\",\n \"line-items:li_2\": \"fr line item with id 3\",\n \"p_13\": [\n \"fr A paragraph with {1} is converted to {2}.\",\n \"fr parameters\",\n \"fr \"\n ],\n \"paragraph\": [\n \"fr A paragraph with {1} is converted to {2}.\",\n \"fr id\",\n \"fr \"\n ],\n \"text_15\": \" fr outermost text at the end \"\n}" +}; diff --git a/test/src3/fallback-text-element/fallback-text-element.js b/test/src3/fallback-text-element/fallback-text-element.js new file mode 100644 index 00000000..95d9b50d --- /dev/null +++ b/test/src3/fallback-text-element/fallback-text-element.js @@ -0,0 +1,314 @@ +/** +@license https://github.com/t2ym/i18n-behavior/blob/master/LICENSE.md +Copyright (c) 2016, Tetsuya Mori . All rights reserved. +*/ +import 'i18n-behavior/i18n-behavior.js'; + +import { html } from '@polymer/polymer/lib/utils/html-tag.js'; +import { Polymer } from '@polymer/polymer/lib/legacy/polymer-fn.js'; +import { LegacyElementMixin } from '@polymer/polymer/lib/legacy/legacy-element-mixin.js'; +import { dom } from '@polymer/polymer/lib/legacy/polymer.dom.js'; +import deepcopy from 'deepcopy/dist/deepcopy.js'; +const $_documentContainer = document.createElement('template'); + +$_documentContainer.innerHTML = ``; + +document.head.appendChild($_documentContainer.content); +switch (syntax) { +default: +case 'mixin': + { + class FallbackTextElement extends Mixins.Localizable(LegacyElementMixin(HTMLElement)) { + static get importMeta() { + return import.meta; + } + + static get template() { + return html` + outermost text at the beginning +
+   +

outermost header 1

+ outermost text in the middle + simple text without id + simple text without id 2 + simple text with id + simple text with id 2 +
+ simple text within div + simple text within div 2 +
great grandchild text within div
+
+
+ simple text as the first element in div + simple text within div + simple text in the middle of div + simple text within div 2 +
great grandchild text within div
+ simple text at the last element in div +
+
+ simple text within div + simple text within div 2 +
+
great grandchild text within div
+
great grandchild text within div without id
+
+
+
+
    +
  • line item without id 1
  • +
  • line item without id 2
  • +
  • line item without id 3
  • +
+
    +
  • line item with id 1
  • +
  • line item with id 2
  • +
  • line item with id 3
  • +
+
+

A paragraph with parameters is converted to <i18n-format>.

+

A paragraph with id is converted to <i18n-format>.

+ outermost text at the end +`; + } + + static get is() { return 'fallback-text-element' } + + ready() { + this.addEventListener('lang-updated', this._langUpdated); + super.ready(); + } + + _langUpdated (e) { + console.log(this.is, 'lang-updated', e.detail); + if (e.composedPath()[0] === this) { + this.model = deepcopy(this.text.model); + } + } + } + customElements.define(FallbackTextElement.is, FallbackTextElement); + } + break; +case 'base-element': + { + class FallbackTextElement extends BaseElements.I18nElement { + static get importMeta() { + return import.meta; + } + + static get template() { + return html` + outermost text at the beginning +
+   +

outermost header 1

+ outermost text in the middle + simple text without id + simple text without id 2 + simple text with id + simple text with id 2 +
+ simple text within div + simple text within div 2 +
great grandchild text within div
+
+
+ simple text as the first element in div + simple text within div + simple text in the middle of div + simple text within div 2 +
great grandchild text within div
+ simple text at the last element in div +
+
+ simple text within div + simple text within div 2 +
+
great grandchild text within div
+
great grandchild text within div without id
+
+
+
+
    +
  • line item without id 1
  • +
  • line item without id 2
  • +
  • line item without id 3
  • +
+
    +
  • line item with id 1
  • +
  • line item with id 2
  • +
  • line item with id 3
  • +
+
+

A paragraph with parameters is converted to <i18n-format>.

+

A paragraph with id is converted to <i18n-format>.

+ outermost text at the end +`; + } + + static get is() { return 'fallback-text-element' } + + ready() { + this.addEventListener('lang-updated', this._langUpdated); + super.ready(); + } + + _langUpdated (e) { + console.log(this.is, 'lang-updated', e.detail); + if (e.composedPath()[0] === this) { + this.model = deepcopy(this.text.model); + } + } + } + customElements.define(FallbackTextElement.is, FallbackTextElement); + } + break; +case 'thin': + { + Define = class FallbackTextElement extends BaseElements.I18nElement { + + static get importMeta() { + return import.meta; + } + + ready() { + this.addEventListener('lang-updated', this._langUpdated); + super.ready(); + } + + _langUpdated (e) { + console.log(this.is, 'lang-updated', e.detail); + if (e.composedPath()[0] === this) { + this.model = deepcopy(this.text.model); + } + } + }; + } + break; +case 'legacy': + { + Polymer({ + importMeta: import.meta, + + _template: html` + outermost text at the beginning +
+   +

outermost header 1

+ outermost text in the middle + simple text without id + simple text without id 2 + simple text with id + simple text with id 2 +
+ simple text within div + simple text within div 2 +
great grandchild text within div
+
+
+ simple text as the first element in div + simple text within div + simple text in the middle of div + simple text within div 2 +
great grandchild text within div
+ simple text at the last element in div +
+
+ simple text within div + simple text within div 2 +
+
great grandchild text within div
+
great grandchild text within div without id
+
+
+
+
    +
  • line item without id 1
  • +
  • line item without id 2
  • +
  • line item without id 3
  • +
+
    +
  • line item with id 1
  • +
  • line item with id 2
  • +
  • line item with id 3
  • +
+
+

A paragraph with parameters is converted to <i18n-format>.

+

A paragraph with id is converted to <i18n-format>.

+ outermost text at the end +`, + + is: 'fallback-text-element', + + behaviors: [ + BehaviorsStore.I18nBehavior + ], + + listeners: { + 'lang-updated': '_langUpdated' + }, + + ready: function () { + //this.observeHtmlLang = false; + }, + + attached: function () { + }, + + _langUpdated: function (e) { + console.log(this.is, 'lang-updated', e.detail); + if (dom(e).rootTarget === this) { + this.model = deepcopy(this.text.model); + } + } + }); + } + break; +} diff --git a/test/src3/fallback-text-element/fallback-text-element.json b/test/src3/fallback-text-element/fallback-text-element.json new file mode 100644 index 00000000..f9b2ccf7 --- /dev/null +++ b/test/src3/fallback-text-element/fallback-text-element.json @@ -0,0 +1,41 @@ +{ + "meta": {}, + "model": {}, + "text": " outermost text at the beginning ", + "h1_3": "outermost header 1", + "text_4": " outermost text in the middle ", + "span_5": "simple text without id", + "span_6": "simple text without id 2", + "label-1": "simple text with id", + "label-2": "simple text with id 2", + "div_9:span": "simple text within div", + "div_9:span_1": "simple text within div 2", + "div_9:div_2:div": "great grandchild text within div", + "div_10:text": " simple text as the first element in div ", + "div_10:span_1": "simple text within div", + "div_10:text_2": " simple text in the middle of div ", + "div_10:span_3": "simple text within div 2", + "div_10:div_4:div": "great grandchild text within div", + "div_10:text_5": " simple text at the last element in div ", + "toplevel-div:span": "simple text within div", + "toplevel-div:span_1": "simple text within div 2", + "third-level-div": "great grandchild text within div", + "second-level-div:div_1": "great grandchild text within div without id", + "div_12:ul:li": "line item without id 1", + "div_12:ul:li_1": "line item without id 2", + "div_12:ul:li_2": "line item without id 3", + "line-items:li": "line item with id 1", + "line-items:li_1": "line item with id 2", + "line-items:li_2": "line item with id 3", + "p_13": [ + "A paragraph with {1} is converted to {2}.", + "parameters", + "" + ], + "paragraph": [ + "A paragraph with {1} is converted to {2}.", + "id", + "" + ], + "text_15": " outermost text at the end " +} \ No newline at end of file diff --git a/test/src3/fallback-text-element/locales/fallback-text-element.fr-CA.json b/test/src3/fallback-text-element/locales/fallback-text-element.fr-CA.json new file mode 100755 index 00000000..708de1a0 --- /dev/null +++ b/test/src3/fallback-text-element/locales/fallback-text-element.fr-CA.json @@ -0,0 +1,26 @@ +{ + "model": {}, + "text": "fr-CA outermost text at the beginning ", + "h1_3": "fr-CA outermost header 1", + "text_4": "fr-CA outermost text in the middle ", + "span_5": "fr-CA simple text without id", + "span_6": "fr-CA simple text without id 2", + "label-1": "fr-CA simple text with id", + "label-2": "fr-CA simple text with id 2", + "div_10:span_1": "fr-CA simple text within div", + "toplevel-div:span": "fr-CA simple text within div", + "toplevel-div:span_1": "fr-CA simple text within div 2", + "third-level-div": "fr-CA great grandchild text within div", + "second-level-div:div_1": "fr-CA great grandchild text within div without id", + "p_13": [ + "fr-CA A paragraph with {1} is converted to {2}.", + "fr-CA parameters", + "fr-CA " + ], + "paragraph": [ + "fr-CA A paragraph with {1} is converted to {2}.", + "fr-CA id", + "fr-CA " + ], + "text_15": "fr-CA outermost text at the end " +} diff --git a/test/src3/fallback-text-element/locales/fallback-text-element.fr.json b/test/src3/fallback-text-element/locales/fallback-text-element.fr.json new file mode 100755 index 00000000..ca43eeb3 --- /dev/null +++ b/test/src3/fallback-text-element/locales/fallback-text-element.fr.json @@ -0,0 +1,31 @@ +{ + "model": {}, + "text": "fr outermost text at the beginning ", + "h1_3": "fr outermost header 1", + "text_4": "fr outermost text in the middle ", + "span_5": "fr simple text without id", + "span_6": "fr simple text without id 2", + "label-1": "fr simple text with id", + "label-2": "fr simple text with id 2", + "toplevel-div:span": "fr simple text within div", + "toplevel-div:span_1": "fr simple text within div 2", + "third-level-div": "fr great grandchild text within div", + "second-level-div:div_1": "fr great grandchild text within div without id", + "div_12:ul:li": "fr line item without id 1", + "div_12:ul:li_1": "fr line item without id 2", + "div_12:ul:li_2": "fr line item without id 3", + "line-items:li": "fr line item with id 1", + "line-items:li_1": "fr line item with id 2", + "line-items:li_2": "fr line item with id 3", + "p_13": [ + "fr A paragraph with {1} is converted to {2}.", + "fr parameters", + "fr " + ], + "paragraph": [ + "fr A paragraph with {1} is converted to {2}.", + "fr id", + "fr " + ], + "text_15": "fr outermost text at the end " +} diff --git a/test/src3/locales/compound-binding-dom-bind.fr.json b/test/src3/locales/compound-binding-dom-bind.fr.json new file mode 100755 index 00000000..7fdc88d3 --- /dev/null +++ b/test/src3/locales/compound-binding-dom-bind.fr.json @@ -0,0 +1,150 @@ +{ + "model": {}, + "text": [ + " fr outermost text at the beginning with compound {1} and {2} variables ", + "{{param1}}", + "{{param2}}" + ], + "h1_3": [ + "fr outermost header 1 with {1} and {2} variables", + "{{param1}}", + "{{param2}}" + ], + "text_4": [ + " fr outermost text in the middle with {1} and {2} variables ", + "{{param1}}", + "{{param2}}" + ], + "span_5": [ + "fr simple text without id with {1} and {2} variables", + "{{param1}}", + "{{param2}}" + ], + "span_6": [ + "fr simple text without id 2 with {1} and {2} variables", + "{{param1}}", + "{{param2}}" + ], + "label-1": [ + "fr simple text with id and {1} and {2} variables", + "{{param1}}", + "{{param2}}" + ], + "label-2": [ + "fr simple text with id and {1} and {2} variables 2", + "{{param1}}", + "{{param2}}" + ], + "div_9:span": [ + "fr simple text within div with {1} and {2} variables", + "{{param1}}", + "{{param2}}" + ], + "div_9:span_1": [ + "fr simple text within div with {1} and {2} variables 2", + "{{param1}}", + "{{param2}}" + ], + "div_9:div_2:div": [ + "fr great grandchild text within div with {1} and {2} variables", + "{{param1}}", + "{{param2}}" + ], + "div_10:text": [ + " fr simple text as the first element in div with {1} and {2} variables ", + "{{param1}}", + "{{param2}}" + ], + "div_10:span_1": [ + "fr simple text within div with {1} and {2} variables", + "{{param1}}", + "{{param2}}" + ], + "div_10:text_2": [ + " fr simple text in the middle of div with {1} and {2} variables ", + "{{param1}}", + "{{param2}}" + ], + "div_10:span_3": [ + "fr simple text within div with {1} and {2} variables 2", + "{{param1}}", + "{{param2}}" + ], + "div_10:div_4:div": [ + "fr great grandchild text within div with {1} and {2} variables", + "{{param1}}", + "{{param2}}" + ], + "div_10:text_5": [ + " fr simple text at the last element in div with {1} and {2} variables ", + "{{param1}}", + "{{param2}}" + ], + "toplevel-div:span": [ + "fr simple text within div with {1} and {2} variables", + "{{param1}}", + "{{param2}}" + ], + "toplevel-div:span_1": [ + "fr simple text within div 2 with {1} and {2} variables", + "{{param1}}", + "{{param2}}" + ], + "third-level-div": [ + "fr great grandchild text within div with {1} and {2} variables", + "{{param1}}", + "{{param2}}" + ], + "second-level-div:div_1": [ + "fr great grandchild text within div without id with {1} and {2} variables", + "{{param1}}", + "{{param2}}" + ], + "div_12:ul:li": [ + "fr line item without id 1 with {1} and {2} variables", + "{{param1}}", + "{{param2}}" + ], + "div_12:ul:li_1": [ + "fr line item without id 2 with {1} and {2} variables", + "{{param1}}", + "{{param2}}" + ], + "div_12:ul:li_2": [ + "fr line item without id 3 with {1} and {2} variables", + "{{param1}}", + "{{param2}}" + ], + "line-items:li": [ + "fr line item with id 1 with {1} and {2} variables", + "{{param1}}", + "{{param2}}" + ], + "line-items:li_1": [ + "fr line item with id 2 with {1} and {2} variables", + "{{param1}}", + "{{param2}}" + ], + "line-items:li_2": [ + "fr line item with id 3 with {1} and {2} variables", + "{{param1}}", + "{{param2}}" + ], + "p_13": [ + "fr A paragraph with {1} is converted to {2}.", + "{{param1}}", + "{{param2}}" + ], + "paragraph": [ + "fr A paragraph with {1}, {2}, and {3} is converted to {4}.", + "fr id", + "{{param1}}", + "{{param2}}", + "fr " + ], + "text_15": [ + " fr outermost text at the end with {1} and {2} variables ", + "{{param1}}", + "{{param2}}" + ] +} diff --git a/test/src3/locales/simple-attribute-dom-bind.fr.json b/test/src3/locales/simple-attribute-dom-bind.fr.json new file mode 100755 index 00000000..98b3b098 --- /dev/null +++ b/test/src3/locales/simple-attribute-dom-bind.fr.json @@ -0,0 +1,143 @@ +{ + "model": { + "standard-input": { + "placeholder": "fr standard HTML5 attribute" + }, + "outer-div:input_2": { + "placeholder": "fr standard HTML5 attribute without id" + }, + "paper-input-element": { + "label": "fr paper-input label", + "error-message": "fr paper-input error message", + "placeholder": "fr paper-input placeholder" + }, + "outer-div:paper-input_4": { + "label": "fr paper-input label without id", + "error-message": "fr paper-input error message without id", + "placeholder": "fr paper-input placeholder without id" + }, + "pie-chart": { + "options": { + "title": "fr Distribution of days in 2001H1" + }, + "cols": [ + { + "label": "fr Month", + "type": "string" + }, + { + "label": "fr Days", + "type": "number" + } + ], + "rows": [ + [ + "fr Jan", + 31 + ], + [ + "fr Feb", + 28 + ], + [ + "fr Mar", + 31 + ], + [ + "fr Apr", + 30 + ], + [ + "fr May", + 31 + ], + [ + "fr Jun", + 30 + ] + ] + }, + "column-chart": { + "options": { + "title": "fr Inventory" + }, + "data": [ + [ + "fr Year", + "fr Things", + "fr Stuff" + ], + [ + "2004", + 1000, + 400 + ], + [ + "2005", + 1170, + 460 + ], + [ + "2006", + 660, + 1120 + ], + [ + "2007", + 1030, + 540 + ] + ] + }, + "custom-attr": { + "custom-text-attr1": "fr custom text attribute 1", + "custom-text-attr2": "fr custom text attribute 2", + "custom-text-attr3": "fr custom text attribute 3" + }, + "selective-attr": { + "custom-text-attr4": [ + "fr {1} custom-text-attr4 attribute with param {2} and param {3} {4}", + "{{text.ordinary-div}}", + "{{text.ordinary-div}}", + "[[text.ordinary-div]]", + "{{text.ordinary-div}}" + ], + "custom-text-attr5": [ + "[[text.ordinary-div]]", + " fr custom-text-attr5 attribute with param ", + "{{or('',text.ordinary-div)}}", + " fr and param ", + "[[text.ordinary-div]]" + ], + "i18n-target": [ + "fr i18n-target attribute with param {1} and param {2}", + "{{text.ordinary-div}}", + "[[text.ordinary-div]]" + ], + "i18n-target2": [ + "fr i18n-target2 attribute with param ", + "{{or('',text.ordinary-div)}}", + " fr and param ", + "[[text.ordinary-div]]" + ] + }, + "selective-attr2": { + "i18n-target": "fr i18n-target attribute 2" + }, + "selective-attr3": { + "i18n-target6": "fr i18n-target6 attribute 2" + }, + "selective-attr4": { + "i18n-target6": "fr i18n-target6 attribute 3" + }, + "json-data-id": { + "attr1": "fr this attr1 is extracted", + "i18n-target-attr": "fr this attribute is also extracted" + }, + "template_2:json-data_1": { + "attr1": "fr this attr1 without id is extracted", + "i18n-target-attr": "fr this attribute without id is also extracted" + } + }, + "ordinary-div": "fr text 1" +} diff --git a/test/src3/locales/simple-text-dom-bind.fr.json b/test/src3/locales/simple-text-dom-bind.fr.json new file mode 100755 index 00000000..3b5a034f --- /dev/null +++ b/test/src3/locales/simple-text-dom-bind.fr.json @@ -0,0 +1,40 @@ +{ + "model": {}, + "text": " fr outermost text at the beginning ", + "h1_3": "fr outermost header 1", + "text_4": " fr outermost text in the middle ", + "span_5": "fr simple text without id", + "span_6": "fr simple text without id 2", + "label-1": "fr simple text with id", + "label-2": "fr simple text with id 2", + "div_9:span": "fr simple text within div", + "div_9:span_1": "fr simple text within div 2", + "div_9:div_2:div": "fr great grandchild text within div", + "div_10:text": " fr simple text as the first element in div ", + "div_10:span_1": "fr simple text within div", + "div_10:text_2": " fr simple text in the middle of div ", + "div_10:span_3": "fr simple text within div 2", + "div_10:div_4:div": "fr great grandchild text within div", + "div_10:text_5": " fr simple text at the last element in div ", + "toplevel-div:span": "fr simple text within div", + "toplevel-div:span_1": "fr simple text within div 2", + "third-level-div": "fr great grandchild text within div", + "second-level-div:div_1": "fr great grandchild text within div without id", + "div_12:ul:li": "fr line item without id 1", + "div_12:ul:li_1": "fr line item without id 2", + "div_12:ul:li_2": "fr line item without id 3", + "line-items:li": "fr line item with id 1", + "line-items:li_1": "fr line item with id 2", + "line-items:li_2": "fr line item with id 3", + "p_13": [ + "fr A paragraph with {1} is converted to {2}.", + "fr parameters", + "fr " + ], + "paragraph": [ + "fr A paragraph with {1} is converted to {2}.", + "fr id", + "fr " + ], + "text_15": " fr outermost text at the end " +} diff --git a/test/src3/multiple-case-test-imports.js b/test/src3/multiple-case-test-imports.js new file mode 100644 index 00000000..859606cf --- /dev/null +++ b/test/src3/multiple-case-test-imports.js @@ -0,0 +1,22 @@ +import './fake-server.js'; +import './test-runner.js'; +import '../../i18n-element.js'; +import '../../define-element.js'; +import '../../i18n-dom-bind.js'; +import './multiple-case/multiple-element.js'; +import './multiple-case-test-suites.js'; + +/** +@license https://github.com/t2ym/i18n-behavior/blob/master/LICENSE.md +Copyright (c) 2016, Tetsuya Mori . All rights reserved. +*/ +/* + + +*/ +/* + FIXME(polymer-modulizer): the above comments were extracted + from HTML and may be out of place here. Review them and + then delete this comment! +*/ +; diff --git a/test/src3/multiple-case-test-suites.js b/test/src3/multiple-case-test-suites.js new file mode 100644 index 00000000..0fdb8034 --- /dev/null +++ b/test/src3/multiple-case-test-suites.js @@ -0,0 +1,47 @@ +/** +@license https://github.com/t2ym/i18n-behavior/blob/master/LICENSE.md +Copyright (c) 2016, Tetsuya Mori . All rights reserved. +*/ +import './test-runner.js'; +suite('I18nElement with ' + + (window.location.href.indexOf('?dom=Shadow') >= 0 ? 'Shadow DOM' : 'Shady DOM') + + (' in ' + syntax + ' syntax'), + function () { + + var lang0 = ''; + var lang1 = 'en'; + var lang2 = 'fr'; + var lang3 = 'ja'; + var lang4 = 'fr-CA'; + var lang5 = 'zh-Hans-CN'; + var count1 = 100; + + var localDOM_multiple_element = [ + { select: 'div item-element', '$.label.textContent': 'A' } + ]; + + var suites = [ + s('multiple element', null, { + fixture: 'multiple-element-fixture', + fixtureModel: { observeHtmlLang: false, lang: lang0, count: count1 }, + assign: { lang: lang1 }, + lang: lang1, + effectiveLang: lang1, + templateDefaultLang: lang1, + observeHtmlLang: false, + event: 'local-dom-ready', + text: { model: {} }, + model: {}, + localDOM: localDOM_multiple_element, + lightDOM: undefined + }), + s(lang2 + ' multiple element', 'multiple element', { + assign: { lang: lang2 }, + lang: lang2, + effectiveLang: lang2 + }) + ]; + + suitesRunner(suites, 3000); + +}); diff --git a/test/src3/multiple-case-test.html b/test/src3/multiple-case-test.html new file mode 100755 index 00000000..40b44b49 --- /dev/null +++ b/test/src3/multiple-case-test.html @@ -0,0 +1,35 @@ + + + + + + + + + + + + + +

+ + + + + + diff --git a/test/src3/multiple-case/item-element.js b/test/src3/multiple-case/item-element.js new file mode 100644 index 00000000..36ee2ecc --- /dev/null +++ b/test/src3/multiple-case/item-element.js @@ -0,0 +1,83 @@ +/** +@license https://github.com/t2ym/i18n-behavior/blob/master/LICENSE.md +Copyright (c) 2016, Tetsuya Mori . All rights reserved. +*/ +import 'i18n-behavior/i18n-behavior.js'; + +import { html } from '@polymer/polymer/lib/utils/html-tag.js'; +import { Polymer } from '@polymer/polymer/lib/legacy/polymer-fn.js'; +import { LegacyElementMixin } from '@polymer/polymer/lib/legacy/legacy-element-mixin.js'; +const $_documentContainer = document.createElement('template'); + +$_documentContainer.innerHTML = ``; + +document.head.appendChild($_documentContainer.content); +switch (syntax) { +default: +case 'mixin': + { + class ItemElement extends Mixins.Localizable(LegacyElementMixin(HTMLElement)) { + static get importMeta() { + return import.meta; + } + + static get template() { + return html` + A +`; + } + + static get is() { return 'item-element' } + } + customElements.define(ItemElement.is, ItemElement); + } + break; +case 'base-element': + { + class ItemElement extends BaseElements.I18nElement { + static get importMeta() { + return import.meta; + } + + static get template() { + return html` + A +`; + } + + static get is() { return 'item-element' } + } + customElements.define(ItemElement.is, ItemElement); + } + break; +case 'thin': + { + Define = class ItemElement extends BaseElements.I18nElement { + + static get importMeta() { + return import.meta; + } + + } + } + break; +case 'legacy': + { + Polymer({ + importMeta: import.meta, + + _template: html` + A +`, + + is: 'item-element', + + behaviors: [ + BehaviorsStore.I18nBehavior + ] + }); + } + break; +} diff --git a/test/src3/multiple-case/item-element.json b/test/src3/multiple-case/item-element.json new file mode 100644 index 00000000..ccab72af --- /dev/null +++ b/test/src3/multiple-case/item-element.json @@ -0,0 +1,5 @@ +{ + "meta": {}, + "model": {}, + "label": "A" +} \ No newline at end of file diff --git a/test/src3/multiple-case/locales/item-element.fr.json b/test/src3/multiple-case/locales/item-element.fr.json new file mode 100755 index 00000000..279165f5 --- /dev/null +++ b/test/src3/multiple-case/locales/item-element.fr.json @@ -0,0 +1,5 @@ +{ + "meta": {}, + "model": {}, + "label": "fr A" +} \ No newline at end of file diff --git a/test/src3/multiple-case/locales/multiple-element.fr.json b/test/src3/multiple-case/locales/multiple-element.fr.json new file mode 100644 index 00000000..ce3f5d10 --- /dev/null +++ b/test/src3/multiple-case/locales/multiple-element.fr.json @@ -0,0 +1,4 @@ +{ + "meta": {}, + "model": {} +} \ No newline at end of file diff --git a/test/src3/multiple-case/multiple-element.js b/test/src3/multiple-case/multiple-element.js new file mode 100644 index 00000000..6bc5c08f --- /dev/null +++ b/test/src3/multiple-case/multiple-element.js @@ -0,0 +1,341 @@ +/** +@license https://github.com/t2ym/i18n-behavior/blob/master/LICENSE.md +Copyright (c) 2016, Tetsuya Mori . All rights reserved. +*/ +import 'i18n-behavior/i18n-behavior.js'; + +import './item-element.js'; +import { html } from '@polymer/polymer/lib/utils/html-tag.js'; +import { dom } from '@polymer/polymer/lib/legacy/polymer.dom.js'; +import { Polymer } from '@polymer/polymer/lib/legacy/polymer-fn.js'; +import { LegacyElementMixin } from '@polymer/polymer/lib/legacy/legacy-element-mixin.js'; +const $_documentContainer = document.createElement('template'); + +$_documentContainer.innerHTML = ``; + +document.head.appendChild($_documentContainer.content); +switch (syntax) { +default: +case 'mixin': + { + class MultipleElement extends Mixins.Localizable(LegacyElementMixin(HTMLElement)) { + static get importMeta() { + return import.meta; + } + + static get template() { + return html` +
+ +
+
+`; + } + + static get is() { return 'multiple-element' } + + static get properties () { + return { + count: { + type: Number, + value: 100 + } + } + } + + ready() { + this.addEventListener('lang-updated', this.langUpdated); + super.ready(); + } + + getArray(count) { + var a = []; + for (var i = 0; i < count; i++) { + a.push(i); + } + return a; + } + + domChanged(e) { + var nodes = dom(this.root).querySelectorAll('item-element'); + if (this.lang === 'en' && this.effectiveLang === '') { + this.effectiveLang = 'en'; + } + //console.log('multiple-element: dom-change count = ' + nodes.length + ' lang = ' + this.lang + ' effectiveLang = ' + this.effectiveLang); + if (nodes.length === this.count && + (this.lang === this.effectiveLang || + (this.effectiveLang === '' && this.lang ==='en'))) { + Array.prototype.forEach.call(nodes, function (node) { + this.$.save.appendChild(node); + }.bind(this)); + //console.log('multiple-element: local-dom-ready'); + this.async(function () { this.fire('local-dom-ready'); }, 500); + } + } + + langUpdated(e) { + var target = e.composedPath()[0]; + var lang = target.lang === '' ? 'en' : target.lang; + this.itemLang = this.itemLang || {}; + if (target.tagName.toLowerCase() === 'item-element') { + //console.log('item-element: lang-updated lang = ' + target.lang); + this.itemLang[lang] = this.itemLang[lang] || 0; + this.itemLang[lang]++; + } + //console.log('multiple-element: ' + target.is + ' ' + + // 'lang-updated lang = ' + this.lang + ' effectiveLang = ' + this.effectiveLang + + // ' itemLang[' + this.lang + '] = ' + this.itemLang[this.lang]); + if (this.itemLang[this.lang] === this.count) { + //console.log('count reached for ' + this.lang); + this.$.items.render(); + } + return false; + } + } + customElements.define(MultipleElement.is, MultipleElement); + } + break; +case 'base-element': + { + class MultipleElement extends BaseElements.I18nElement { + static get importMeta() { + return import.meta; + } + + static get template() { + return html` +
+ +
+
+`; + } + + static get is() { return 'multiple-element' } + + static get properties () { + return { + count: { + type: Number, + value: 100 + } + } + } + + ready() { + this.addEventListener('lang-updated', this.langUpdated); + super.ready(); + } + + getArray(count) { + var a = []; + for (var i = 0; i < count; i++) { + a.push(i); + } + return a; + } + + domChanged(e) { + var nodes = dom(this.root).querySelectorAll('item-element'); + if (this.lang === 'en' && this.effectiveLang === '') { + this.effectiveLang = 'en'; + } + //console.log('multiple-element: dom-change count = ' + nodes.length + ' lang = ' + this.lang + ' effectiveLang = ' + this.effectiveLang); + if (nodes.length === this.count && + (this.lang === this.effectiveLang || + (this.effectiveLang === '' && this.lang ==='en'))) { + Array.prototype.forEach.call(nodes, function (node) { + this.$.save.appendChild(node); + }.bind(this)); + //console.log('multiple-element: local-dom-ready'); + this.async(function () { this.fire('local-dom-ready'); }, 500); + } + } + + langUpdated(e) { + var target = e.composedPath()[0]; + var lang = target.lang === '' ? 'en' : target.lang; + this.itemLang = this.itemLang || {}; + if (target.tagName.toLowerCase() === 'item-element') { + //console.log('item-element: lang-updated lang = ' + target.lang); + this.itemLang[lang] = this.itemLang[lang] || 0; + this.itemLang[lang]++; + } + //console.log('multiple-element: ' + target.is + ' ' + + // 'lang-updated lang = ' + this.lang + ' effectiveLang = ' + this.effectiveLang + + // ' itemLang[' + this.lang + '] = ' + this.itemLang[this.lang]); + if (this.itemLang[this.lang] === this.count) { + //console.log('count reached for ' + this.lang); + this.$.items.render(); + } + return false; + } + } + customElements.define(MultipleElement.is, MultipleElement); + } + break; +case 'thin': + { + Define = class MultipleElement extends BaseElements.I18nElement { + + static get importMeta() { + return import.meta; + } + + static get properties () { + return { + count: { + type: Number, + value: 100 + } + } + } + + ready() { + this.addEventListener('lang-updated', this.langUpdated); + super.ready(); + } + + getArray(count) { + var a = []; + for (var i = 0; i < count; i++) { + a.push(i); + } + return a; + } + + domChanged(e) { + var nodes = dom(this.root).querySelectorAll('item-element'); + if (this.lang === 'en' && this.effectiveLang === '') { + this.effectiveLang = 'en'; + } + //console.log('multiple-element: dom-change count = ' + nodes.length + ' lang = ' + this.lang + ' effectiveLang = ' + this.effectiveLang); + if (nodes.length === this.count && + (this.lang === this.effectiveLang || + (this.effectiveLang === '' && this.lang ==='en'))) { + Array.prototype.forEach.call(nodes, function (node) { + this.$.save.appendChild(node); + }.bind(this)); + //console.log('multiple-element: local-dom-ready'); + this.async(function () { this.fire('local-dom-ready'); }, 500); + } + } + + langUpdated(e) { + var target = e.composedPath()[0]; + var lang = target.lang === '' ? 'en' : target.lang; + this.itemLang = this.itemLang || {}; + if (target.tagName.toLowerCase() === 'item-element') { + //console.log('item-element: lang-updated lang = ' + target.lang); + this.itemLang[lang] = this.itemLang[lang] || 0; + this.itemLang[lang]++; + } + //console.log('multiple-element: ' + target.is + ' ' + + // 'lang-updated lang = ' + this.lang + ' effectiveLang = ' + this.effectiveLang + + // ' itemLang[' + this.lang + '] = ' + this.itemLang[this.lang]); + if (this.itemLang[this.lang] === this.count) { + //console.log('count reached for ' + this.lang); + this.$.items.render(); + } + return false; + } + } + } + break; +case 'legacy': + { + Polymer({ + importMeta: import.meta, + + _template: html` +
+ +
+
+`, + + is: 'multiple-element', + + behaviors: [ + BehaviorsStore.I18nBehavior + ], + + properties: { + count: { + type: Number, + value: 100 + } + }, + + listeners: { + 'lang-updated': 'langUpdated' + }, + + getArray: function (count) { + var a = []; + for (var i = 0; i < count; i++) { + a.push(i); + } + return a; + }, + + domChanged: function (e) { + var nodes = dom(this.root).querySelectorAll('item-element'); + if (this.lang === 'en' && this.effectiveLang === '') { + this.effectiveLang = 'en'; + } + //console.log('multiple-element: dom-change count = ' + nodes.length + ' lang = ' + this.lang + ' effectiveLang = ' + this.effectiveLang); + if (nodes.length === this.count && + (this.lang === this.effectiveLang || + (this.effectiveLang === '' && this.lang ==='en'))) { + Array.prototype.forEach.call(nodes, function (node) { + this.$.save.appendChild(node); + }.bind(this)); + //console.log('multiple-element: local-dom-ready'); + this.async(function () { this.fire('local-dom-ready'); }, 500); + } + }, + + langUpdated: function (e) { + var target = dom(e).rootTarget; + var lang = target.lang === '' ? 'en' : target.lang; + this.itemLang = this.itemLang || {}; + if (target.tagName.toLowerCase() === 'item-element') { + //console.log('item-element: lang-updated lang = ' + target.lang); + this.itemLang[lang] = this.itemLang[lang] || 0; + this.itemLang[lang]++; + } + //console.log('multiple-element: ' + target.is + ' ' + + // 'lang-updated lang = ' + this.lang + ' effectiveLang = ' + this.effectiveLang + + // ' itemLang[' + this.lang + '] = ' + this.itemLang[this.lang]); + if (this.itemLang[this.lang] === this.count) { + //console.log('count reached for ' + this.lang); + this.$.items.render(); + } + return false; + } + }); + } + break; +} diff --git a/test/src3/multiple-case/multiple-element.json b/test/src3/multiple-case/multiple-element.json new file mode 100644 index 00000000..ce3f5d10 --- /dev/null +++ b/test/src3/multiple-case/multiple-element.json @@ -0,0 +1,4 @@ +{ + "meta": {}, + "model": {} +} \ No newline at end of file diff --git a/test/src3/no-persist-test-imports.js b/test/src3/no-persist-test-imports.js new file mode 100644 index 00000000..6da6b750 --- /dev/null +++ b/test/src3/no-persist-test-imports.js @@ -0,0 +1,22 @@ +import './fake-server.js'; +import './test-runner.js'; +import '../../i18n-element.js'; +import '../../define-element.js'; +import '../../i18n-dom-bind.js'; +import './preference/preference-element.js'; +import './no-persist-test-suites.js'; + +/** +@license https://github.com/t2ym/i18n-behavior/blob/master/LICENSE.md +Copyright (c) 2016, Tetsuya Mori . All rights reserved. +*/ +/* + + +*/ +/* + FIXME(polymer-modulizer): the above comments were extracted + from HTML and may be out of place here. Review them and + then delete this comment! +*/ +; diff --git a/test/src3/no-persist-test-suites.js b/test/src3/no-persist-test-suites.js new file mode 100644 index 00000000..1bdc7ec4 --- /dev/null +++ b/test/src3/no-persist-test-suites.js @@ -0,0 +1,64 @@ +/** +@license https://github.com/t2ym/i18n-behavior/blob/master/LICENSE.md +Copyright (c) 2016, Tetsuya Mori . All rights reserved. +*/ +import './test-runner.js'; +suite('I18nElement with ' + + (window.location.href.indexOf('?dom=Shadow') >= 0 ? 'Shadow DOM' : 'Shady DOM') + + (' in ' + syntax + ' syntax'), + function () { + + var lang0 = ''; + var lang1 = 'en'; + var lang2 = 'fr'; + var lang3 = 'ja'; + var lang4 = 'fr-CA'; + var lang5 = 'zh-Hans-CN'; + var lang6 = 'ru'; + var lang7 = 'zh-yue-Hans-CN'; + var lang8 = 'zh-CN'; + var lang9 = 'zh-TW'; + var lang10 = 'zh-Hans-CN-x-Linux'; + var navigatorLanguage = navigator.language || navigator.browserLanguage; + var isNavigatorLanguageEn = navigatorLanguage.match(/^en/); + + var suites = [ + s('preference', null, { + fixture: 'preference-element-fixture', + fixtureModel: undefined, + assign: undefined, + lang: isNavigatorLanguageEn ? lang1 : lang0, + effectiveLang: isNavigatorLanguageEn ? lang1 : lang0, + templateDefaultLang: lang1, + observeHtmlLang: true, + event: 'local-dom-ready', + text: { model: {} }, + model: {}, + localDOM: [ + (function F(){}).name ? { select: 'span#oldLang', 'lang.raw': navigatorLanguage } : { select: 'span#oldLang' } + ], + lightDOM: undefined + }) + ]; + + suitesRunner(suites); + + suite('change persist', function () { + test('persist true', function (done) { + var p = document.querySelector('i18n-preference'); + p.persist = true; + assert.equal(p.persist, true, 'persist is true'); + done(); + }); + }); + + suite('detech preference', function () { + test('detach', function (done) { + var p = document.querySelector('i18n-preference'); + p.parentNode.removeChild(p); + assert.equal(p.persist, true, 'persist is true'); + done(); + }); + }); + +}); diff --git a/test/src3/no-persist-test.html b/test/src3/no-persist-test.html new file mode 100755 index 00000000..ed3011c6 --- /dev/null +++ b/test/src3/no-persist-test.html @@ -0,0 +1,40 @@ + + + + + + + + + + + + + +

+ + + + + + + + + diff --git a/test/src3/plural-gender-element/locales/plural-gender-element.fr.json b/test/src3/plural-gender-element/locales/plural-gender-element.fr.json new file mode 100755 index 00000000..f00cb1fd --- /dev/null +++ b/test/src3/plural-gender-element/locales/plural-gender-element.fr.json @@ -0,0 +1,24 @@ +{ + "model": {}, + "compound-format-text": [ + { + "0": "fr You ({3}) gave no gifts.", + "1": { + "male": "fr You ({3}) gave him ({4}) {5}.", + "female": "fr You ({3}) gave her ({4}) {5}.", + "other": "fr You ({3}) gave them ({4}) {5}." + }, + "one": { + "male": "fr You ({3}) gave him ({4}) and one other person {5}.", + "female": "fr You ({3}) gave her ({4}) and one other person {5}.", + "other": "fr You ({3}) gave them ({4}) and one other person {5}." + }, + "other": "fr You ({3}) gave them ({4}) and {1} other people gifts." + }, + "{{recipients.length - 1}}", + "{{recipients.0.gender}}", + "{{sender.name}}", + "{{recipients.0.name}}", + "fr a gift" + ] +} diff --git a/test/src3/plural-gender-element/plural-gender-element.js b/test/src3/plural-gender-element/plural-gender-element.js new file mode 100644 index 00000000..0a1168cf --- /dev/null +++ b/test/src3/plural-gender-element/plural-gender-element.js @@ -0,0 +1,316 @@ +/** +@license https://github.com/t2ym/i18n-behavior/blob/master/LICENSE.md +Copyright (c) 2016, Tetsuya Mori . All rights reserved. +*/ +import 'i18n-behavior/i18n-behavior.js'; + +import { html } from '@polymer/polymer/lib/utils/html-tag.js'; +import { Polymer } from '@polymer/polymer/lib/legacy/polymer-fn.js'; +import { LegacyElementMixin } from '@polymer/polymer/lib/legacy/legacy-element-mixin.js'; +import deepcopy from 'deepcopy/dist/deepcopy.js'; +const $_documentContainer = document.createElement('template'); + +$_documentContainer.innerHTML = ``; + +document.head.appendChild($_documentContainer.content); +switch (syntax) { +default: +case 'mixin': + { + class PluralGenderElement extends Mixins.Localizable(LegacyElementMixin(HTMLElement)) { + static get importMeta() { + return import.meta; + } + + static get template() { + return html` +

+ + { + "0": "You ({3}) gave no gifts.", + "1": { + "male": "You ({3}) gave him ({4}) {5}.", + "female": "You ({3}) gave her ({4}) {5}.", + "other": "You ({3}) gave them ({4}) {5}." + }, + "one": { + "male": "You ({3}) gave him ({4}) and one other person {5}.", + "female": "You ({3}) gave her ({4}) and one other person {5}.", + "other": "You ({3}) gave them ({4}) and one other person {5}." + }, + "other": "You ({3}) gave them ({4}) and {1} other people gifts." + } + {{recipients.length}} + {{recipients.0.gender}} + {{sender.name}} + {{recipients.0.name}} + a gift + +

+`; + } + + static get is() { return 'plural-gender-element' } + + static get properties () { + return { + sender: { + type: Object + }, + recipients: { + type: Array + } + } + } + + ready() { + this.addEventListener('lang-updated', this._langUpdated); + super.ready(); + } + + _langUpdated(e) { + console.log('plural-gender-element lang-updated lang = ' + this.lang + ' effectiveLang = ' + this.effectiveLang); + if (e.composedPath()[0] === this) { + this.model = deepcopy(this.text.model); + } + if (this.renderedEffectiveLang === this.effectiveLang || + (this.renderedEffectiveLang === '' && this.effectiveLang === 'en')) { + this.fire('local-dom-ready'); + } + } + + _rendered() { + console.log('plural-gender-element rendered lang = ' + this.lang + ' effectiveLang = ' + this.effectiveLang); + if (this.lang === this.effectiveLang) { + this.fire('local-dom-ready'); + } + else { + this.renderedEffectiveLang = this.effectiveLang; + } + } + } + customElements.define(PluralGenderElement.is, PluralGenderElement); + } + break; +case 'base-element': + { + class PluralGenderElement extends BaseElements.I18nElement { + static get importMeta() { + return import.meta; + } + + static get template() { + return html` +

+ + { + "0": "You ({3}) gave no gifts.", + "1": { + "male": "You ({3}) gave him ({4}) {5}.", + "female": "You ({3}) gave her ({4}) {5}.", + "other": "You ({3}) gave them ({4}) {5}." + }, + "one": { + "male": "You ({3}) gave him ({4}) and one other person {5}.", + "female": "You ({3}) gave her ({4}) and one other person {5}.", + "other": "You ({3}) gave them ({4}) and one other person {5}." + }, + "other": "You ({3}) gave them ({4}) and {1} other people gifts." + } + {{recipients.length}} + {{recipients.0.gender}} + {{sender.name}} + {{recipients.0.name}} + a gift + +

+`; + } + + static get is() { return 'plural-gender-element' } + + static get properties () { + return { + sender: { + type: Object + }, + recipients: { + type: Array + } + } + } + + ready() { + this.addEventListener('lang-updated', this._langUpdated); + super.ready(); + } + + _langUpdated(e) { + console.log('plural-gender-element lang-updated lang = ' + this.lang + ' effectiveLang = ' + this.effectiveLang); + if (e.composedPath()[0] === this) { + this.model = deepcopy(this.text.model); + } + if (this.renderedEffectiveLang === this.effectiveLang || + (this.renderedEffectiveLang === '' && this.effectiveLang === 'en')) { + this.fire('local-dom-ready'); + } + } + + _rendered() { + console.log('plural-gender-element rendered lang = ' + this.lang + ' effectiveLang = ' + this.effectiveLang); + if (this.lang === this.effectiveLang) { + this.fire('local-dom-ready'); + } + else { + this.renderedEffectiveLang = this.effectiveLang; + } + } + } + customElements.define(PluralGenderElement.is, PluralGenderElement); + } + break; +case 'thin': + { + Define = class PluralGenderElement extends BaseElements.I18nElement { + + static get importMeta() { + return import.meta; + } + + static get properties () { + return { + sender: { + type: Object + }, + recipients: { + type: Array + } + } + } + + ready() { + this.addEventListener('lang-updated', this._langUpdated); + super.ready(); + } + + _langUpdated(e) { + console.log('plural-gender-element lang-updated lang = ' + this.lang + ' effectiveLang = ' + this.effectiveLang); + if (e.composedPath()[0] === this) { + this.model = deepcopy(this.text.model); + } + if (this.renderedEffectiveLang === this.effectiveLang || + (this.renderedEffectiveLang === '' && this.effectiveLang === 'en')) { + this.fire('local-dom-ready'); + } + } + + _rendered() { + console.log('plural-gender-element rendered lang = ' + this.lang + ' effectiveLang = ' + this.effectiveLang); + if (this.lang === this.effectiveLang) { + this.fire('local-dom-ready'); + } + else { + this.renderedEffectiveLang = this.effectiveLang; + } + } + }; + } + break; +case 'legacy': + { + Polymer({ + importMeta: import.meta, + + _template: html` +

+ + { + "0": "You ({3}) gave no gifts.", + "1": { + "male": "You ({3}) gave him ({4}) {5}.", + "female": "You ({3}) gave her ({4}) {5}.", + "other": "You ({3}) gave them ({4}) {5}." + }, + "one": { + "male": "You ({3}) gave him ({4}) and one other person {5}.", + "female": "You ({3}) gave her ({4}) and one other person {5}.", + "other": "You ({3}) gave them ({4}) and one other person {5}." + }, + "other": "You ({3}) gave them ({4}) and {1} other people gifts." + } + {{recipients.length}} + {{recipients.0.gender}} + {{sender.name}} + {{recipients.0.name}} + a gift + +

+`, + + is: 'plural-gender-element', + + behaviors: [ + BehaviorsStore.I18nBehavior + ], + + properties: { + sender: { + type: Object + }, + recipients: { + type: Array + } + }, + + listeners: { + 'lang-updated': '_langUpdated' + }, + + _langUpdated: function (e) { + console.log('plural-gender-element lang-updated lang = ' + this.lang + ' effectiveLang = ' + this.effectiveLang); + if (e.composedPath()[0] === this) { + this.model = deepcopy(this.text.model); + } + if (this.renderedEffectiveLang === this.effectiveLang || + (this.renderedEffectiveLang === '' && this.effectiveLang === 'en')) { + this.fire('local-dom-ready'); + } + }, + + _rendered: function () { + console.log('plural-gender-element rendered lang = ' + this.lang + ' effectiveLang = ' + this.effectiveLang); + if (this.lang === this.effectiveLang) { + this.fire('local-dom-ready'); + } + else { + this.renderedEffectiveLang = this.effectiveLang; + } + } + }); + } + break; +} diff --git a/test/src3/plural-gender-element/plural-gender-element.json b/test/src3/plural-gender-element/plural-gender-element.json new file mode 100644 index 00000000..c8cf341f --- /dev/null +++ b/test/src3/plural-gender-element/plural-gender-element.json @@ -0,0 +1,25 @@ +{ + "meta": {}, + "model": {}, + "compound-format-text": [ + { + "0": "You ({3}) gave no gifts.", + "1": { + "male": "You ({3}) gave him ({4}) {5}.", + "female": "You ({3}) gave her ({4}) {5}.", + "other": "You ({3}) gave them ({4}) {5}." + }, + "one": { + "male": "You ({3}) gave him ({4}) and one other person {5}.", + "female": "You ({3}) gave her ({4}) and one other person {5}.", + "other": "You ({3}) gave them ({4}) and one other person {5}." + }, + "other": "You ({3}) gave them ({4}) and {1} other people gifts." + }, + "{{recipients.length - 1}}", + "{{recipients.0.gender}}", + "{{sender.name}}", + "{{recipients.0.name}}", + "a gift" + ] +} \ No newline at end of file diff --git a/test/src3/preference-test-imports.js b/test/src3/preference-test-imports.js new file mode 100644 index 00000000..a645e634 --- /dev/null +++ b/test/src3/preference-test-imports.js @@ -0,0 +1,22 @@ +import './fake-server.js'; +import './test-runner.js'; +import '../../i18n-element.js'; +import '../../define-element.js'; +import '../../i18n-dom-bind.js'; +import './preference/preference-element.js'; +import './preference-test-suites.js'; + +/** +@license https://github.com/t2ym/i18n-behavior/blob/master/LICENSE.md +Copyright (c) 2016, Tetsuya Mori . All rights reserved. +*/ +/* + + +*/ +/* + FIXME(polymer-modulizer): the above comments were extracted + from HTML and may be out of place here. Review them and + then delete this comment! +*/ +; diff --git a/test/src3/preference-test-suites.js b/test/src3/preference-test-suites.js new file mode 100644 index 00000000..a9e44be1 --- /dev/null +++ b/test/src3/preference-test-suites.js @@ -0,0 +1,46 @@ +/** +@license https://github.com/t2ym/i18n-behavior/blob/master/LICENSE.md +Copyright (c) 2016, Tetsuya Mori . All rights reserved. +*/ +import './test-runner.js'; +suite('I18nElement with ' + + (window.location.href.indexOf('?dom=Shadow') >= 0 ? 'Shadow DOM' : 'Shady DOM') + + (' in ' + syntax + ' syntax'), + function () { + + var lang0 = ''; + var lang1 = 'en'; + var lang2 = 'fr'; + var lang3 = 'ja'; + var lang4 = 'fr-CA'; + var lang5 = 'zh-Hans-CN'; + var lang6 = 'ru'; + var lang7 = 'zh-yue-Hans-CN'; + var lang8 = 'zh-CN'; + var lang9 = 'zh-TW'; + var lang10 = 'zh-Hans-CN-x-Linux'; + var navigatorLanguage = navigator.language || navigator.browserLanguage; + var isNavigatorLanguageEn = navigatorLanguage.match(/^en/); + + var suites = [ + s('preference', null, { + fixture: 'preference-element-fixture', + fixtureModel: undefined, + assign: undefined, + lang: isNavigatorLanguageEn ? lang1 : lang0, + effectiveLang: isNavigatorLanguageEn ? lang1 : lang0, + templateDefaultLang: lang1, + observeHtmlLang: true, + event: 'local-dom-ready', + text: { model: {} }, + model: {}, + localDOM: [ + (function F(){}).name ? { select: 'span#oldLang', 'lang.raw': navigatorLanguage } : { select: 'span#oldLang' } + ], + lightDOM: undefined + }) + ]; + + suitesRunner(suites); + +}); diff --git a/test/src3/preference-test.html b/test/src3/preference-test.html new file mode 100755 index 00000000..01cdab9c --- /dev/null +++ b/test/src3/preference-test.html @@ -0,0 +1,34 @@ + + + + + + + + + + + + + +

+ + + + + + + + diff --git a/test/src3/preference/preference-element.js b/test/src3/preference/preference-element.js new file mode 100644 index 00000000..65965b86 --- /dev/null +++ b/test/src3/preference/preference-element.js @@ -0,0 +1,149 @@ +/** +@license https://github.com/t2ym/i18n-behavior/blob/master/LICENSE.md +Copyright (c) 2016, Tetsuya Mori . All rights reserved. +*/ +import 'i18n-behavior/i18n-behavior.js'; + +import { html } from '@polymer/polymer/lib/utils/html-tag.js'; +import { Polymer } from '@polymer/polymer/lib/legacy/polymer-fn.js'; +import { LegacyElementMixin } from '@polymer/polymer/lib/legacy/legacy-element-mixin.js'; +import { dom } from '@polymer/polymer/lib/legacy/polymer.dom.js'; +const $_documentContainer = document.createElement('template'); + +$_documentContainer.innerHTML = ``; + +document.head.appendChild($_documentContainer.content); +switch (syntax) { +default: +case 'mixin': + { + class PreferenceElement extends Mixins.Localizable(LegacyElementMixin(HTMLElement)) { + static get importMeta() { + return import.meta; + } + + static get template() { + return html` + +`; + } + + static get is() { return 'preference-element' } + + ready() { + this.addEventListener('lang-updated', this._langUpdated); + super.ready(); + } + + _langUpdated (e) { + console.log(this.is, 'lang-updated', e.detail); + if (e.composedPath()[0] === this) { + console.log(e.detail); + console.log('navigator.language = ' + navigator.language); + if (!e.detail.lastLang || e.detail.lastLang === 'en' || e.detail.lastLang === 'en-US') { + this.$.oldLang.lang = e.detail.oldLang; + this.fire('local-dom-ready'); + } + } + } + } + customElements.define(PreferenceElement.is, PreferenceElement); + } + break; +case 'base-element': + { + class PreferenceElement extends BaseElements.I18nElement { + static get importMeta() { + return import.meta; + } + + static get template() { + return html` + +`; + } + + static get is() { return 'preference-element' } + + ready() { + this.addEventListener('lang-updated', this._langUpdated); + super.ready(); + } + + _langUpdated (e) { + console.log(this.is, 'lang-updated', e.detail); + if (e.composedPath()[0] === this) { + console.log(e.detail); + console.log('navigator.language = ' + navigator.language); + if (!e.detail.lastLang || e.detail.lastLang === 'en') { + this.$.oldLang.lang = e.detail.oldLang; + this.fire('local-dom-ready'); + } + } + } + } + customElements.define(PreferenceElement.is, PreferenceElement); + } + break; +case 'thin': + { + Define = class PreferenceElement extends BaseElements.I18nElement { + + static get importMeta() { + return import.meta; + } + + ready() { + this.addEventListener('lang-updated', this._langUpdated); + super.ready(); + } + + _langUpdated (e) { + console.log(this.is, 'lang-updated', e.detail); + if (e.composedPath()[0] === this) { + console.log(e.detail); + console.log('navigator.language = ' + navigator.language); + if (!e.detail.lastLang || e.detail.lastLang === 'en') { + this.$.oldLang.lang = e.detail.oldLang; + this.fire('local-dom-ready'); + } + } + } + }; + } + break; +case 'legacy': + { + Polymer({ + importMeta: import.meta, + + _template: html` + +`, + + is: 'preference-element', + + behaviors: [ + BehaviorsStore.I18nBehavior + ], + + listeners: { + 'lang-updated': '_langUpdated' + }, + + _langUpdated: function (e) { + if (dom(e).rootTarget === this) { + console.log(e.detail); + console.log('navigator.language = ' + navigator.language); + if (!e.detail.lastLang || e.detail.lastLang === 'en') { + this.$.oldLang.lang = e.detail.oldLang; + this.fire('local-dom-ready'); + } + } + } + }); + } + break; +} diff --git a/test/src3/preference/preference-element.json b/test/src3/preference/preference-element.json new file mode 100644 index 00000000..ce3f5d10 --- /dev/null +++ b/test/src3/preference/preference-element.json @@ -0,0 +1,4 @@ +{ + "meta": {}, + "model": {} +} \ No newline at end of file diff --git a/test/src3/simple-attribute-dom-bind.json b/test/src3/simple-attribute-dom-bind.json new file mode 100644 index 00000000..6844e568 --- /dev/null +++ b/test/src3/simple-attribute-dom-bind.json @@ -0,0 +1,144 @@ +{ + "meta": {}, + "model": { + "standard-input": { + "placeholder": "standard HTML5 attribute" + }, + "outer-div:input_2": { + "placeholder": "standard HTML5 attribute without id" + }, + "paper-input-element": { + "label": "paper-input label", + "error-message": "paper-input error message", + "placeholder": "paper-input placeholder" + }, + "outer-div:paper-input_4": { + "label": "paper-input label without id", + "error-message": "paper-input error message without id", + "placeholder": "paper-input placeholder without id" + }, + "pie-chart": { + "options": { + "title": "Distribution of days in 2001H1" + }, + "cols": [ + { + "label": "Month", + "type": "string" + }, + { + "label": "Days", + "type": "number" + } + ], + "rows": [ + [ + "Jan", + 31 + ], + [ + "Feb", + 28 + ], + [ + "Mar", + 31 + ], + [ + "Apr", + 30 + ], + [ + "May", + 31 + ], + [ + "Jun", + 30 + ] + ] + }, + "column-chart": { + "options": { + "title": "Inventory" + }, + "data": [ + [ + "Year", + "Things", + "Stuff" + ], + [ + "2004", + 1000, + 400 + ], + [ + "2005", + 1170, + 460 + ], + [ + "2006", + 660, + 1120 + ], + [ + "2007", + 1030, + 540 + ] + ] + }, + "custom-attr": { + "custom-text-attr1": "custom text attribute 1", + "custom-text-attr2": "custom text attribute 2", + "custom-text-attr3": "custom text attribute 3" + }, + "selective-attr": { + "custom-text-attr4": [ + "{1} custom-text-attr4 attribute with param {2} and param {3} {4}", + "{{text.ordinary-div}}", + "{{text.ordinary-div}}", + "[[text.ordinary-div]]", + "{{text.ordinary-div}}" + ], + "custom-text-attr5": [ + "[[text.ordinary-div]]", + " custom-text-attr5 attribute with param ", + "{{or('',text.ordinary-div)}}", + " and param ", + "[[text.ordinary-div]]" + ], + "i18n-target": [ + "i18n-target attribute with param {1} and param {2}", + "{{text.ordinary-div}}", + "[[text.ordinary-div]]" + ], + "i18n-target2": [ + "i18n-target2 attribute with param ", + "{{or('',text.ordinary-div)}}", + " and param ", + "[[text.ordinary-div]]" + ] + }, + "selective-attr2": { + "i18n-target": "i18n-target attribute 2" + }, + "selective-attr3": { + "i18n-target6": "i18n-target6 attribute 2" + }, + "selective-attr4": { + "i18n-target6": "i18n-target6 attribute 3" + }, + "json-data-id": { + "attr1": "this attr1 is extracted", + "i18n-target-attr": "this attribute is also extracted" + }, + "template_2:json-data_1": { + "attr1": "this attr1 without id is extracted", + "i18n-target-attr": "this attribute without id is also extracted" + } + }, + "ordinary-div": "text 1" +} \ No newline at end of file diff --git a/test/src3/simple-attribute-element/locales/simple-attribute-element.fr.json b/test/src3/simple-attribute-element/locales/simple-attribute-element.fr.json new file mode 100755 index 00000000..792aa23e --- /dev/null +++ b/test/src3/simple-attribute-element/locales/simple-attribute-element.fr.json @@ -0,0 +1,144 @@ +{ + "meta": {}, + "model": { + "standard-input": { + "placeholder": "fr standard HTML5 attribute" + }, + "outer-div:input_2": { + "placeholder": "fr standard HTML5 attribute without id" + }, + "paper-input-element": { + "label": "fr paper-input label", + "error-message": "fr paper-input error message", + "placeholder": "fr paper-input placeholder" + }, + "outer-div:paper-input_4": { + "label": "fr paper-input label without id", + "error-message": "fr paper-input error message without id", + "placeholder": "fr paper-input placeholder without id" + }, + "pie-chart": { + "options": { + "title": "fr Distribution of days in 2001H1" + }, + "cols": [ + { + "label": "fr Month", + "type": "string" + }, + { + "label": "fr Days", + "type": "number" + } + ], + "rows": [ + [ + "fr Jan", + 31 + ], + [ + "fr Feb", + 28 + ], + [ + "fr Mar", + 31 + ], + [ + "fr Apr", + 30 + ], + [ + "fr May", + 31 + ], + [ + "fr Jun", + 30 + ] + ] + }, + "column-chart": { + "options": { + "title": "fr Inventory" + }, + "data": [ + [ + "fr Year", + "fr Things", + "fr Stuff" + ], + [ + "2004", + 1000, + 400 + ], + [ + "2005", + 1170, + 460 + ], + [ + "2006", + 660, + 1120 + ], + [ + "2007", + 1030, + 540 + ] + ] + }, + "custom-attr": { + "custom-text-attr1": "fr custom text attribute 1", + "custom-text-attr2": "fr custom text attribute 2", + "custom-text-attr3": "fr custom text attribute 3" + }, + "selective-attr": { + "custom-text-attr4": [ + "fr {1} custom-text-attr4 attribute with param {2} and param {3} {4}", + "{{text.ordinary-div}}", + "{{text.ordinary-div}}", + "[[text.ordinary-div]]", + "{{text.ordinary-div}}" + ], + "custom-text-attr5": [ + "[[text.ordinary-div]]", + " fr custom-text-attr5 attribute with param ", + "{{or('',text.ordinary-div)}}", + " fr and param ", + "[[text.ordinary-div]]" + ], + "i18n-target": [ + "fr i18n-target attribute with param {1} and param {2}", + "{{text.ordinary-div}}", + "[[text.ordinary-div]]" + ], + "i18n-target2": [ + "fr i18n-target2 attribute with param ", + "{{or('',text.ordinary-div)}}", + " fr and param ", + "[[text.ordinary-div]]" + ] + }, + "selective-attr2": { + "i18n-target": "fr i18n-target attribute 2" + }, + "selective-attr3": { + "i18n-target6": "fr i18n-target6 attribute 2" + }, + "selective-attr4": { + "i18n-target6": "fr i18n-target6 attribute 3" + }, + "json-data-id": { + "attr1": "fr this attr1 is extracted", + "i18n-target-attr": "fr this attribute is also extracted" + }, + "template_2:json-data_1": { + "attr1": "fr this attr1 without id is extracted", + "i18n-target-attr": "fr this attribute without id is also extracted" + } + }, + "ordinary-div": "fr text 1" +} diff --git a/test/src3/simple-attribute-element/locales/text-attribute-element.fr.json b/test/src3/simple-attribute-element/locales/text-attribute-element.fr.json new file mode 100644 index 00000000..6a4c57ab --- /dev/null +++ b/test/src3/simple-attribute-element/locales/text-attribute-element.fr.json @@ -0,0 +1,5 @@ +{ + "meta": {}, + "model": {}, + "span_4": "fr text" +} diff --git a/test/src3/simple-attribute-element/simple-attribute-element.js b/test/src3/simple-attribute-element/simple-attribute-element.js new file mode 100644 index 00000000..fbb1c328 --- /dev/null +++ b/test/src3/simple-attribute-element/simple-attribute-element.js @@ -0,0 +1,525 @@ +/** +@license https://github.com/t2ym/i18n-behavior/blob/master/LICENSE.md +Copyright (c) 2016, Tetsuya Mori . All rights reserved. +*/ +/* */ +/* */ +/* + FIXME(polymer-modulizer): the above comments were extracted + from HTML and may be out of place here. Review them and + then delete this comment! +*/ +import 'i18n-behavior/i18n-behavior.js'; + +import './text-attribute-element.js'; +import { html } from '@polymer/polymer/lib/utils/html-tag.js'; +import { Polymer } from '@polymer/polymer/lib/legacy/polymer-fn.js'; +import { LegacyElementMixin } from '@polymer/polymer/lib/legacy/legacy-element-mixin.js'; +import { dom } from '@polymer/polymer/lib/legacy/polymer.dom.js'; +import deepcopy from 'deepcopy/dist/deepcopy.js'; +const $_documentContainer = document.createElement('template'); + +$_documentContainer.innerHTML = ``; + +document.head.appendChild($_documentContainer.content); +switch (syntax) { +default: +case 'mixin': + { + class SimpleAttributeElement extends Mixins.Localizable(LegacyElementMixin(HTMLElement)) { + static get importMeta() { + return import.meta; + } + + static get template() { + return html` + +
+
text 1
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{model.json-data-id.attr1}} + {{model.json-data-id.i18n-target-attr}} + {{model.template_2:json-data_1.attr1}} + {{model.template_2:json-data_1.i18n-target-attr}} + +
+ +`; + } + + static get is() { return 'simple-attribute-element' } + + ready() { + this.addEventListener('lang-updated', this._langUpdated); + super.ready(); + } + + connectedCallback() { + super.connectedCallback(); + this._intervalId = window.setInterval(this._checkChartStatus.bind(this), 1000); + setTimeout(function () { + this.isPieChartRendered = true; + this.isColumnChartRendered = true; + window.clearInterval(this._intervalId); + this.fire('local-dom-ready'); // TODO: this is an ad-hoc solution + }.bind(this), 200); + } + + disconnectedCallback() { + super.disconnectedCallback(); + } + + _checkChartStatus() { + var pieChartInnerHtml = this.$['pie-chart'].$.chartdiv.innerHTML; + var columnChartInnerHtml = this.$['column-chart'].$.chartdiv.innerHTML; + var notLoaded = false; + if (pieChartInnerHtml === 'Undefined chart type') { + this.isPieChartRendered = true; // google api to blame; this is not responsible + } + if (columnChartInnerHtml === 'Undefined chart type') { + this.isColumnChartRendered = true; // google api to blame; this is not responsible + } + if (this.isPieChartRendered && this.isColumnChartRendered) { + window.clearInterval(this._intervalId); + this.fire('local-dom-ready'); + } + } + + _langUpdated(e) { + console.log('_langUpdated'); + if (e.composedPath()[0] === this) { + this.model = deepcopy(this.text.model); + } + } + + _pieChartRendered() { + this.isPieChartRendered = true; + if (this.isPieChartRendered && this.isColumnChartRendered) { + window.clearInterval(this._intervalId); + this.fire('local-dom-ready'); + } + } + + _columnChartRendered() { + this.isColumnChartRendered = true; + if (this.isPieChartRendered && this.isColumnChartRendered) { + window.clearInterval(this._intervalId); + this.fire('local-dom-ready'); + } + } + } + customElements.define(SimpleAttributeElement.is, SimpleAttributeElement); + } + break; +case 'base-element': + { + class SimpleAttributeElement extends BaseElements.I18nElement { + static get importMeta() { + return import.meta; + } + + static get template() { + return html` + +
+
text 1
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{model.json-data-id.attr1}} + {{model.json-data-id.i18n-target-attr}} + {{model.template_2:json-data_1.attr1}} + {{model.template_2:json-data_1.i18n-target-attr}} + +
+ +`; + } + + static get is() { return 'simple-attribute-element' } + + ready() { + this.addEventListener('lang-updated', this._langUpdated); + super.ready(); + } + + connectedCallback() { + super.connectedCallback(); + this._intervalId = window.setInterval(this._checkChartStatus.bind(this), 1000); + setTimeout(function () { + this.isPieChartRendered = true; + this.isColumnChartRendered = true; + window.clearInterval(this._intervalId); + this.fire('local-dom-ready'); // TODO: this is an ad-hoc solution + }.bind(this), 200); + } + + disconnectedCallback() { + super.disconnectedCallback(); + } + + _checkChartStatus() { + var pieChartInnerHtml = this.$['pie-chart'].$.chartdiv.innerHTML; + var columnChartInnerHtml = this.$['column-chart'].$.chartdiv.innerHTML; + var notLoaded = false; + if (pieChartInnerHtml === 'Undefined chart type') { + this.isPieChartRendered = true; // google api to blame; this is not responsible + } + if (columnChartInnerHtml === 'Undefined chart type') { + this.isColumnChartRendered = true; // google api to blame; this is not responsible + } + if (this.isPieChartRendered && this.isColumnChartRendered) { + window.clearInterval(this._intervalId); + this.fire('local-dom-ready'); + } + } + + _langUpdated(e) { + if (e.composedPath()[0] === this) { + this.model = deepcopy(this.text.model); + } + } + + _pieChartRendered() { + this.isPieChartRendered = true; + if (this.isPieChartRendered && this.isColumnChartRendered) { + window.clearInterval(this._intervalId); + this.fire('local-dom-ready'); + } + } + + _columnChartRendered() { + this.isColumnChartRendered = true; + if (this.isPieChartRendered && this.isColumnChartRendered) { + window.clearInterval(this._intervalId); + this.fire('local-dom-ready'); + } + } + } + customElements.define(SimpleAttributeElement.is, SimpleAttributeElement); + } + break; +case 'thin': + { + Define = class SimpleAttributeElement extends BaseElements.I18nElement { + + static get importMeta() { + return import.meta; + } + + ready() { + this.addEventListener('lang-updated', this._langUpdated); + super.ready(); + } + + connectedCallback() { + super.connectedCallback(); + this._intervalId = window.setInterval(this._checkChartStatus.bind(this), 1000); + setTimeout(function () { + this.isPieChartRendered = true; + this.isColumnChartRendered = true; + window.clearInterval(this._intervalId); + this.fire('local-dom-ready'); // TODO: this is an ad-hoc solution + }.bind(this), 200); + } + + disconnectedCallback() { + super.disconnectedCallback(); + } + + _checkChartStatus() { + var pieChartInnerHtml = this.$['pie-chart'].$.chartdiv.innerHTML; + var columnChartInnerHtml = this.$['column-chart'].$.chartdiv.innerHTML; + var notLoaded = false; + if (pieChartInnerHtml === 'Undefined chart type') { + this.isPieChartRendered = true; // google api to blame; this is not responsible + } + if (columnChartInnerHtml === 'Undefined chart type') { + this.isColumnChartRendered = true; // google api to blame; this is not responsible + } + if (this.isPieChartRendered && this.isColumnChartRendered) { + window.clearInterval(this._intervalId); + this.fire('local-dom-ready'); + } + } + + _langUpdated(e) { + if (e.composedPath()[0] === this) { + this.model = deepcopy(this.text.model); + } + } + + _pieChartRendered() { + this.isPieChartRendered = true; + if (this.isPieChartRendered && this.isColumnChartRendered) { + window.clearInterval(this._intervalId); + this.fire('local-dom-ready'); + } + } + + _columnChartRendered() { + this.isColumnChartRendered = true; + if (this.isPieChartRendered && this.isColumnChartRendered) { + window.clearInterval(this._intervalId); + this.fire('local-dom-ready'); + } + } + } + } + break; +case 'legacy': + { + Polymer({ + importMeta: import.meta, + + _template: html` + +
+
text 1
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{model.json-data-id.attr1}} + {{model.json-data-id.i18n-target-attr}} + {{model.template_2:json-data_1.attr1}} + {{model.template_2:json-data_1.i18n-target-attr}} + +
+ +`, + + is: 'simple-attribute-element', + + behaviors: [ + BehaviorsStore.I18nBehavior + ], + + listeners: { + 'lang-updated': '_langUpdated', + 'pie-chart.google-chart-render': '_pieChartRendered', + 'column-chart.google-chart-render': '_columnChartRendered' + }, + + ready: function () { + //this.observeHtmlLang = false; + }, + + attached: function () { + this._intervalId = window.setInterval(this._checkChartStatus.bind(this), 1000); + setTimeout(function () { + this.isPieChartRendered = true; + this.isColumnChartRendered = true; + window.clearInterval(this._intervalId); + this.fire('local-dom-ready'); // TODO: this is an ad-hoc solution + }.bind(this), 200); + }, + + _checkChartStatus: function () { + var pieChartInnerHtml = this.$['pie-chart'].$.chartdiv.innerHTML; + var columnChartInnerHtml = this.$['column-chart'].$.chartdiv.innerHTML; + var notLoaded = false; + if (pieChartInnerHtml === 'Undefined chart type') { + this.isPieChartRendered = true; // google api to blame; this is not responsible + } + if (columnChartInnerHtml === 'Undefined chart type') { + this.isColumnChartRendered = true; // google api to blame; this is not responsible + } + if (this.isPieChartRendered && this.isColumnChartRendered) { + window.clearInterval(this._intervalId); + this.fire('local-dom-ready'); + } + }, + + _langUpdated: function (e) { + if (dom(e).rootTarget === this) { + this.model = deepcopy(this.text.model); + } + }, + + _pieChartRendered: function () { + this.isPieChartRendered = true; + if (this.isPieChartRendered && this.isColumnChartRendered) { + window.clearInterval(this._intervalId); + this.fire('local-dom-ready'); + } + }, + + _columnChartRendered: function () { + this.isColumnChartRendered = true; + if (this.isPieChartRendered && this.isColumnChartRendered) { + window.clearInterval(this._intervalId); + this.fire('local-dom-ready'); + } + } + }); + } + break; +} diff --git a/test/src3/simple-attribute-element/simple-attribute-element.json b/test/src3/simple-attribute-element/simple-attribute-element.json new file mode 100644 index 00000000..6844e568 --- /dev/null +++ b/test/src3/simple-attribute-element/simple-attribute-element.json @@ -0,0 +1,144 @@ +{ + "meta": {}, + "model": { + "standard-input": { + "placeholder": "standard HTML5 attribute" + }, + "outer-div:input_2": { + "placeholder": "standard HTML5 attribute without id" + }, + "paper-input-element": { + "label": "paper-input label", + "error-message": "paper-input error message", + "placeholder": "paper-input placeholder" + }, + "outer-div:paper-input_4": { + "label": "paper-input label without id", + "error-message": "paper-input error message without id", + "placeholder": "paper-input placeholder without id" + }, + "pie-chart": { + "options": { + "title": "Distribution of days in 2001H1" + }, + "cols": [ + { + "label": "Month", + "type": "string" + }, + { + "label": "Days", + "type": "number" + } + ], + "rows": [ + [ + "Jan", + 31 + ], + [ + "Feb", + 28 + ], + [ + "Mar", + 31 + ], + [ + "Apr", + 30 + ], + [ + "May", + 31 + ], + [ + "Jun", + 30 + ] + ] + }, + "column-chart": { + "options": { + "title": "Inventory" + }, + "data": [ + [ + "Year", + "Things", + "Stuff" + ], + [ + "2004", + 1000, + 400 + ], + [ + "2005", + 1170, + 460 + ], + [ + "2006", + 660, + 1120 + ], + [ + "2007", + 1030, + 540 + ] + ] + }, + "custom-attr": { + "custom-text-attr1": "custom text attribute 1", + "custom-text-attr2": "custom text attribute 2", + "custom-text-attr3": "custom text attribute 3" + }, + "selective-attr": { + "custom-text-attr4": [ + "{1} custom-text-attr4 attribute with param {2} and param {3} {4}", + "{{text.ordinary-div}}", + "{{text.ordinary-div}}", + "[[text.ordinary-div]]", + "{{text.ordinary-div}}" + ], + "custom-text-attr5": [ + "[[text.ordinary-div]]", + " custom-text-attr5 attribute with param ", + "{{or('',text.ordinary-div)}}", + " and param ", + "[[text.ordinary-div]]" + ], + "i18n-target": [ + "i18n-target attribute with param {1} and param {2}", + "{{text.ordinary-div}}", + "[[text.ordinary-div]]" + ], + "i18n-target2": [ + "i18n-target2 attribute with param ", + "{{or('',text.ordinary-div)}}", + " and param ", + "[[text.ordinary-div]]" + ] + }, + "selective-attr2": { + "i18n-target": "i18n-target attribute 2" + }, + "selective-attr3": { + "i18n-target6": "i18n-target6 attribute 2" + }, + "selective-attr4": { + "i18n-target6": "i18n-target6 attribute 3" + }, + "json-data-id": { + "attr1": "this attr1 is extracted", + "i18n-target-attr": "this attribute is also extracted" + }, + "template_2:json-data_1": { + "attr1": "this attr1 without id is extracted", + "i18n-target-attr": "this attribute without id is also extracted" + } + }, + "ordinary-div": "text 1" +} \ No newline at end of file diff --git a/test/src3/simple-attribute-element/text-attribute-element.js b/test/src3/simple-attribute-element/text-attribute-element.js new file mode 100644 index 00000000..983c9027 --- /dev/null +++ b/test/src3/simple-attribute-element/text-attribute-element.js @@ -0,0 +1,294 @@ +/** +@license https://github.com/t2ym/i18n-behavior/blob/master/LICENSE.md +Copyright (c) 2016, Tetsuya Mori . All rights reserved. +*/ +import 'i18n-behavior/i18n-behavior.js'; + +import { html } from '@polymer/polymer/lib/utils/html-tag.js'; +import { Polymer } from '@polymer/polymer/lib/legacy/polymer-fn.js'; +import { LegacyElementMixin } from '@polymer/polymer/lib/legacy/legacy-element-mixin.js'; +import deepcopy from 'deepcopy/dist/deepcopy.js'; +const $_documentContainer = document.createElement('template'); + +$_documentContainer.innerHTML = ` + + +`; + +document.head.appendChild($_documentContainer.content); +switch (syntax) { +default: +case 'mixin': + { + class TextAttributeElement extends Mixins.Localizable(LegacyElementMixin(HTMLElement)) { + static get importMeta() { + return import.meta; + } + + static get template() { + return html` + {{customTextAttr1}} + {{customTextAttr2}} + {{customTextAttr3}} + {{outOfScopeAttr}} + text +`; + } + + static get is() { return 'text-attribute-element'; } + + static get properties () { + return { + customTextAttr1: { + type: String, + reflectToAttribute: true + }, + customTextAttr2: { + type: String, + reflectToAttribute: true + }, + customTextAttr3: { + type: String, + reflectToAttribute: true + }, + outOfScopeAttr: { + type: String, + reflectToAttribute: true + }, + i18nTarget: { + type: String + }, + i18nTarget2: { + type: String + } + }; + } + + ready() { + this.addEventListener('lang-updated', this._langUpdated); + super.ready(); + } + + _langUpdated () { + this.model = deepcopy(this.text.model); + } + } + customElements.define(TextAttributeElement.is, TextAttributeElement); + } + break; +case 'base-element': + { + class TextAttributeElement extends BaseElements.I18nElement { + static get importMeta() { + return import.meta; + } + + static get template() { + return html` + {{customTextAttr1}} + {{customTextAttr2}} + {{customTextAttr3}} + {{outOfScopeAttr}} + text +`; + } + + static get is() { return 'text-attribute-element'; } + + static get properties () { + return { + customTextAttr1: { + type: String, + reflectToAttribute: true + }, + customTextAttr2: { + type: String, + reflectToAttribute: true + }, + customTextAttr3: { + type: String, + reflectToAttribute: true + }, + outOfScopeAttr: { + type: String, + reflectToAttribute: true + }, + i18nTarget: { + type: String + }, + i18nTarget2: { + type: String + } + }; + } + + ready() { + this.addEventListener('lang-updated', this._langUpdated); + super.ready(); + } + + _langUpdated () { + this.model = deepcopy(this.text.model); + } + } + customElements.define(TextAttributeElement.is, TextAttributeElement); + } + break; +case 'thin': + { + Define = class TextAttributeElement extends BaseElements.I18nElement { + + static get importMeta() { + return import.meta; + } + + static get properties () { + return { + customTextAttr1: { + type: String, + reflectToAttribute: true + }, + customTextAttr2: { + type: String, + reflectToAttribute: true + }, + customTextAttr3: { + type: String, + reflectToAttribute: true + }, + outOfScopeAttr: { + type: String, + reflectToAttribute: true + }, + i18nTarget: { + type: String + }, + i18nTarget2: { + type: String + } + }; + } + + ready() { + this.addEventListener('lang-updated', this._langUpdated); + super.ready(); + } + + _langUpdated () { + this.model = deepcopy(this.text.model); + } + }; + } + break; +case 'legacy': + { + Polymer({ + importMeta: import.meta, + + _template: html` + {{customTextAttr1}} + {{customTextAttr2}} + {{customTextAttr3}} + {{outOfScopeAttr}} + text +`, + + is: 'text-attribute-element', + + behaviors: [ + BehaviorsStore.I18nBehavior + ], + + properties: { + customTextAttr1: { + type: String, + reflectToAttribute: true + }, + customTextAttr2: { + type: String, + reflectToAttribute: true + }, + customTextAttr3: { + type: String, + reflectToAttribute: true + }, + outOfScopeAttr: { + type: String, + reflectToAttribute: true + }, + i18nTarget: { + type: String + }, + i18nTarget2: { + type: String + } + }, + + listeners: { + 'lang-updated': '_langUpdated' + }, + + ready: function () { + //this.observeHtmlLang = false; + }, + + attached: function () { + }, + + _langUpdated: function () { + this.model = deepcopy(this.text.model); + } + }); + } + break; +} diff --git a/test/src3/simple-attribute-element/text-attribute-element.json b/test/src3/simple-attribute-element/text-attribute-element.json new file mode 100644 index 00000000..2715342d --- /dev/null +++ b/test/src3/simple-attribute-element/text-attribute-element.json @@ -0,0 +1,5 @@ +{ + "meta": {}, + "model": {}, + "span_4": "text" +} \ No newline at end of file diff --git a/test/src3/simple-text-dom-bind.json b/test/src3/simple-text-dom-bind.json new file mode 100644 index 00000000..f9b2ccf7 --- /dev/null +++ b/test/src3/simple-text-dom-bind.json @@ -0,0 +1,41 @@ +{ + "meta": {}, + "model": {}, + "text": " outermost text at the beginning ", + "h1_3": "outermost header 1", + "text_4": " outermost text in the middle ", + "span_5": "simple text without id", + "span_6": "simple text without id 2", + "label-1": "simple text with id", + "label-2": "simple text with id 2", + "div_9:span": "simple text within div", + "div_9:span_1": "simple text within div 2", + "div_9:div_2:div": "great grandchild text within div", + "div_10:text": " simple text as the first element in div ", + "div_10:span_1": "simple text within div", + "div_10:text_2": " simple text in the middle of div ", + "div_10:span_3": "simple text within div 2", + "div_10:div_4:div": "great grandchild text within div", + "div_10:text_5": " simple text at the last element in div ", + "toplevel-div:span": "simple text within div", + "toplevel-div:span_1": "simple text within div 2", + "third-level-div": "great grandchild text within div", + "second-level-div:div_1": "great grandchild text within div without id", + "div_12:ul:li": "line item without id 1", + "div_12:ul:li_1": "line item without id 2", + "div_12:ul:li_2": "line item without id 3", + "line-items:li": "line item with id 1", + "line-items:li_1": "line item with id 2", + "line-items:li_2": "line item with id 3", + "p_13": [ + "A paragraph with {1} is converted to {2}.", + "parameters", + "" + ], + "paragraph": [ + "A paragraph with {1} is converted to {2}.", + "id", + "" + ], + "text_15": " outermost text at the end " +} \ No newline at end of file diff --git a/test/src3/simple-text-element/locales/simple-text-element.fr.json b/test/src3/simple-text-element/locales/simple-text-element.fr.json new file mode 100755 index 00000000..3b5a034f --- /dev/null +++ b/test/src3/simple-text-element/locales/simple-text-element.fr.json @@ -0,0 +1,40 @@ +{ + "model": {}, + "text": " fr outermost text at the beginning ", + "h1_3": "fr outermost header 1", + "text_4": " fr outermost text in the middle ", + "span_5": "fr simple text without id", + "span_6": "fr simple text without id 2", + "label-1": "fr simple text with id", + "label-2": "fr simple text with id 2", + "div_9:span": "fr simple text within div", + "div_9:span_1": "fr simple text within div 2", + "div_9:div_2:div": "fr great grandchild text within div", + "div_10:text": " fr simple text as the first element in div ", + "div_10:span_1": "fr simple text within div", + "div_10:text_2": " fr simple text in the middle of div ", + "div_10:span_3": "fr simple text within div 2", + "div_10:div_4:div": "fr great grandchild text within div", + "div_10:text_5": " fr simple text at the last element in div ", + "toplevel-div:span": "fr simple text within div", + "toplevel-div:span_1": "fr simple text within div 2", + "third-level-div": "fr great grandchild text within div", + "second-level-div:div_1": "fr great grandchild text within div without id", + "div_12:ul:li": "fr line item without id 1", + "div_12:ul:li_1": "fr line item without id 2", + "div_12:ul:li_2": "fr line item without id 3", + "line-items:li": "fr line item with id 1", + "line-items:li_1": "fr line item with id 2", + "line-items:li_2": "fr line item with id 3", + "p_13": [ + "fr A paragraph with {1} is converted to {2}.", + "fr parameters", + "fr " + ], + "paragraph": [ + "fr A paragraph with {1} is converted to {2}.", + "fr id", + "fr " + ], + "text_15": " fr outermost text at the end " +} diff --git a/test/src3/simple-text-element/locales/simple-text-element.ru.json b/test/src3/simple-text-element/locales/simple-text-element.ru.json new file mode 100644 index 00000000..88b1b37a --- /dev/null +++ b/test/src3/simple-text-element/locales/simple-text-element.ru.json @@ -0,0 +1,40 @@ +{ + "model": {}, + "text": " ru outermost text at the beginning ", + "h1_3": "ru outermost header 1", + "text_4": " ru outermost text in the middle ", + "span_5": "ru simple text without id", + "span_6": "ru simple text without id 2", + "label-1": "ru simple text with id", + "label-2": "ru simple text with id 2", + "div_9:span": "ru simple text within div", + "div_9:span_1": "ru simple text within div 2", + "div_9:div_2:div": "ru great grandchild text within div", + "div_10:text": " ru simple text as the first element in div ", + "div_10:span_1": "ru simple text within div", + "div_10:text_2": " ru simple text in the middle of div ", + "div_10:span_3": "ru simple text within div 2", + "div_10:div_4:div": "ru great grandchild text within div", + "div_10:text_5": " ru simple text at the last element in div ", + "toplevel-div:span": "ru simple text within div", + "toplevel-div:span_1": "ru simple text within div 2", + "third-level-div": "ru great grandchild text within div", + "second-level-div:div_1": "ru great grandchild text within div without id", + "div_12:ul:li": "ru line item without id 1", + "div_12:ul:li_1": "ru line item without id 2", + "div_12:ul:li_2": "ru line item without id 3", + "line-items:li": "ru line item with id 1", + "line-items:li_1": "ru line item with id 2", + "line-items:li_2": "ru line item with id 3", + "p_13": [ + "ru A paragraph with {1} is converted to {2}.", + "ru parameters", + "ru " + ], + "paragraph": [ + "ru A paragraph with {1} is converted to {2}.", + "ru id", + "ru " + ], + "text_15": " ru outermost text at the end " +} diff --git a/test/src3/simple-text-element/simple-text-element.js b/test/src3/simple-text-element/simple-text-element.js new file mode 100644 index 00000000..990a2d71 --- /dev/null +++ b/test/src3/simple-text-element/simple-text-element.js @@ -0,0 +1,314 @@ +/** +@license https://github.com/t2ym/i18n-behavior/blob/master/LICENSE.md +Copyright (c) 2016, Tetsuya Mori . All rights reserved. +*/ +import 'i18n-behavior/i18n-behavior.js'; + +import { html } from '@polymer/polymer/lib/utils/html-tag.js'; +import { Polymer } from '@polymer/polymer/lib/legacy/polymer-fn.js'; +import { LegacyElementMixin } from '@polymer/polymer/lib/legacy/legacy-element-mixin.js'; +import { dom } from '@polymer/polymer/lib/legacy/polymer.dom.js'; +import deepcopy from 'deepcopy/dist/deepcopy.js'; +const $_documentContainer = document.createElement('template'); + +$_documentContainer.innerHTML = ``; + +document.head.appendChild($_documentContainer.content); +switch (syntax) { +default: +case 'mixin': + { + class SimpleTextElement extends Mixins.Localizable(LegacyElementMixin(HTMLElement)) { + static get importMeta() { + return import.meta; + } + + static get template() { + return html` + outermost text at the beginning +
+   +

outermost header 1

+ outermost text in the middle + simple text without id + simple text without id 2 + simple text with id + simple text with id 2 +
+ simple text within div + simple text within div 2 +
great grandchild text within div
+
+
+ simple text as the first element in div + simple text within div + simple text in the middle of div + simple text within div 2 +
great grandchild text within div
+ simple text at the last element in div +
+
+ simple text within div + simple text within div 2 +
+
great grandchild text within div
+
great grandchild text within div without id
+
+
+
+
    +
  • line item without id 1
  • +
  • line item without id 2
  • +
  • line item without id 3
  • +
+
    +
  • line item with id 1
  • +
  • line item with id 2
  • +
  • line item with id 3
  • +
+
+

A paragraph with parameters is converted to <i18n-format>.

+

A paragraph with id is converted to <i18n-format>.

+ outermost text at the end +`; + } + + static get is() { return 'simple-text-element' } + + ready() { + this.addEventListener('lang-updated', this._langUpdated); + super.ready(); + } + + _langUpdated (e) { + console.log(this.is, 'lang-updated', e.detail); + if (e.composedPath()[0] === this) { + this.model = deepcopy(this.text.model); + } + } + } + customElements.define(SimpleTextElement.is, SimpleTextElement); + } + break; +case 'base-element': + { + class SimpleTextElement extends BaseElements.I18nElement { + static get importMeta() { + return import.meta; + } + + static get template() { + return html` + outermost text at the beginning +
+   +

outermost header 1

+ outermost text in the middle + simple text without id + simple text without id 2 + simple text with id + simple text with id 2 +
+ simple text within div + simple text within div 2 +
great grandchild text within div
+
+
+ simple text as the first element in div + simple text within div + simple text in the middle of div + simple text within div 2 +
great grandchild text within div
+ simple text at the last element in div +
+
+ simple text within div + simple text within div 2 +
+
great grandchild text within div
+
great grandchild text within div without id
+
+
+
+
    +
  • line item without id 1
  • +
  • line item without id 2
  • +
  • line item without id 3
  • +
+
    +
  • line item with id 1
  • +
  • line item with id 2
  • +
  • line item with id 3
  • +
+
+

A paragraph with parameters is converted to <i18n-format>.

+

A paragraph with id is converted to <i18n-format>.

+ outermost text at the end +`; + } + + static get is() { return 'simple-text-element' } + + ready() { + this.addEventListener('lang-updated', this._langUpdated); + super.ready(); + } + + _langUpdated (e) { + console.log(this.is, 'lang-updated', e.detail); + if (e.composedPath()[0] === this) { + this.model = deepcopy(this.text.model); + } + } + } + customElements.define(SimpleTextElement.is, SimpleTextElement); + } + break; +case 'thin': + { + Define = class SimpleTextElement extends BaseElements.I18nElement { + + static get importMeta() { + return import.meta; + } + + ready() { + this.addEventListener('lang-updated', this._langUpdated); + super.ready(); + } + + _langUpdated (e) { + console.log(this.is, 'lang-updated', e.detail); + if (e.composedPath()[0] === this) { + this.model = deepcopy(this.text.model); + } + } + }; + } + break; +case 'legacy': + { + Polymer({ + importMeta: import.meta, + + _template: html` + outermost text at the beginning +
+   +

outermost header 1

+ outermost text in the middle + simple text without id + simple text without id 2 + simple text with id + simple text with id 2 +
+ simple text within div + simple text within div 2 +
great grandchild text within div
+
+
+ simple text as the first element in div + simple text within div + simple text in the middle of div + simple text within div 2 +
great grandchild text within div
+ simple text at the last element in div +
+
+ simple text within div + simple text within div 2 +
+
great grandchild text within div
+
great grandchild text within div without id
+
+
+
+
    +
  • line item without id 1
  • +
  • line item without id 2
  • +
  • line item without id 3
  • +
+
    +
  • line item with id 1
  • +
  • line item with id 2
  • +
  • line item with id 3
  • +
+
+

A paragraph with parameters is converted to <i18n-format>.

+

A paragraph with id is converted to <i18n-format>.

+ outermost text at the end +`, + + is: 'simple-text-element', + + behaviors: [ + BehaviorsStore.I18nBehavior + ], + + listeners: { + 'lang-updated': '_langUpdated' + }, + + ready: function () { + //this.observeHtmlLang = false; + }, + + attached: function () { + }, + + _langUpdated: function (e) { + console.log(this.is, 'lang-updated', e.detail); + if (dom(e).rootTarget === this) { + this.model = deepcopy(this.text.model); + } + } + }); + } + break; +} diff --git a/test/src3/simple-text-element/simple-text-element.json b/test/src3/simple-text-element/simple-text-element.json new file mode 100644 index 00000000..f9b2ccf7 --- /dev/null +++ b/test/src3/simple-text-element/simple-text-element.json @@ -0,0 +1,41 @@ +{ + "meta": {}, + "model": {}, + "text": " outermost text at the beginning ", + "h1_3": "outermost header 1", + "text_4": " outermost text in the middle ", + "span_5": "simple text without id", + "span_6": "simple text without id 2", + "label-1": "simple text with id", + "label-2": "simple text with id 2", + "div_9:span": "simple text within div", + "div_9:span_1": "simple text within div 2", + "div_9:div_2:div": "great grandchild text within div", + "div_10:text": " simple text as the first element in div ", + "div_10:span_1": "simple text within div", + "div_10:text_2": " simple text in the middle of div ", + "div_10:span_3": "simple text within div 2", + "div_10:div_4:div": "great grandchild text within div", + "div_10:text_5": " simple text at the last element in div ", + "toplevel-div:span": "simple text within div", + "toplevel-div:span_1": "simple text within div 2", + "third-level-div": "great grandchild text within div", + "second-level-div:div_1": "great grandchild text within div without id", + "div_12:ul:li": "line item without id 1", + "div_12:ul:li_1": "line item without id 2", + "div_12:ul:li_2": "line item without id 3", + "line-items:li": "line item with id 1", + "line-items:li_1": "line item with id 2", + "line-items:li_2": "line item with id 3", + "p_13": [ + "A paragraph with {1} is converted to {2}.", + "parameters", + "" + ], + "paragraph": [ + "A paragraph with {1} is converted to {2}.", + "id", + "" + ], + "text_15": " outermost text at the end " +} \ No newline at end of file diff --git a/test/src3/simple-text-id-element/locales/simple-text-id-element.fr.json b/test/src3/simple-text-id-element/locales/simple-text-id-element.fr.json new file mode 100755 index 00000000..d06d212b --- /dev/null +++ b/test/src3/simple-text-id-element/locales/simple-text-id-element.fr.json @@ -0,0 +1,46 @@ +{ + "model": {}, + "text": " fr outermost text at the beginning ", + "h1_3": "fr outermost header 1", + "text_4": " fr outermost text in the middle ", + "span_5": "fr simple text without id", + "span_6": "fr simple text without id 2", + "label-1": "fr simple text with id", + "label-2": "fr simple text with id 2", + "div_9:span": "fr simple text within div", + "div_9:span_1": "fr simple text within div 2", + "div_9:div_2:div": "fr great grandchild text within div", + "div_10:text": " fr simple text as the first element in div ", + "div_10:span_1": "fr simple text within div", + "div_10:text_2": " fr simple text in the middle of div ", + "div_10:span_3": "fr simple text within div 2", + "div_10:div_4:div": "fr great grandchild text within div", + "div_10:text_5": " fr simple text at the last element in div ", + "toplevel-div:span": "fr simple text within div", + "toplevel-div:span_1": "fr simple text within div 2", + "second-level-div": [ + " fr {1}\n {2} ", + "fr great grandchild text within div", + "fr great grandchild text within div without id" + ], + "div_12:ul:li": "fr line item without id 1", + "div_12:ul:li_1": "fr line item without id 2", + "div_12:ul:li_2": "fr line item without id 3", + "line-items": [ + " fr {1}\n {2}\n {3} ", + "fr line item with id 1", + "fr line item with id 2", + "fr line item with id 3" + ], + "p_13": [ + "fr A paragraph with {1} is converted to {2}.", + "fr parameters", + "fr " + ], + "paragraph": [ + "fr A paragraph with {1} is converted to {2}.", + "fr id", + "fr " + ], + "text_15": " fr outermost text at the end " +} diff --git a/test/src3/simple-text-id-element/simple-text-id-element.js b/test/src3/simple-text-id-element/simple-text-id-element.js new file mode 100644 index 00000000..5fbbb0d6 --- /dev/null +++ b/test/src3/simple-text-id-element/simple-text-id-element.js @@ -0,0 +1,314 @@ +/** +@license https://github.com/t2ym/i18n-behavior/blob/master/LICENSE.md +Copyright (c) 2016, Tetsuya Mori . All rights reserved. +*/ +import 'i18n-behavior/i18n-behavior.js'; + +import { html } from '@polymer/polymer/lib/utils/html-tag.js'; +import { Polymer } from '@polymer/polymer/lib/legacy/polymer-fn.js'; +import { LegacyElementMixin } from '@polymer/polymer/lib/legacy/legacy-element-mixin.js'; +import { dom } from '@polymer/polymer/lib/legacy/polymer.dom.js'; +import deepcopy from 'deepcopy/dist/deepcopy.js'; +const $_documentContainer = document.createElement('template'); + +$_documentContainer.innerHTML = ``; + +document.head.appendChild($_documentContainer.content); +switch (syntax) { +default: +case 'mixin': + { + class SimpleTextIdElement extends Mixins.Localizable(LegacyElementMixin(HTMLElement)) { + static get importMeta() { + return import.meta; + } + + static get template() { + return html` + outermost text at the beginning +
+   +

outermost header 1

+ outermost text in the middle + simple text without id + simple text without id 2 + simple text with id + simple text with id 2 +
+ simple text within div + simple text within div 2 +
great grandchild text within div
+
+
+ simple text as the first element in div + simple text within div + simple text in the middle of div + simple text within div 2 +
great grandchild text within div
+ simple text at the last element in div +
+
+ simple text within div + simple text within div 2 +
+
great grandchild text within div
+
great grandchild text within div without id
+
+
+
+
    +
  • line item without id 1
  • +
  • line item without id 2
  • +
  • line item without id 3
  • +
+
    +
  • line item with id 1
  • +
  • line item with id 2
  • +
  • line item with id 3
  • +
+
+

A paragraph with parameters is converted to <i18n-format>.

+

A paragraph with id is converted to <i18n-format>.

+ outermost text at the end +`; + } + + static get is() { return 'simple-text-id-element' } + + ready() { + this.addEventListener('lang-updated', this._langUpdated); + super.ready(); + } + + _langUpdated (e) { + console.log(this.is, 'lang-updated', e.detail); + if (e.composedPath()[0] === this) { + this.model = deepcopy(this.text.model); + } + } + } + customElements.define(SimpleTextIdElement.is, SimpleTextIdElement); + } + break; +case 'base-element': + { + class SimpleTextIdElement extends BaseElements.I18nElement { + static get importMeta() { + return import.meta; + } + + static get template() { + return html` + outermost text at the beginning +
+   +

outermost header 1

+ outermost text in the middle + simple text without id + simple text without id 2 + simple text with id + simple text with id 2 +
+ simple text within div + simple text within div 2 +
great grandchild text within div
+
+
+ simple text as the first element in div + simple text within div + simple text in the middle of div + simple text within div 2 +
great grandchild text within div
+ simple text at the last element in div +
+
+ simple text within div + simple text within div 2 +
+
great grandchild text within div
+
great grandchild text within div without id
+
+
+
+
    +
  • line item without id 1
  • +
  • line item without id 2
  • +
  • line item without id 3
  • +
+
    +
  • line item with id 1
  • +
  • line item with id 2
  • +
  • line item with id 3
  • +
+
+

A paragraph with parameters is converted to <i18n-format>.

+

A paragraph with id is converted to <i18n-format>.

+ outermost text at the end +`; + } + + static get is() { return 'simple-text-id-element' } + + ready() { + this.addEventListener('lang-updated', this._langUpdated); + super.ready(); + } + + _langUpdated (e) { + console.log(this.is, 'lang-updated', e.detail); + if (e.composedPath()[0] === this) { + this.model = deepcopy(this.text.model); + } + } + } + customElements.define(SimpleTextIdElement.is, SimpleTextIdElement); + } + break; +case 'thin': + { + Define = class SimpleTextIdElement extends BaseElements.I18nElement { + + static get importMeta() { + return import.meta; + } + + ready() { + this.addEventListener('lang-updated', this._langUpdated); + super.ready(); + } + + _langUpdated (e) { + console.log(this.is, 'lang-updated', e.detail); + if (e.composedPath()[0] === this) { + this.model = deepcopy(this.text.model); + } + } + }; + } + break; +case 'legacy': + { + Polymer({ + importMeta: import.meta, + + _template: html` + outermost text at the beginning +
+   +

outermost header 1

+ outermost text in the middle + simple text without id + simple text without id 2 + simple text with id + simple text with id 2 +
+ simple text within div + simple text within div 2 +
great grandchild text within div
+
+
+ simple text as the first element in div + simple text within div + simple text in the middle of div + simple text within div 2 +
great grandchild text within div
+ simple text at the last element in div +
+
+ simple text within div + simple text within div 2 +
+
great grandchild text within div
+
great grandchild text within div without id
+
+
+
+
    +
  • line item without id 1
  • +
  • line item without id 2
  • +
  • line item without id 3
  • +
+
    +
  • line item with id 1
  • +
  • line item with id 2
  • +
  • line item with id 3
  • +
+
+

A paragraph with parameters is converted to <i18n-format>.

+

A paragraph with id is converted to <i18n-format>.

+ outermost text at the end +`, + + is: 'simple-text-id-element', + + behaviors: [ + BehaviorsStore.I18nBehavior + ], + + listeners: { + 'lang-updated': '_langUpdated' + }, + + ready: function () { + //this.observeHtmlLang = false; + }, + + attached: function () { + }, + + _langUpdated: function (e) { + console.log(this.is, 'lang-updated', e.detail); + if (dom(e).rootTarget === this) { + this.model = deepcopy(this.text.model); + } + } + }); + } + break; +} diff --git a/test/src3/simple-text-id-element/simple-text-id-element.json b/test/src3/simple-text-id-element/simple-text-id-element.json new file mode 100644 index 00000000..0e01076e --- /dev/null +++ b/test/src3/simple-text-id-element/simple-text-id-element.json @@ -0,0 +1,47 @@ +{ + "meta": {}, + "model": {}, + "text": " outermost text at the beginning ", + "h1_3": "outermost header 1", + "text_4": " outermost text in the middle ", + "span_5": "simple text without id", + "span_6": "simple text without id 2", + "label-1": "simple text with id", + "label-2": "simple text with id 2", + "div_9:span": "simple text within div", + "div_9:span_1": "simple text within div 2", + "div_9:div_2:div": "great grandchild text within div", + "div_10:text": " simple text as the first element in div ", + "div_10:span_1": "simple text within div", + "div_10:text_2": " simple text in the middle of div ", + "div_10:span_3": "simple text within div 2", + "div_10:div_4:div": "great grandchild text within div", + "div_10:text_5": " simple text at the last element in div ", + "toplevel-div:span": "simple text within div", + "toplevel-div:span_1": "simple text within div 2", + "second-level-div": [ + " {1}\n {2} ", + "great grandchild text within div", + "great grandchild text within div without id" + ], + "div_12:ul:li": "line item without id 1", + "div_12:ul:li_1": "line item without id 2", + "div_12:ul:li_2": "line item without id 3", + "line-items": [ + " {1}\n {2}\n {3} ", + "line item with id 1", + "line item with id 2", + "line item with id 3" + ], + "p_13": [ + "A paragraph with {1} is converted to {2}.", + "parameters", + "" + ], + "paragraph": [ + "A paragraph with {1} is converted to {2}.", + "id", + "" + ], + "text_15": " outermost text at the end " +} \ No newline at end of file diff --git a/test/src3/template-default-lang-test-imports.js b/test/src3/template-default-lang-test-imports.js new file mode 100644 index 00000000..9fde3551 --- /dev/null +++ b/test/src3/template-default-lang-test-imports.js @@ -0,0 +1,23 @@ +import './fake-server.js'; +import './test-runner.js'; +import '../../i18n-element.js'; +import '../../define-element.js'; +import '../../i18n-dom-bind.js'; +import './template-default-lang/template-default-lang-element.js'; +import './template-default-lang/null-template-default-lang-element.js'; +import './template-default-lang-test-suites.js'; + +/** +@license https://github.com/t2ym/i18n-behavior/blob/master/LICENSE.md +Copyright (c) 2016, Tetsuya Mori . All rights reserved. +*/ +/* + + +*/ +/* + FIXME(polymer-modulizer): the above comments were extracted + from HTML and may be out of place here. Review them and + then delete this comment! +*/ +; diff --git a/test/src3/template-default-lang-test-suites.js b/test/src3/template-default-lang-test-suites.js new file mode 100644 index 00000000..c176bca3 --- /dev/null +++ b/test/src3/template-default-lang-test-suites.js @@ -0,0 +1,325 @@ +/** +@license https://github.com/t2ym/i18n-behavior/blob/master/LICENSE.md +Copyright (c) 2016, Tetsuya Mori . All rights reserved. +*/ +import './test-runner.js'; +suite('I18nElement with ' + + (window.location.href.indexOf('?dom=Shadow') >= 0 ? 'Shadow DOM' : 'Shady DOM') + + (' in ' + syntax + ' syntax'), + function () { + + var lang0 = ''; + var lang1 = 'en'; + var lang2 = 'fr'; + var lang3 = 'ja'; + var lang4 = 'fr-CA'; + var lang5 = 'zh-Hans-CN'; + var lang6 = 'ru'; + var text_template_default_lang = { + 'model': {}, + 'text': ' outermost text at the beginning ', + 'h1_3': 'outermost header 1', + 'text_4': ' outermost text in the middle ', + 'span_5': 'simple text without id', + 'span_6': 'simple text without id 2', + 'label-1': 'simple text with id', + 'label-2': 'simple text with id 2', + 'div_9:span': 'simple text within div', + 'div_9:span_1': 'simple text within div 2', + 'div_9:div_2:div': 'great grandchild text within div', + 'div_10:text': ' simple text as the first element in div ', + 'div_10:span_1': 'simple text within div', + 'div_10:text_2': ' simple text in the middle of div ', + 'div_10:span_3': 'simple text within div 2', + 'div_10:div_4:div': 'great grandchild text within div', + 'div_10:text_5': ' simple text at the last element in div ', + 'toplevel-div:span': 'simple text within div', + 'toplevel-div:span_1': 'simple text within div 2', + 'third-level-div': 'great grandchild text within div', + 'second-level-div:div_1': 'great grandchild text within div without id', + 'div_12:ul:li': 'line item without id 1', + 'div_12:ul:li_1': 'line item without id 2', + 'div_12:ul:li_2': 'line item without id 3', + 'line-items:li': 'line item with id 1', + 'line-items:li_1': 'line item with id 2', + 'line-items:li_2': 'line item with id 3', + 'p_13': [ + 'A paragraph with {1} is converted to {2}.', + 'parameters', + '' + ], + 'paragraph': [ + 'A paragraph with {1} is converted to {2}.', + 'id', + '' + ], + 'text_15': ' outermost text at the end ' + }; + var text_template_default_lang_fr = { + 'model': {}, + 'text': ' fr outermost text at the beginning ', + 'h1_3': 'fr outermost header 1', + 'text_4': ' fr outermost text in the middle ', + 'span_5': 'fr simple text without id', + 'span_6': 'fr simple text without id 2', + 'label-1': 'fr simple text with id', + 'label-2': 'fr simple text with id 2', + 'div_9:span': 'fr simple text within div', + 'div_9:span_1': 'fr simple text within div 2', + 'div_9:div_2:div': 'fr great grandchild text within div', + 'div_10:text': ' fr simple text as the first element in div ', + 'div_10:span_1': 'fr simple text within div', + 'div_10:text_2': ' fr simple text in the middle of div ', + 'div_10:span_3': 'fr simple text within div 2', + 'div_10:div_4:div': 'fr great grandchild text within div', + 'div_10:text_5': ' fr simple text at the last element in div ', + 'toplevel-div:span': 'fr simple text within div', + 'toplevel-div:span_1': 'fr simple text within div 2', + 'third-level-div': 'fr great grandchild text within div', + 'second-level-div:div_1': 'fr great grandchild text within div without id', + 'div_12:ul:li': 'fr line item without id 1', + 'div_12:ul:li_1': 'fr line item without id 2', + 'div_12:ul:li_2': 'fr line item without id 3', + 'line-items:li': 'fr line item with id 1', + 'line-items:li_1': 'fr line item with id 2', + 'line-items:li_2': 'fr line item with id 3', + 'p_13': [ + 'fr A paragraph with {1} is converted to {2}.', + 'fr parameters', + 'fr ' + ], + 'paragraph': [ + 'fr A paragraph with {1} is converted to {2}.', + 'fr id', + 'fr ' + ], + 'text_15': ' fr outermost text at the end ' + }; + var localDOM_template_default_lang = [ + { select: 'div:not([id])', 'previousTextSibling.data.trim': ' outermost text at the beginning ' }, + { select: 'h1', textContent: 'outermost header 1' }, + { select: 'h1', 'nextTextSibling.data.trim': ' outermost text in the middle ' }, + { select: 'span:not([id])', textContent: [ 'simple text without id', 'simple text without id 2' ] }, + { select: 'span[id="label-1"]', textContent: 'simple text with id' }, + { select: 'span[id="label-2"]', textContent: 'simple text with id 2' }, + { select: 'span[id="label-2"] + div span:not([id])', textContent: [ 'simple text within div', 'simple text within div 2' ] }, + { select: 'span[id="label-2"] + div div:not([id]) div:not([id])', textContent: [ 'great grandchild text within div' ] }, + { select: 'span[id="label-2"] + div + div', 'childNodes.0.data.trim': ' simple text as the first element in div ' }, + { select: 'span[id="label-2"] + div + div span:not([id])', textContent: [ 'simple text within div', 'simple text within div 2' ] }, + { select: 'span[id="label-2"] + div + div span:not([id])', 'nextTextSibling.data.trim': ' simple text in the middle of div ' }, + { select: 'span[id="label-2"] + div + div div:not([id]) div:not([id])', textContent: 'great grandchild text within div' }, + { select: 'span[id="label-2"] + div + div div:not([id])', 'nextTextSibling.data.trim': ' simple text at the last element in div ' }, + { select: '[id="toplevel-div"] span:not([id])', textContent: [ 'simple text within div', 'simple text within div 2' ] }, + { select: '[id="third-level-div"]', textContent: 'great grandchild text within div' }, + { select: '[id="second-level-div"] div:not([id])', textContent: 'great grandchild text within div without id' }, + { select: 'div ul:not([id]) li:not([id])', textContent: [ 'line item without id 1', 'line item without id 2', 'line item without id 3' ] }, + { select: '[id="line-items"] li:not([id])', textContent: [ 'line item with id 1', 'line item with id 2', 'line item with id 3' ] }, + { select: 'p:not([id]) i18n-format', + 'PolymerDom.children.0.textContent': 'A paragraph with {1} is converted to {2}.' }, + { select: 'p:not([id]) i18n-format', + 'PolymerDom.children.1.tagName': 'B', + 'PolymerDom.children.1.textContent': 'parameters', + 'PolymerDom.children.1.attributes.slot.value': '1' }, + { select: 'p:not([id]) i18n-format', + 'PolymerDom.children.2.tagName': 'CODE', + 'PolymerDom.children.2.textContent': '', + 'PolymerDom.children.2.attributes.slot.value': '2' }, + { select: '[id="paragraph"] i18n-format', + 'PolymerDom.children.0.textContent': 'A paragraph with {1} is converted to {2}.' }, + { select: '[id="paragraph"] i18n-format', + 'PolymerDom.children.1.tagName': 'B', + 'PolymerDom.children.1.textContent': 'id', + 'PolymerDom.children.1.attributes.slot.value': '1' }, + { select: '[id="paragraph"] i18n-format', + 'PolymerDom.children.2.tagName': 'CODE', + 'PolymerDom.children.2.textContent': '', + 'PolymerDom.children.2.attributes.slot.value': '2' }, + { select: '[id="paragraph"]', 'nextTextSibling.data.trim': ' outermost text at the end ' } + ]; + var localDOM_template_default_lang_fr = [ + { select: 'div:not([id])', 'previousTextSibling.data.trim': 'fr outermost text at the beginning' }, + { select: 'h1', textContent: 'fr outermost header 1' }, + { select: 'h1', 'nextTextSibling.data.trim': 'fr outermost text in the middle' }, + { select: 'span:not([id])', textContent: [ 'fr simple text without id', 'fr simple text without id 2' ] }, + { select: 'span[id="label-1"]', textContent: 'fr simple text with id' }, + { select: 'span[id="label-2"]', textContent: 'fr simple text with id 2' }, + { select: 'span[id="label-2"] + div span:not([id])', textContent: [ 'fr simple text within div', 'fr simple text within div 2' ] }, + { select: 'span[id="label-2"] + div div:not([id]) div:not([id])', textContent: [ 'fr great grandchild text within div' ] }, + { select: 'span[id="label-2"] + div + div', 'childNodes.0.data.trim': 'fr simple text as the first element in div' }, + { select: 'span[id="label-2"] + div + div span:not([id])', textContent: [ 'fr simple text within div', 'fr simple text within div 2' ] }, + { select: 'span[id="label-2"] + div + div span:not([id])', 'nextTextSibling.data.trim': 'fr simple text in the middle of div' }, + { select: 'span[id="label-2"] + div + div div:not([id]) div:not([id])', textContent: 'fr great grandchild text within div' }, + { select: 'span[id="label-2"] + div + div div:not([id])', 'nextTextSibling.data.trim': 'fr simple text at the last element in div' }, + { select: '[id="toplevel-div"] span:not([id])', textContent: [ 'fr simple text within div', 'fr simple text within div 2' ] }, + { select: '[id="third-level-div"]', textContent: 'fr great grandchild text within div' }, + { select: '[id="second-level-div"] div:not([id])', textContent: 'fr great grandchild text within div without id' }, + { select: 'div ul:not([id]) li:not([id])', textContent: [ 'fr line item without id 1', 'fr line item without id 2', 'fr line item without id 3' ] }, + { select: '[id="line-items"] li:not([id])', textContent: [ 'fr line item with id 1', 'fr line item with id 2', 'fr line item with id 3' ] }, + { select: 'p:not([id]) i18n-format', + 'PolymerDom.children.0.textContent': 'fr A paragraph with {1} is converted to {2}.' }, + { select: 'p:not([id]) i18n-format', + 'PolymerDom.children.1.tagName': 'B', + 'PolymerDom.children.1.textContent': 'fr parameters', + 'PolymerDom.children.1.attributes.slot.value': '1' }, + { select: 'p:not([id]) i18n-format', + 'PolymerDom.children.2.tagName': 'CODE', + 'PolymerDom.children.2.textContent': 'fr ', + 'PolymerDom.children.2.attributes.slot.value': '2' }, + { select: '[id="paragraph"] i18n-format', + 'PolymerDom.children.0.textContent': 'fr A paragraph with {1} is converted to {2}.' }, + { select: '[id="paragraph"] i18n-format', + 'PolymerDom.children.1.tagName': 'B', + 'PolymerDom.children.1.textContent': 'fr id', + 'PolymerDom.children.1.attributes.slot.value': '1' }, + { select: '[id="paragraph"] i18n-format', + 'PolymerDom.children.2.tagName': 'CODE', + 'PolymerDom.children.2.textContent': 'fr ', + 'PolymerDom.children.2.attributes.slot.value': '2' }, + { select: '[id="paragraph"]', 'nextTextSibling.data.trim': 'fr outermost text at the end' } + ]; + var text_simple = { + 'model': {}, + 'text': ' outermost text at the beginning ', + 'h1_3': 'outermost header 1', + 'text_4': ' outermost text in the middle ', + 'span_5': 'simple text without id', + 'span_6': 'simple text without id 2', + 'label-1': 'simple text with id', + 'label-2': 'simple text with id 2', + 'div_9:span': 'simple text within div', + 'div_9:span_1': 'simple text within div 2', + 'div_9:div_2:div': 'great grandchild text within div', + 'div_10:text': ' simple text as the first element in div ', + 'div_10:span_1': 'simple text within div', + 'div_10:text_2': ' simple text in the middle of div ', + 'div_10:span_3': 'simple text within div 2', + 'div_10:div_4:div': 'great grandchild text within div', + 'div_10:text_5': ' simple text at the last element in div ', + 'toplevel-div:span': 'simple text within div', + 'toplevel-div:span_1': 'simple text within div 2', + 'third-level-div': 'great grandchild text within div', + 'second-level-div:div_1': 'great grandchild text within div without id', + 'div_12:ul:li': 'line item without id 1', + 'div_12:ul:li_1': 'line item without id 2', + 'div_12:ul:li_2': 'line item without id 3', + 'line-items:li': 'line item with id 1', + 'line-items:li_1': 'line item with id 2', + 'line-items:li_2': 'line item with id 3', + 'p_13': [ + 'A paragraph with {1} is converted to {2}.', + 'parameters', + '' + ], + 'paragraph': [ + 'A paragraph with {1} is converted to {2}.', + 'id', + '' + ], + 'text_15': ' outermost text at the end ' + }; + var localDOM_simple = [ + { select: 'div:not([id])', 'previousTextSibling.data.trim': 'outermost text at the beginning' }, + { select: 'h1', textContent: 'outermost header 1' }, + { select: 'h1', 'nextTextSibling.data.trim': 'outermost text in the middle' }, + { select: 'span:not([id])', textContent: [ 'simple text without id', 'simple text without id 2' ] }, + { select: 'span[id="label-1"]', textContent: 'simple text with id' }, + { select: 'span[id="label-2"]', textContent: 'simple text with id 2' }, + { select: 'span[id="label-2"] + div span:not([id])', textContent: [ 'simple text within div', 'simple text within div 2' ] }, + { select: 'span[id="label-2"] + div div:not([id]) div:not([id])', textContent: [ 'great grandchild text within div' ] }, + { select: 'span[id="label-2"] + div + div', 'childNodes.0.data.trim': 'simple text as the first element in div' }, + { select: 'span[id="label-2"] + div + div span:not([id])', textContent: [ 'simple text within div', 'simple text within div 2' ] }, + { select: 'span[id="label-2"] + div + div span:not([id])', 'nextTextSibling.data.trim': 'simple text in the middle of div' }, + { select: 'span[id="label-2"] + div + div div:not([id]) div:not([id])', textContent: 'great grandchild text within div' }, + { select: 'span[id="label-2"] + div + div div:not([id])', 'nextTextSibling.data.trim': 'simple text at the last element in div' }, + { select: '[id="toplevel-div"] span:not([id])', textContent: [ 'simple text within div', 'simple text within div 2' ] }, + { select: '[id="third-level-div"]', textContent: 'great grandchild text within div' }, + { select: '[id="second-level-div"] div:not([id])', textContent: 'great grandchild text within div without id' }, + { select: 'div ul:not([id]) li:not([id])', textContent: [ 'line item without id 1', 'line item without id 2', 'line item without id 3' ] }, + { select: '[id="line-items"] li:not([id])', textContent: [ 'line item with id 1', 'line item with id 2', 'line item with id 3' ] }, + { select: 'p:not([id]) i18n-format', + 'PolymerDom.children.0.textContent': 'A paragraph with {1} is converted to {2}.' }, + { select: 'p:not([id]) i18n-format', + 'PolymerDom.children.1.tagName': 'B', + 'PolymerDom.children.1.textContent': 'parameters', + 'PolymerDom.children.1.attributes.slot.value': '1' }, + { select: 'p:not([id]) i18n-format', + 'PolymerDom.children.2.tagName': 'CODE', + 'PolymerDom.children.2.textContent': '', + 'PolymerDom.children.2.attributes.slot.value': '2' }, + { select: '[id="paragraph"] i18n-format', + 'PolymerDom.children.0.textContent': 'A paragraph with {1} is converted to {2}.' }, + { select: '[id="paragraph"] i18n-format', + 'PolymerDom.children.1.tagName': 'B', + 'PolymerDom.children.1.textContent': 'id', + 'PolymerDom.children.1.attributes.slot.value': '1' }, + { select: '[id="paragraph"] i18n-format', + 'PolymerDom.children.2.tagName': 'CODE', + 'PolymerDom.children.2.textContent': '', + 'PolymerDom.children.2.attributes.slot.value': '2' }, + { select: '[id="paragraph"]', 'nextTextSibling.data.trim': 'outermost text at the end' } + ]; + + var suites = [ + s('template default lang element', null, { + fixture: 'template-default-lang-element-fixture', + fixtureModel: undefined, + assign: undefined, + lang: lang0, + effectiveLang: lang0, + templateDefaultLang: lang2, + observeHtmlLang: true, + text: text_template_default_lang_fr, + model: {}, + localDOM: localDOM_template_default_lang_fr, + lightDOM: undefined + }), + s(lang6 + ' template default lang element', 'template default lang element', { + timeout: 60000, + assign: { lang: lang6 }, + lang: lang0, + effectiveLang: lang0 + }), + s(lang5 + ' template default lang element', 'template default lang element', { + timeout: 60000, + assign: { lang: lang5 }, + lang: lang5, + effectiveLang: lang5, + text: text_template_default_lang, + localDOM: localDOM_template_default_lang + }), + s('null template default lang element', null, { + fixture: 'null-template-default-lang-element-fixture', + fixtureModel: undefined, + assign: undefined, + lang: lang0, + effectiveLang: lang0, + templateDefaultLang: lang0, + observeHtmlLang: true, + text: text_simple, + model: {}, + localDOM: localDOM_simple, + lightDOM: undefined + }), + s(lang6 + ' null template default lang element', 'null template default lang element', { + timeout: 60000, + assign: { lang: lang6 }, + lang: lang0, + effectiveLang: lang0 + }), + s(lang5 + ' null template default lang element', 'null template default lang element', { + timeout: 60000, + assign: { lang: lang5 }, + lang: lang5, + effectiveLang: lang5 + }), + s(lang3 + ' null template default lang element', 'null template default lang element', { + timeout: 60000, + assign: { lang: lang3 }, + lang: lang0, + effectiveLang: lang0 + }), + ]; + + suitesRunner(suites); + +}); diff --git a/test/src3/template-default-lang-test.html b/test/src3/template-default-lang-test.html new file mode 100755 index 00000000..9bd08715 --- /dev/null +++ b/test/src3/template-default-lang-test.html @@ -0,0 +1,38 @@ + + + + + + + + + + + + + +

+ + + + + + + + + + diff --git a/test/src3/template-default-lang/locales/null-template-default-lang-element.ja.json b/test/src3/template-default-lang/locales/null-template-default-lang-element.ja.json new file mode 100644 index 00000000..e69de29b diff --git a/test/src3/template-default-lang/locales/null-template-default-lang-element.zh-Hans-CN.json b/test/src3/template-default-lang/locales/null-template-default-lang-element.zh-Hans-CN.json new file mode 100755 index 00000000..ca809727 --- /dev/null +++ b/test/src3/template-default-lang/locales/null-template-default-lang-element.zh-Hans-CN.json @@ -0,0 +1,41 @@ +{ + "meta": {}, + "model": {}, + "text": " zh-Hans-CN outermost text at the beginning ", + "h1_3": "zh-Hans-CN outermost header 1", + "text_4": " zh-Hans-CN outermost text in the middle ", + "span_5": "zh-Hans-CN simple text without id", + "span_6": "zh-Hans-CN simple text without id 2", + "label-1": "zh-Hans-CN simple text with id", + "label-2": "zh-Hans-CN simple text with id 2", + "div_9:span": "zh-Hans-CN simple text within div", + "div_9:span_1": "zh-Hans-CN simple text within div 2", + "div_9:div_2:div": "zh-Hans-CN great grandchild text within div", + "div_10:text": " zh-Hans-CN simple text as the first element in div ", + "div_10:span_1": "zh-Hans-CN simple text within div", + "div_10:text_2": " zh-Hans-CN simple text in the middle of div ", + "div_10:span_3": "zh-Hans-CN simple text within div 2", + "div_10:div_4:div": "zh-Hans-CN great grandchild text within div", + "div_10:text_5": " zh-Hans-CN simple text at the last element in div ", + "toplevel-div:span": "zh-Hans-CN simple text within div", + "toplevel-div:span_1": "zh-Hans-CN simple text within div 2", + "third-level-div": "zh-Hans-CN great grandchild text within div", + "second-level-div:div_1": "zh-Hans-CN great grandchild text within div without id", + "div_12:ul:li": "zh-Hans-CN line item without id 1", + "div_12:ul:li_1": "zh-Hans-CN line item without id 2", + "div_12:ul:li_2": "zh-Hans-CN line item without id 3", + "line-items:li": "zh-Hans-CN line item with id 1", + "line-items:li_1": "zh-Hans-CN line item with id 2", + "line-items:li_2": "zh-Hans-CN line item with id 3", + "p_13": [ + "zh-Hans-CN A paragraph with {1} is converted to {2}.", + "zh-Hans-CN parameters", + "zh-Hans-CN " + ], + "paragraph": [ + "zh-Hans-CN A paragraph with {1} is converted to {2}.", + "zh-Hans-CN id", + "zh-Hans-CN " + ], + "text_15": " zh-Hans-CN outermost text at the end " +} \ No newline at end of file diff --git a/test/src3/template-default-lang/locales/template-default-lang-element.zh-Hans-CN.json b/test/src3/template-default-lang/locales/template-default-lang-element.zh-Hans-CN.json new file mode 100755 index 00000000..ca809727 --- /dev/null +++ b/test/src3/template-default-lang/locales/template-default-lang-element.zh-Hans-CN.json @@ -0,0 +1,41 @@ +{ + "meta": {}, + "model": {}, + "text": " zh-Hans-CN outermost text at the beginning ", + "h1_3": "zh-Hans-CN outermost header 1", + "text_4": " zh-Hans-CN outermost text in the middle ", + "span_5": "zh-Hans-CN simple text without id", + "span_6": "zh-Hans-CN simple text without id 2", + "label-1": "zh-Hans-CN simple text with id", + "label-2": "zh-Hans-CN simple text with id 2", + "div_9:span": "zh-Hans-CN simple text within div", + "div_9:span_1": "zh-Hans-CN simple text within div 2", + "div_9:div_2:div": "zh-Hans-CN great grandchild text within div", + "div_10:text": " zh-Hans-CN simple text as the first element in div ", + "div_10:span_1": "zh-Hans-CN simple text within div", + "div_10:text_2": " zh-Hans-CN simple text in the middle of div ", + "div_10:span_3": "zh-Hans-CN simple text within div 2", + "div_10:div_4:div": "zh-Hans-CN great grandchild text within div", + "div_10:text_5": " zh-Hans-CN simple text at the last element in div ", + "toplevel-div:span": "zh-Hans-CN simple text within div", + "toplevel-div:span_1": "zh-Hans-CN simple text within div 2", + "third-level-div": "zh-Hans-CN great grandchild text within div", + "second-level-div:div_1": "zh-Hans-CN great grandchild text within div without id", + "div_12:ul:li": "zh-Hans-CN line item without id 1", + "div_12:ul:li_1": "zh-Hans-CN line item without id 2", + "div_12:ul:li_2": "zh-Hans-CN line item without id 3", + "line-items:li": "zh-Hans-CN line item with id 1", + "line-items:li_1": "zh-Hans-CN line item with id 2", + "line-items:li_2": "zh-Hans-CN line item with id 3", + "p_13": [ + "zh-Hans-CN A paragraph with {1} is converted to {2}.", + "zh-Hans-CN parameters", + "zh-Hans-CN " + ], + "paragraph": [ + "zh-Hans-CN A paragraph with {1} is converted to {2}.", + "zh-Hans-CN id", + "zh-Hans-CN " + ], + "text_15": " zh-Hans-CN outermost text at the end " +} \ No newline at end of file diff --git a/test/src3/template-default-lang/null-template-default-lang-element.js b/test/src3/template-default-lang/null-template-default-lang-element.js new file mode 100644 index 00000000..4d4f0523 --- /dev/null +++ b/test/src3/template-default-lang/null-template-default-lang-element.js @@ -0,0 +1,310 @@ +/** +@license https://github.com/t2ym/i18n-behavior/blob/master/LICENSE.md +Copyright (c) 2016, Tetsuya Mori . All rights reserved. +*/ +import 'i18n-behavior/i18n-behavior.js'; + +import { html } from '@polymer/polymer/lib/utils/html-tag.js'; +import { Polymer } from '@polymer/polymer/lib/legacy/polymer-fn.js'; +import { LegacyElementMixin } from '@polymer/polymer/lib/legacy/legacy-element-mixin.js'; +import { dom } from '@polymer/polymer/lib/legacy/polymer.dom.js'; +import deepcopy from 'deepcopy/dist/deepcopy.js'; +const $_documentContainer = document.createElement('template'); + +$_documentContainer.innerHTML = ``; + +document.head.appendChild($_documentContainer.content); +switch (syntax) { +default: +case 'mixin': + { + class NullTemplateDefaultLangElement extends Mixins.Logger(Mixins.Localizable(LegacyElementMixin(HTMLElement))) { + static get importMeta() { + return import.meta; + } + + static get template() { + return ((t) => { t.setAttribute("lang", ""); return t; })(html` + outermost text at the beginning +
+   +

outermost header 1

+ outermost text in the middle + simple text without id + simple text without id 2 + simple text with id + simple text with id 2 +
+ simple text within div + simple text within div 2 +
great grandchild text within div
+
+
+ simple text as the first element in div + simple text within div + simple text in the middle of div + simple text within div 2 +
great grandchild text within div
+ simple text at the last element in div +
+
+ simple text within div + simple text within div 2 +
+
great grandchild text within div
+
great grandchild text within div without id
+
+
+
+
    +
  • line item without id 1
  • +
  • line item without id 2
  • +
  • line item without id 3
  • +
+
    +
  • line item with id 1
  • +
  • line item with id 2
  • +
  • line item with id 3
  • +
+
+

A paragraph with parameters is converted to <i18n-format>.

+

A paragraph with id is converted to <i18n-format>.

+ outermost text at the end +`); + } + + static get is() { return 'null-template-default-lang-element'; } + + ready() { + this.addEventListener('lang-updated', this._langUpdated); + super.ready(); + } + + _langUpdated(e) { + if (e.composedPath()[0] === this && this.text) { + this.model = deepcopy(this.text.model); + } + } + } + customElements.define(NullTemplateDefaultLangElement.is, NullTemplateDefaultLangElement); + } + break; +case 'base-element': + { + class NullTemplateDefaultLangElement extends BaseElements.I18nElement { + static get importMeta() { + return import.meta; + } + + static get template() { + return ((t) => { t.setAttribute("lang", ""); return t; })(html` + outermost text at the beginning +
+   +

outermost header 1

+ outermost text in the middle + simple text without id + simple text without id 2 + simple text with id + simple text with id 2 +
+ simple text within div + simple text within div 2 +
great grandchild text within div
+
+
+ simple text as the first element in div + simple text within div + simple text in the middle of div + simple text within div 2 +
great grandchild text within div
+ simple text at the last element in div +
+
+ simple text within div + simple text within div 2 +
+
great grandchild text within div
+
great grandchild text within div without id
+
+
+
+
    +
  • line item without id 1
  • +
  • line item without id 2
  • +
  • line item without id 3
  • +
+
    +
  • line item with id 1
  • +
  • line item with id 2
  • +
  • line item with id 3
  • +
+
+

A paragraph with parameters is converted to <i18n-format>.

+

A paragraph with id is converted to <i18n-format>.

+ outermost text at the end +`); + } + + static get is() { return 'null-template-default-lang-element'; } + + ready() { + this.addEventListener('lang-updated', this._langUpdated); + super.ready(); + } + + _langUpdated(e) { + if (e.composedPath()[0] === this && this.text) { + this.model = deepcopy(this.text.model); + } + } + } + customElements.define(NullTemplateDefaultLangElement.is, NullTemplateDefaultLangElement); + } + break; +case 'thin': + { + Define = class NullTemplateDefaultLangElement extends BaseElements.I18nElement { + + static get importMeta() { + return import.meta; + } + + ready() { + this.addEventListener('lang-updated', this._langUpdated); + super.ready(); + } + + _langUpdated(e) { + if (e.composedPath()[0] === this && this.text) { + this.model = deepcopy(this.text.model); + } + } + } + } + break; +case 'legacy': + { + Polymer({ + importMeta: import.meta, + + _template: ((t) => { t.setAttribute("lang", ""); return t; })(html` + outermost text at the beginning +
+   +

outermost header 1

+ outermost text in the middle + simple text without id + simple text without id 2 + simple text with id + simple text with id 2 +
+ simple text within div + simple text within div 2 +
great grandchild text within div
+
+
+ simple text as the first element in div + simple text within div + simple text in the middle of div + simple text within div 2 +
great grandchild text within div
+ simple text at the last element in div +
+
+ simple text within div + simple text within div 2 +
+
great grandchild text within div
+
great grandchild text within div without id
+
+
+
+
    +
  • line item without id 1
  • +
  • line item without id 2
  • +
  • line item without id 3
  • +
+
    +
  • line item with id 1
  • +
  • line item with id 2
  • +
  • line item with id 3
  • +
+
+

A paragraph with parameters is converted to <i18n-format>.

+

A paragraph with id is converted to <i18n-format>.

+ outermost text at the end +`), + + is: 'null-template-default-lang-element', + + behaviors: [ + BehaviorsStore.I18nBehavior + ], + + listeners: { + 'lang-updated': '_langUpdated' + }, + + ready: function () { + //this.observeHtmlLang = false; + }, + + attached: function () { + }, + + _langUpdated: function (e) { + if (dom(e).rootTarget === this && this.text) { + this.model = deepcopy(this.text.model); + } + } + }); + } + break; +} diff --git a/test/src3/template-default-lang/null-template-default-lang-element.json b/test/src3/template-default-lang/null-template-default-lang-element.json new file mode 100644 index 00000000..f9b2ccf7 --- /dev/null +++ b/test/src3/template-default-lang/null-template-default-lang-element.json @@ -0,0 +1,41 @@ +{ + "meta": {}, + "model": {}, + "text": " outermost text at the beginning ", + "h1_3": "outermost header 1", + "text_4": " outermost text in the middle ", + "span_5": "simple text without id", + "span_6": "simple text without id 2", + "label-1": "simple text with id", + "label-2": "simple text with id 2", + "div_9:span": "simple text within div", + "div_9:span_1": "simple text within div 2", + "div_9:div_2:div": "great grandchild text within div", + "div_10:text": " simple text as the first element in div ", + "div_10:span_1": "simple text within div", + "div_10:text_2": " simple text in the middle of div ", + "div_10:span_3": "simple text within div 2", + "div_10:div_4:div": "great grandchild text within div", + "div_10:text_5": " simple text at the last element in div ", + "toplevel-div:span": "simple text within div", + "toplevel-div:span_1": "simple text within div 2", + "third-level-div": "great grandchild text within div", + "second-level-div:div_1": "great grandchild text within div without id", + "div_12:ul:li": "line item without id 1", + "div_12:ul:li_1": "line item without id 2", + "div_12:ul:li_2": "line item without id 3", + "line-items:li": "line item with id 1", + "line-items:li_1": "line item with id 2", + "line-items:li_2": "line item with id 3", + "p_13": [ + "A paragraph with {1} is converted to {2}.", + "parameters", + "" + ], + "paragraph": [ + "A paragraph with {1} is converted to {2}.", + "id", + "" + ], + "text_15": " outermost text at the end " +} \ No newline at end of file diff --git a/test/src3/template-default-lang/template-default-lang-element.js b/test/src3/template-default-lang/template-default-lang-element.js new file mode 100644 index 00000000..88485ace --- /dev/null +++ b/test/src3/template-default-lang/template-default-lang-element.js @@ -0,0 +1,310 @@ +/** +@license https://github.com/t2ym/i18n-behavior/blob/master/LICENSE.md +Copyright (c) 2016, Tetsuya Mori . All rights reserved. +*/ +import 'i18n-behavior/i18n-behavior.js'; + +import { html } from '@polymer/polymer/lib/utils/html-tag.js'; +import { Polymer } from '@polymer/polymer/lib/legacy/polymer-fn.js'; +import { LegacyElementMixin } from '@polymer/polymer/lib/legacy/legacy-element-mixin.js'; +import { dom } from '@polymer/polymer/lib/legacy/polymer.dom.js'; +import deepcopy from 'deepcopy/dist/deepcopy.js'; +const $_documentContainer = document.createElement('template'); + +$_documentContainer.innerHTML = ``; + +document.head.appendChild($_documentContainer.content); +switch (syntax) { +default: +case 'mixin': + { + class TemplateDefaultLangElement extends Mixins.Logger(Mixins.Localizable(LegacyElementMixin(HTMLElement))) { + static get importMeta() { + return import.meta; + } + + static get template() { + return ((t) => { t.setAttribute("lang", "fr"); return t; })(html` + fr outermost text at the beginning +
+   +

fr outermost header 1

+ fr outermost text in the middle + fr simple text without id + fr simple text without id 2 + fr simple text with id + fr simple text with id 2 +
+ fr simple text within div + fr simple text within div 2 +
fr great grandchild text within div
+
+
+ fr simple text as the first element in div + fr simple text within div + fr simple text in the middle of div + fr simple text within div 2 +
fr great grandchild text within div
+ fr simple text at the last element in div +
+
+ fr simple text within div + fr simple text within div 2 +
+
fr great grandchild text within div
+
fr great grandchild text within div without id
+
+
+
+
    +
  • fr line item without id 1
  • +
  • fr line item without id 2
  • +
  • fr line item without id 3
  • +
+
    +
  • fr line item with id 1
  • +
  • fr line item with id 2
  • +
  • fr line item with id 3
  • +
+
+

fr A paragraph with fr parameters is converted to fr <i18n-format>.

+

fr A paragraph with fr id is converted to fr <i18n-format>.

+ fr outermost text at the end +`); + } + + static get is() { return 'template-default-lang-element'; } + + ready() { + this.addEventListener('lang-updated', this._langUpdated); + super.ready(); + } + + _langUpdated(e) { + if (e.composedPath()[0] === this && this.text) { + this.model = deepcopy(this.text.model); + } + } + } + customElements.define(TemplateDefaultLangElement.is, TemplateDefaultLangElement); + } + break; +case 'base-element': + { + class TemplateDefaultLangElement extends BaseElements.I18nElement { + static get importMeta() { + return import.meta; + } + + static get template() { + return ((t) => { t.setAttribute("lang", "fr"); return t; })(html` + fr outermost text at the beginning +
+   +

fr outermost header 1

+ fr outermost text in the middle + fr simple text without id + fr simple text without id 2 + fr simple text with id + fr simple text with id 2 +
+ fr simple text within div + fr simple text within div 2 +
fr great grandchild text within div
+
+
+ fr simple text as the first element in div + fr simple text within div + fr simple text in the middle of div + fr simple text within div 2 +
fr great grandchild text within div
+ fr simple text at the last element in div +
+
+ fr simple text within div + fr simple text within div 2 +
+
fr great grandchild text within div
+
fr great grandchild text within div without id
+
+
+
+
    +
  • fr line item without id 1
  • +
  • fr line item without id 2
  • +
  • fr line item without id 3
  • +
+
    +
  • fr line item with id 1
  • +
  • fr line item with id 2
  • +
  • fr line item with id 3
  • +
+
+

fr A paragraph with fr parameters is converted to fr <i18n-format>.

+

fr A paragraph with fr id is converted to fr <i18n-format>.

+ fr outermost text at the end +`); + } + + static get is() { return 'template-default-lang-element'; } + + ready() { + this.addEventListener('lang-updated', this._langUpdated); + super.ready(); + } + + _langUpdated(e) { + if (e.composedPath()[0] === this && this.text) { + this.model = deepcopy(this.text.model); + } + } + } + customElements.define(TemplateDefaultLangElement.is, TemplateDefaultLangElement); + } + break; +case 'thin': + { + Define = class TemplateDefaultLangElement extends BaseElements.I18nElement { + + static get importMeta() { + return import.meta; + } + + ready() { + this.addEventListener('lang-updated', this._langUpdated); + super.ready(); + } + + _langUpdated(e) { + if (e.composedPath()[0] === this && this.text) { + this.model = deepcopy(this.text.model); + } + } + } + } + break; +case 'legacy': + { + Polymer({ + importMeta: import.meta, + + _template: ((t) => { t.setAttribute("lang", "fr"); return t; })(html` + fr outermost text at the beginning +
+   +

fr outermost header 1

+ fr outermost text in the middle + fr simple text without id + fr simple text without id 2 + fr simple text with id + fr simple text with id 2 +
+ fr simple text within div + fr simple text within div 2 +
fr great grandchild text within div
+
+
+ fr simple text as the first element in div + fr simple text within div + fr simple text in the middle of div + fr simple text within div 2 +
fr great grandchild text within div
+ fr simple text at the last element in div +
+
+ fr simple text within div + fr simple text within div 2 +
+
fr great grandchild text within div
+
fr great grandchild text within div without id
+
+
+
+
    +
  • fr line item without id 1
  • +
  • fr line item without id 2
  • +
  • fr line item without id 3
  • +
+
    +
  • fr line item with id 1
  • +
  • fr line item with id 2
  • +
  • fr line item with id 3
  • +
+
+

fr A paragraph with fr parameters is converted to fr <i18n-format>.

+

fr A paragraph with fr id is converted to fr <i18n-format>.

+ fr outermost text at the end +`), + + is: 'template-default-lang-element', + + behaviors: [ + BehaviorsStore.I18nBehavior + ], + + listeners: { + 'lang-updated': '_langUpdated' + }, + + ready: function () { + //this.observeHtmlLang = false; + }, + + attached: function () { + }, + + _langUpdated: function (e) { + if (dom(e).rootTarget === this && this.text) { + this.model = deepcopy(this.text.model); + } + } + }); + } + break; +} diff --git a/test/src3/template-default-lang/template-default-lang-element.json b/test/src3/template-default-lang/template-default-lang-element.json new file mode 100644 index 00000000..21a74b9c --- /dev/null +++ b/test/src3/template-default-lang/template-default-lang-element.json @@ -0,0 +1,41 @@ +{ + "meta": {}, + "model": {}, + "text": " fr outermost text at the beginning ", + "h1_3": "fr outermost header 1", + "text_4": " fr outermost text in the middle ", + "span_5": "fr simple text without id", + "span_6": "fr simple text without id 2", + "label-1": "fr simple text with id", + "label-2": "fr simple text with id 2", + "div_9:span": "fr simple text within div", + "div_9:span_1": "fr simple text within div 2", + "div_9:div_2:div": "fr great grandchild text within div", + "div_10:text": " fr simple text as the first element in div ", + "div_10:span_1": "fr simple text within div", + "div_10:text_2": " fr simple text in the middle of div ", + "div_10:span_3": "fr simple text within div 2", + "div_10:div_4:div": "fr great grandchild text within div", + "div_10:text_5": " fr simple text at the last element in div ", + "toplevel-div:span": "fr simple text within div", + "toplevel-div:span_1": "fr simple text within div 2", + "third-level-div": "fr great grandchild text within div", + "second-level-div:div_1": "fr great grandchild text within div without id", + "div_12:ul:li": "fr line item without id 1", + "div_12:ul:li_1": "fr line item without id 2", + "div_12:ul:li_2": "fr line item without id 3", + "line-items:li": "fr line item with id 1", + "line-items:li_1": "fr line item with id 2", + "line-items:li_2": "fr line item with id 3", + "p_13": [ + "fr A paragraph with {1} is converted to {2}.", + "fr parameters", + "fr " + ], + "paragraph": [ + "fr A paragraph with {1} is converted to {2}.", + "fr id", + "fr " + ], + "text_15": " fr outermost text at the end " +} \ No newline at end of file diff --git a/test/src3/test-runner.js b/test/src3/test-runner.js new file mode 100755 index 00000000..3d8686e8 --- /dev/null +++ b/test/src3/test-runner.js @@ -0,0 +1,721 @@ +/*! + * @license https://github.com/t2ym/i18n-behavior/blob/master/LICENSE.md + * Copyright (c) 2016, Tetsuya Mori . All rights reserved. + */ +import { dom } from '@polymer/polymer/lib/legacy/polymer.dom.js'; +import { templatize } from '@polymer/polymer/lib/utils/templatize.js'; +import { PolymerElement } from '@polymer/polymer/polymer-element.js'; +import deepcopy from 'deepcopy/dist/deepcopy.js'; + +window.deepcopy = deepcopy; + +if (!Number.isNaN) { + // polyfill Number.isNaN for IE11 + Number.isNaN = function (value) { + return typeof value === 'number' && isNaN(value); + }; +} + +// Inheritance of test parameters +window.p = Object.setPrototypeOf || function (target, base) { + var obj = Object.create(base); + for (var p in target) { + obj[p] = target[p]; + } + return obj; +}; +window.g = Object.getPrototypeOf; +window._name = 'suite'; +window.suiteMap = { null: {} }; +window.s = function (name, baseName, extension) { + if (suiteMap[name]) { + throw new Error('duplicate suite name ' + name); + } + if (!suiteMap[baseName]) { + throw new Error('inexistent base suite name ' + baseName); + } + extension[_name] = name; + extension = p(extension, suiteMap[baseName]); + suiteMap[name] = extension; + return extension; +}; + +// Utility functions + +window.updateProperty = function updateProperty (element, properties) { + for (var name in properties) { + var path = name.split(/[.]/); + if (path.length === 1) { + element[name] = properties[name]; + } + else { + var cursor = element; + var p = path.shift(); + while (p) { + if (path.length < 1) { + cursor[p] = properties[name]; + element.notifyPath(name, properties[name], true); + break; + } + else if (p === 'PolymerDom') { + cursor = dom(cursor); + } + else if (p === 'html') { + cursor = document.querySelector('html'); + } + else { + cursor = cursor[p]; + } + p = path.shift(); + } + } + } +} + +window.getProperty = function getProperty (target, name) { + var path = name.split(/[.]/); + if (path.length === 1) { + switch (name) { + case 'textContent': + return Array.prototype.map.call(target.childNodes, function (n) { return n.nodeType === n.TEXT_NODE ? n.textContent : ''; }).join(''); + default: + return target[name]; + break; + } + } + else { + var cursor = target; + var p = path.shift(); + while (p) { + //console.log(p, cursor); + if (path.length < 1) { + if (p === 'raw' || + p === 'text') { + return cursor; + } + else if (p === 'trim') { + return cursor.trim(); + } + if (p === 'data') { + cursor = cursor[p]; + cursor = cursor.replace(/^[\s]{1,}/g, ' ').replace(/[\s]{1,}$/g, ' '); + return cursor; + } + else { + return cursor[p]; + } + } + else if (p === 'PolymerDom') { + cursor = dom(cursor); + } + else if (p === 'previousTextSibling') { + do { + cursor = cursor.previousSibling; + } while (cursor.nodeType === cursor.COMMENT_NODE || + (cursor.nodeType === cursor.TEXT_NODE && cursor.data.match(/^[\s]*$/))); + } + else if (p === 'nextTextSibling') { + do { + cursor = cursor.nextSibling; + } while (cursor.nodeType === cursor.COMMENT_NODE || + (cursor.nodeType === cursor.TEXT_NODE && cursor.data.match(/^[\s]*$/))); + } + else if (p === 'effectiveChildNodes') { + cursor = cursor.getEffectiveChildNodes(); + } + else if (p === 'nonWS') { + cursor = Array.prototype.filter.call(cursor, function (item) { + return (item.nodeType !== item.TEXT_NODE && + item.nodeType !== item.COMMENT_NODE) || + (item.nodeType === item.TEXT_NODE && + !item.data.match(/^[\s]*$/)); + }); + } + else { + cursor = cursor[p]; + } + p = path.shift(); + } + } +} + +window.deepMap = function deepMap (target, source, map) { + var value; + for (var prop in source) { + value = source[prop]; + switch (typeof value) { + case 'string': + case 'number': + case 'boolean': + if (typeof target === 'object') { + target[prop] = map(value, prop); + } + break; + case 'object': + if (typeof target === 'object') { + if (Array.isArray(value)) { + target[prop] = target[prop] || []; + deepMap(target[prop], value, map); + } + else { + target[prop] = target[prop] || {}; + deepMap(target[prop], value, map); + } + } + break; + case 'function': + case 'symbol': + case 'undefined': + if (typeof target === 'object') { + target[prop] = value; + } + break; + default: + if (typeof target === 'object') { + target[prop] = value; + } + break; + } + } + return target; +} + +window.translate = function translate (lang, path, text) { + var result; + switch (lang) { + case '': + case 'en': + case null: + case undefined: + result = text; + break; + default: + if (!path || path.match(/(textContent|[.]data|[.]text|[.]trim)$/)) { + result = {}; + deepMap(result, { text: text }, function (value, prop) { + if (typeof value === 'string' && + !value.match(/^({{[^{}]*}}|\[\[[^\[\]]*\]\])$/) && + !value.match(/^[0-9]{1,}$/) && + prop !== 'type') { + if (path && path.match(/[.]trim$/)) { + return minifyText((lang + ' ' + value).trim()); + } + else { + if (value.match(/^ /)) { + return minifyText(' ' + lang + ' ' + value); + } + else { + return minifyText(lang + ' ' + value); + } + } + } + return minifyText(value); + }); + result = result.text; + } + else { + result = text; + } + } + //console.log('translate (' + lang + ', ' + path + ', ' + JSON.stringify(text, null, 2) + ') = ' + JSON.stringify(result, null, 2)); + return result; +} + +window.minifyText = function minifyText (text) { + if (text && typeof text === 'string') { + text = text.replace(/[\s]{1,}/g, ' '); + } + return text; +} + +window.isFakeServer = typeof window === 'object' && + typeof window.location.href === 'string' && + window.location.href.indexOf('xhr=fake') > 0 && + typeof window.fakeServerContents === 'object'; + +window.isSuppressingSuiteParams = typeof window === 'object' && + typeof window.location.href === 'string' && + window.location.href.indexOf('suppress=true') > 0; + +window.syntax = 'mixin'; +(function () { + var href = typeof window === 'object' && typeof window.location.href === 'string' + ? window.location.href : '' + if (href) { + [ 'mixin', 'base-element', 'thin', 'legacy', 'modified-legacy' ].forEach(function (_syntax) { + if (href.indexOf('syntax=' + _syntax) > 0) { + syntax = _syntax; + } + }); + } +})(); + +window.setupFakeServer = function setupFakeServer (e) { + if (isFakeServer) { + e.server = sinon.fakeServer.create(); + e.server.autoRespond = true; + //e.server.autoRespondAfter = 100; + e.server.respondImmediately = true; + e.server.respondWith(/\/test\/[-\w]+(\/.*[.]json)$/, function (xhr, urlPath) { + if (fakeServerContents.hasOwnProperty(urlPath)) { + //console.log('fake-server: 200 ' + urlPath); + xhr.respond(200, { 'Content-Type': 'application/json' }, fakeServerContents[urlPath]) + } + else { + //console.log('fake-server: 404 ' + urlPath); + xhr.respond(404, {}, ''); + } + }); + } +} + +window.teardownFakeServer = function teardownFakeServer (e) { + if (isFakeServer) { + e.server.restore(); + } +} + +window.setupFixture = function setupFixture (params, fixtureModel) { + var fixtureName = params.fixture; + var e = document.querySelector('#' + fixtureName); + var runningTest = document.querySelectorAll('.running-test'); + var title = document.querySelector('#test-name'); + var currentPath = window.location.pathname.split('/'); + if (!e) { + throw new Error('Fixture element with id = ' + fixtureName + ' not found'); + } + if (title) { + title.textContent = + (currentPath.length >= 2 ? currentPath[currentPath.length - 2] : '') + + (currentPath.length >= 1 ? '/' + currentPath[currentPath.length - 1].replace(/-test[.]html$/, '') + '/': '') + + params.suite; + } + //console.log('setupFixture: suite = ' + params.suite); + if (e.is === 'i18n-dom-bind') { + e.parentElement.classList.add('running-test'); + Array.prototype.forEach.call(runningTest, function (node) { + if (node !== e.parentElement) { + node.classList.remove('running-test'); + } + }); + return new Promise(function (resolve, reject) { + e.addEventListener('dom-change', function setupFixtureDomChange (ev) { + if (dom(ev).rootTarget === e) { + //console.log('setupFixture dom-change'); + e.removeEventListener('dom-change', setupFixtureDomChange); + try { + if (fixtureModel && + typeof fixtureModel.lang === 'string' && + fixtureModel.lang !== 'en') { + //console.log('setupFixture: waiting for lang-updated'); + e.addEventListener('lang-updated', function setupFixtureLangDomChange (event) { + //console.log('setupFixtureLangDomChange'); + if (event.target === e && + e.effectiveLang === fixtureModel.lang) { + //console.log('setupFixtureLangDomChange lang = ' + event.detail.lang + ' effectiveLang = ' + e.effectiveLang); + e.removeEventListener('lang-updated', setupFixtureLangDomChange); + e.render(); + resolve(e); + } + }); + for (var p in fixtureModel) { + e[p] = fixtureModel[p]; + } + e.params = params; + } + else { + for (var p in fixtureModel) { + e[p] = fixtureModel[p]; + } + e.params = params; + //console.log('setupFixture: resolving'); + e.render(); + resolve(e); + } + } + catch (ex) { + reject(ex); + } + } + }); + if (e._children) { + e.render(); + } + }); + } + else { + e.classList.add('running-test'); + Array.prototype.forEach.call(runningTest, function (node) { + if (node !== e) { + node.classList.remove('running-test'); + } + }); + setupFakeServer(e); + return new Promise(function (resolve, reject) { + //console.log('setupFixture: name = ' + fixtureName + ' model = ' + JSON.stringify(fixtureModel, null, 2)); + if (!window.FixtureWrapper) { + window.FixtureWrapper = class FixtureWrapper extends PolymerElement { }; + customElements.define('fixture-wrapper', FixtureWrapper); + } + + if (fixtureModel) { + var f = document.querySelector('test-fixture[id=' + fixtureName + ']'); + var t = f.querySelector('template[is=dom-template]'); + if (t) { + var instanceProps = {}; + var p; + for (p in fixtureModel) { + instanceProps[p] = true; + } + var self = new FixtureWrapper(); + t.__templatizeOwner = undefined; + t._ctor = templatize(t, self, { + instanceProps: instanceProps, + forwardHostProp: function(prop, value) { + if (self._instance) { + self._instance.forwardHostProp(prop, value); + } + } + }); + t.stamp = function (model) { + var _instance = new this._ctor(model); + return _instance.root; + }.bind(t); + } + } + var element = fixture(fixtureName, fixtureModel); + //console.log('setupFixture: name = ' + fixtureName + + // ' element.lang = ' + element.lang + + // ' getAttribute("lang") = ' + element.getAttribute('lang') + + // ' element._lang = ' + element._lang); + if (element) { + if (fixtureModel && + typeof fixtureModel.lang === 'string' && + fixtureModel.lang !== 'en' && + fixtureModel.lang !== element.effectiveLang && element.effectiveLang !== 'en') { + //console.log('setupFixture: waiting for lang-updated'); + element.addEventListener('lang-updated', function setupFixtureLangUpdated (event) { + //console.log('setupFixtureLangUpdated'); + if (event.target === element && + element.effectiveLang === fixtureModel.lang) { + //console.log('setupFixtureLangUpdated lang = ' + event.detail.lang + ' effectiveLang = ' + element.effectiveLang); + element.removeEventListener('lang-updated', setupFixtureLangUpdated); + resolve(element); + } + }); + } + else { + //console.log('setupFixture: element ready without lang-updated'); + setTimeout(function () { + if (params.lang === '' || params.lang === 'en') { + element.fire('lang-updated'); + } + }, 500); + resolve(element); + } + } + else { + reject(new Error('setupFixture returns null for ' + + fixtureName + ' ' + JSON.stringify(fixtureModel,null,2))); + } + }); + } +} + +window.restoreFixture = function restoreFixture (fixtureName) { + var e = document.querySelector('#' + fixtureName); + if (!e) { + throw new Error('Fixture element with id = ' + fixtureName + ' not found'); + } + if (e.is === 'i18n-dom-bind') { + if (e._intervalId) { + clearInterval(e._intervalId); + } + Array.prototype.forEach.call(document.querySelectorAll('i18n-dom-bind'), + function (node) { + node.observeHtmlLang = true; + } + ); + } + else { + teardownFakeServer(e); + e.restore(); + } +} + +window.getLocalDomRoot = function getLocalDomRoot (e) { + if (e.is === 'i18n-dom-bind') { + return e.parentElement; + } + else if (e) { + return e.root; + } + else { + return null; + } +} + +window.suitesRunner = function suitesRunner (suites, _wait) { + + suites.forEach(function (params) { + + suite(params.suite, function () { + var el; + var p; + var n; + var i, j; + var expected; + var results; + var node; + var rawValue = params.rawValue; + var fixtureElement; + var noProperties; + var lang = params.assign && params.assign.lang ? params.assign.lang : 'en'; + var event = params.event ? params.event : 'lang-updated'; + var defTimeout = 300000; + var timeout = params.timeout ? (params.timeout < defTimeout ? defTimeout : params.timeout) : defTimeout; + this.timeout(timeout); + + (params.setup ? setup : suiteSetup)(function () { + return (_wait ? new Promise((resolve) => { setTimeout(() => { resolve(); }, _wait); }) : Promise.resolve()).then(() => setupFixture(params, params.fixtureModel)) + .then(function (element) { + el = element; + //console.log('setup: element.lang = ' + element.lang); + return new Promise(function (resolve, reject) { + //console.log(params.suite, 'waiting for ' + event); + if (params && + (params.event || + params.assign && (params.assign.lang || params.assign['html.lang']))) { + el.addEventListener(event, function fixtureSetup (e) { + if (el === dom(e).rootTarget && + el.lang === params.lang && + el.effectiveLang === params.effectiveLang) { + el.removeEventListener(event, fixtureSetup); + //console.log('setup: updateProperty resolving on ' + event); + resolve(el); + } + else { + console.log(params.suite + ' skipping uninteresting event ' + event + + ' "' + el.lang + '" "' + params.lang + '" "' + el.effectiveLang + '" "' + params.effectiveLang + '"'); + } + }); + //console.log('setup: updateProperty ' + JSON.stringify(params.assign, null, 2)); + updateProperty(el, params.assign); + } + else { + //console.log('setup: updateProperty ' + JSON.stringify(params.assign, null, 2)); + //console.log('setup: updateProperty resolving without ' + event); + updateProperty(el, params.assign); + resolve(el); + } + }); + }, function (error) { + throw new Error(error); + }) + .then((result) => { + if (_wait) { + return new Promise((resolve) => { setTimeout(() => resolve(result), _wait); }); + } + else { + return result; + } + }); + }); + + test('{lang, effectiveLang, templateDefaultLang, observeHtmlLang' + + (params.text ? ', text' : '') + + (params.model ? ', model' : '') + + (params.localDOM ? ', local DOM' : '') + + '} properties are set as {' + + (isSuppressingSuiteParams ? '' : + [ params.lang, params.effectiveLang, params.templateDefaultLang, params.observeHtmlLang].join(', ') + + (params.text ? ', ' + JSON.stringify(params.text, null, 2) : '') + + (params.model ? ', ' + JSON.stringify(params.model, null, 2) : '') + + (!params.setup && params.localDOM ? ', ' + JSON.stringify(params.localDOM, null, 2) : '')) + + '}' + + (params.assign && params.assign.lang ? ' for ' + params.assign.lang : ''), function () { + assert.isString(el.lang, 'lang property is a string'); + assert.equal(el.lang, params.lang, 'lang property is set'); + assert.isString(el.effectiveLang, 'effectiveLang property is a string'); + assert.equal(el.effectiveLang, params.effectiveLang, 'effectiveLang property is set'); + assert.isString(el.templateDefaultLang, 'templateDefaultLang property is a string'); + assert.equal(el.templateDefaultLang, params.templateDefaultLang, 'templateDefaultLang property is set'); + assert.isBoolean(el.observeHtmlLang, 'observeHtmlLang property is a Boolean'); + assert.equal(el.observeHtmlLang, params.observeHtmlLang, 'observeHtmlLang property is set'); + if (params.text) { + var actual; + expected = deepMap(deepcopy(params.text), params.text, minifyText); + actual = deepMap(deepcopy(el.text), el.text, minifyText); + noProperties = true; + assert.isObject(el.text, 'text property is an object'); + //console.log(JSON.stringify(e.detail, null, 2)); + //console.log(JSON.stringify(el.text, null, 2)); + for (p in expected) { + if (p === 'meta') { + continue; + } + noProperties = false; + assert.deepEqual(actual[p], + params.rawText ? expected[p] : translate(params.effectiveLang, null, expected[p]), + 'text.' + p + ' property is set for ' + params.effectiveLang); + } + if (noProperties) { + assert.deepEqual(deepMap(deepcopy(el.text), el.text, minifyText), + expected, + 'text property is set'); + } + } + if (params.model) { + noProperties = true; + assert.isObject(el.model, 'model property is an object'); + for (p in expected) { + noProperties = false; + //console.log('model.' + p + ' = ' + JSON.stringify(el.model[p])); + //console.log('expected model.' + p + ' = ' + JSON.stringify(translate(el.effectiveLang, null, params.model[p]))); + assert.deepEqual(minifyText(el.model[p]), + params.rawText ? params.model[p] : translate(params.effectiveLang, null, params.model[p]), + 'model.' + p + ' property is set for ' + params.effectiveLang); + } + if (noProperties) { + assert.deepEqual(el.model, params.model, 'model property is set'); + } + } + if (!params.setup && params.localDOM) { + params.localDOM.forEach(function (childPath) { + var completeStatus; + var nodes = dom(getLocalDomRoot(el)).querySelectorAll(childPath.select); + assert.ok(nodes.length > 0, childPath.select + ' is defined'); + for (var p in childPath) { + if (p === 'select') { + continue; + } + //console.log(p + ' is set as ' + childPath[p]); + if (Array.isArray(childPath[p])) { + //console.log(nodes); + Array.prototype.forEach.call(childPath[p], function (path, i, a) { + assert.equal(minifyText(getProperty(nodes[i], p)), + minifyText(params.rawText ? path : translate(params.effectiveLang, p, path)), + p + ' is set as ' + minifyText(params.rawText ? path : translate(params.effectiveLang, p, path))); + }); + } + else { + //console.log(nodes[0]); + assert.equal(minifyText(getProperty(nodes[0], p)), + minifyText(params.rawText ? childPath[p] : translate(params.effectiveLang, p, childPath[p])), + p + ' is set as ' + translate(params.rawText ? childPath[p] : params.effectiveLang, p, childPath[p])); + } + } + //console.log(childPath); + }); + } + }); + +/* + if (params.text) { + test('text' + ' property is set as ' + JSON.stringify(params.text,null,2) + + (params.assign && params.assign.lang ? ' for ' + params.assign.lang : ''), function () { + expected = deepMap(deepcopy(params.text), params.text, minifyText); + noProperties = true; + assert.isObject(el.text, 'text property is an object'); + //console.log(JSON.stringify(e.detail, null, 2)); + //console.log(JSON.stringify(el.text, null, 2)); + for (p in expected) { + noProperties = false; + assert.deepEqual(deepMap(deepcopy(el.text[p]), el.text[p], minifyText), + params.rawText ? expected[p] : translate(params.effectiveLang, null, expected[p]), + 'text.' + p + ' property is set for ' + params.effectiveLang); + } + if (noProperties) { + assert.deepEqual(deepMap(deepcopy(el.text), el.text, minifyText), + expected, + 'text property is set'); + } + }); + } + + if (params.model) { + test('model' + ' property is set as ' + JSON.stringify(params.model,null,2) + + (params.assign && params.assign.lang ? ' for ' + params.assign.lang : ''), function () { + noProperties = true; + assert.isObject(el.model, 'model property is an object'); + for (p in params.model) { + noProperties = false; + //console.log('model.' + p + ' = ' + JSON.stringify(el.model[p])); + //console.log('expected model.' + p + ' = ' + JSON.stringify(translate(el.effectiveLang, null, params.model[p]))); + assert.deepEqual(el.model[p], + params.rawText ? params.model[p] : translate(params.effectiveLang, null, params.model[p]), + 'model.' + p + ' property is set for ' + params.effectiveLang); + } + if (noProperties) { + assert.deepEqual(el.model, params.model, 'model property is set'); + } + }); + } +*/ + + if (params.setup && params.localDOM) { + test('local DOM ' + (isSuppressingSuiteParams ? '{}' : JSON.stringify(params.localDOM, null, 2)) + ' is set' + + (params.assign && params.assign.lang ? ' for ' + params.assign.lang : ''), function () { + params.localDOM.forEach(function (childPath) { + var completeStatus; + var nodes = dom(getLocalDomRoot(el)).querySelectorAll(childPath.select); + assert.ok(nodes.length > 0, childPath.select + ' is defined'); + for (var p in childPath) { + if (p === 'select') { + continue; + } + //console.log(p + ' is set as ' + childPath[p]); + if (Array.isArray(childPath[p])) { + //console.log(nodes); + Array.prototype.forEach.call(childPath[p], function (path, i, a) { + assert.equal(minifyText(getProperty(nodes[i], p)), + minifyText(params.rawText ? path : translate(params.effectiveLang, p, path)), + p + ' is set as ' + minifyText(params.rawText ? path : translate(params.effectiveLang, p, path))); + }); + } + else { + //console.log(nodes[0]); + assert.equal(minifyText(getProperty(nodes[0], p)), + minifyText(params.rawText ? childPath[p] : translate(params.effectiveLang, p, childPath[p])), + p + ' is set as ' + translate(params.rawText ? childPath[p] : params.effectiveLang, p, childPath[p])); + } + } + //console.log(childPath); + }); + }); + } + + if (params.lightDOM) { + test('light DOM ' + (isSuppressingSuiteParams ? '{}' : JSON.stringify(params.lightDOM, null, 2)) + ' is set' + + (params.assign && params.assign.lang ? ' for ' + params.assign.lang : ''), function () { + params.lightDOM.forEach(function (childPath) { + var completeStatus; + var nodes = dom(el).querySelectorAll(childPath.select); + assert.ok(nodes.length > 0, childPath.select + ' is defined'); + for (var p in childPath) { + if (p === 'select') { + continue; + } + //console.log(p + ' is set as ' + childPath[p]); + if (Array.isArray(childPath[p])) { + //console.log(nodes); + Array.prototype.forEach.call(childPath[p], function (path, i, a) { + assert.equal(getProperty(nodes[i], p), translate(params.effectiveLang, p, path), p + ' is set as ' + translate(params.effectiveLang, p, path)); + }); + } + else { + //console.log(nodes[0]); + assert.equal(getProperty(nodes[0], p), translate(params.effectiveLang, p, childPath[p]), p + ' is set as ' + translate(params.effectiveLang, p, childPath[p])); + } + } + //console.log(childPath); + }); + }); + } + + (params.setup ? teardown : suiteTeardown)(function () { + restoreFixture(params.fixture); + }); + }); + }); +} diff --git a/wct.conf.json b/wct.conf.json index 247e327d..60aca62b 100644 --- a/wct.conf.json +++ b/wct.conf.json @@ -23,20 +23,10 @@ "platform": "Windows 10", "version": "17" }, - { - "browserName": "internet explorer", - "platform": "Windows 8.1", - "version": "11" - }, { "browserName": "safari", "platform": "OS X 10.13", "version": "12" - }, - { - "browserName": "safari", - "platform": "OS X 10.11", - "version": "9" } ] },