diff --git a/.circleci/config.yml b/.circleci/config.yml index d3a4cd0421..32a05cc9db 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,33 +1,33 @@ version: 2 jobs: - build: - docker: - - image: cimg/node:lts - steps: - - checkout - - restore_cache: - key: dependency-cache-{{ checksum "package.json" }} - - run: - name: Install modules - command: yarn install - - save_cache: - key: dependency-cache-{{ checksum "package.json" }} - paths: - - node_modules - - run: - name: Test - command: npm test - - store_artifacts: - path: test-results.xml + build: + docker: + - image: cimg/node:lts + steps: + - checkout + - restore_cache: + key: dependency-cache-{{ checksum "package.json" }} + - run: + name: Install modules + command: yarn install + - save_cache: + key: dependency-cache-{{ checksum "package.json" }} + paths: + - node_modules + - run: + name: Test + command: npm test + - store_artifacts: + path: test-results.xml ######################################################### ## Disables Config (Comment/Remove to re-enable CircleCI) ######################################################### workflows: - version: 2 - build-and-test: - jobs: - - build: - filters: - branches: - ignore: /.*/ \ No newline at end of file + version: 2 + build-and-test: + jobs: + - build: + filters: + branches: + ignore: /.*/ diff --git a/.esdoc.json b/.esdoc.json index 214fcb0f34..9d7ddd55f7 100644 --- a/.esdoc.json +++ b/.esdoc.json @@ -2,7 +2,10 @@ "source": "./src", "destination": "./docs", "plugins": [ - {"name": "esdoc-standard-plugin"}, - {"name": "esdoc-ecmascript-proposal-plugin", "option": {"all": true}} + { "name": "esdoc-standard-plugin" }, + { + "name": "esdoc-ecmascript-proposal-plugin", + "option": { "all": true } + } ] - } +} diff --git a/.eslintrc.json b/.eslintrc.json index 488a48047b..586597158c 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,22 +1,22 @@ { - "env": { - "browser": true, - "es2021": true, - "node": true - }, - "extends": [ - "eslint:recommended", - "plugin:mocha/recommended", - "eslint-config-prettier" - ], - "parserOptions": { - "ecmaVersion": "latest", - "sourceType": "module" - }, - "plugins": ["mocha"], - "rules": { - "mocha/max-top-level-suites": ["warn", { "limit": 5 }], - "mocha/no-setup-in-describe": "warn", - "no-loss-of-precision": "warn" - } + "env": { + "browser": true, + "es2021": true, + "node": true + }, + "extends": [ + "eslint:recommended", + "plugin:mocha/recommended", + "eslint-config-prettier" + ], + "parserOptions": { + "ecmaVersion": "latest", + "sourceType": "module" + }, + "plugins": ["mocha"], + "rules": { + "mocha/max-top-level-suites": ["warn", { "limit": 5 }], + "mocha/no-setup-in-describe": "warn", + "no-loss-of-precision": "warn" + } } diff --git a/.github/workflows/config.yml b/.github/workflows/config.yml index ab65e6faa4..05cfea7472 100644 --- a/.github/workflows/config.yml +++ b/.github/workflows/config.yml @@ -19,7 +19,7 @@ # with: # node-version: ${{ env.NODE_VERSION }} # cache: 'npm' - + # - name: Cache node modules # uses: actions/cache@v3 # with: @@ -27,7 +27,7 @@ # key: ${{ runner.os }}-node-${{ hashFiles('**/yarn.lock') }} # restore-keys: | # ${{ runner.os }}-node- - + # - name: Installing dependencies # if: steps.cache.outputs.cache-hit != 'true' # uses: borales/actions-yarn@v4 @@ -54,78 +54,78 @@ name: Build & Test on: push env: - NODE_VERSION: 18.x + NODE_VERSION: 18.x jobs: - setup: - runs-on: ubuntu-latest - steps: - - run: echo "Triggered by ${{ github.event_name }} event." - - - name: Check out repository code ${{ github.repository }} on ${{ github.ref }} - uses: actions/checkout@v3 - - - name: Set up Node.js ${{ env.NODE_VERSION }} - uses: actions/setup-node@v3 - with: - node-version: ${{ env.NODE_VERSION }} - cache: 'npm' - - - name: Cache node modules - uses: actions/cache@v3 - with: - path: node_modules - key: ${{ runner.os }}-node-${{ hashFiles('**/yarn.lock') }} - restore-keys: | - ${{ runner.os }}-node- - - - name: Installing dependencies - if: steps.cache.outputs.cache-hit != 'true' - uses: borales/actions-yarn@v4 - with: - cmd: install --frozen-lockfile - - - name: Lint - uses: borales/actions-yarn@v4 - with: - cmd: lint - - build: - needs: setup - runs-on: ubuntu-latest - steps: - - name: Check out repository code ${{ github.repository }} on ${{ github.ref }} - uses: actions/checkout@v3 - - - name: Restore node modules from cache - uses: actions/cache@v3 - with: - path: node_modules - key: ${{ runner.os }}-node-${{ hashFiles('**/yarn.lock') }} - restore-keys: | - ${{ runner.os }}-node- - - - name: Build - uses: borales/actions-yarn@v4 - with: - cmd: build - - test: - needs: setup - runs-on: ubuntu-latest - steps: - - name: Check out repository code ${{ github.repository }} on ${{ github.ref }} - uses: actions/checkout@v3 - - - name: Restore node modules from cache - uses: actions/cache@v3 - with: - path: node_modules - key: ${{ runner.os }}-node-${{ hashFiles('**/yarn.lock') }} - restore-keys: | - ${{ runner.os }}-node- - - - name: Test - uses: borales/actions-yarn@v4 - with: - cmd: test \ No newline at end of file + setup: + runs-on: ubuntu-latest + steps: + - run: echo "Triggered by ${{ github.event_name }} event." + + - name: Check out repository code ${{ github.repository }} on ${{ github.ref }} + uses: actions/checkout@v3 + + - name: Set up Node.js ${{ env.NODE_VERSION }} + uses: actions/setup-node@v3 + with: + node-version: ${{ env.NODE_VERSION }} + cache: 'npm' + + - name: Cache node modules + uses: actions/cache@v3 + with: + path: node_modules + key: ${{ runner.os }}-node-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + ${{ runner.os }}-node- + + - name: Installing dependencies + if: steps.cache.outputs.cache-hit != 'true' + uses: borales/actions-yarn@v4 + with: + cmd: install --frozen-lockfile + + - name: Lint + uses: borales/actions-yarn@v4 + with: + cmd: lint + + build: + needs: setup + runs-on: ubuntu-latest + steps: + - name: Check out repository code ${{ github.repository }} on ${{ github.ref }} + uses: actions/checkout@v3 + + - name: Restore node modules from cache + uses: actions/cache@v3 + with: + path: node_modules + key: ${{ runner.os }}-node-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + ${{ runner.os }}-node- + + - name: Build + uses: borales/actions-yarn@v4 + with: + cmd: build + + test: + needs: setup + runs-on: ubuntu-latest + steps: + - name: Check out repository code ${{ github.repository }} on ${{ github.ref }} + uses: actions/checkout@v3 + + - name: Restore node modules from cache + uses: actions/cache@v3 + with: + path: node_modules + key: ${{ runner.os }}-node-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + ${{ runner.os }}-node- + + - name: Test + uses: borales/actions-yarn@v4 + with: + cmd: test diff --git a/.mocharc.js b/.mocharc.js index 882cb62419..e894133b89 100644 --- a/.mocharc.js +++ b/.mocharc.js @@ -1,12 +1,12 @@ process.env.TZ = 'UTC'; module.exports = { - bail: true, - timeout: 20000, - exit: true, - require: [ - 'ts-node/register', - 'mock-local-storage', - 'jsdom-global/register' - ] + bail: true, + timeout: 20000, + exit: true, + require: [ + 'ts-node/register', + 'mock-local-storage', + 'jsdom-global/register', + ], }; diff --git a/.prettierrc b/.prettierrc index 96c36f53c9..cd93fd985c 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,4 +1,4 @@ { - "singleQuote": true, - "tabWidth": 4 + "singleQuote": true, + "tabWidth": 4 } diff --git a/.vscode/launch.json b/.vscode/launch.json index 1424e23bb8..06eea30c17 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -10,34 +10,32 @@ "name": "Mocha Tests", "program": "${workspaceFolder}/node_modules/mocha/bin/_mocha", "args": [ - "-b", - "-t", - "20000", - "--colors", - "${workspaceFolder}/src/**/*.unit.js", - "-t", - "30000" + "-b", + "-t", + "20000", + "--colors", + "${workspaceFolder}/src/**/*.unit.js", + "-t", + "30000" ], "internalConsoleOptions": "openOnSessionStart", - "skipFiles": [ - "/**" - ] - }, - { + "skipFiles": ["/**"] + }, + { "type": "node", "request": "launch", "name": "Launch Mocha Tests via Yarn", "runtimeExecutable": "yarn", "cwd": "${workspaceFolder}", - "runtimeArgs": ["test"], + "runtimeArgs": ["test"] }, { - "type": "node", - "request": "launch", - "name": "Mocha: current file", - "program": "${workspaceFolder}/node_modules/.bin/mocha", - "args": ["-b", "-t", "0", "'${file}'"], - "console": "integratedTerminal", - } + "type": "node", + "request": "launch", + "name": "Mocha: current file", + "program": "${workspaceFolder}/node_modules/.bin/mocha", + "args": ["-b", "-t", "0", "'${file}'"], + "console": "integratedTerminal" + } ] } diff --git a/_config.yml b/_config.yml index 413b816b0f..bdd79caae1 100644 --- a/_config.yml +++ b/_config.yml @@ -4,5 +4,18 @@ author: The Form.io Developers markdown: kramdown highlighter: rouge baseurl: /formio.js/ -exclude: [package.json, package-lock.json, circle.yml, gulpfile.js, node_modules, bower_components, src, test, gulp, lib, config, tsconfig.json] - +exclude: + [ + package.json, + package-lock.json, + circle.yml, + gulpfile.js, + node_modules, + bower_components, + src, + test, + gulp, + lib, + config, + tsconfig.json, + ] diff --git a/gulpfile.js b/gulpfile.js index aad43eaec5..2aaa353850 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -10,97 +10,139 @@ const clean = require('gulp-clean'); // Clean lib folder. gulp.task('clean:dist', () => { - return gulp.src('dist', { read: false, allowEmpty: true }).pipe(clean()); + return gulp.src('dist', { read: false, allowEmpty: true }).pipe(clean()); }); gulp.task('clean:lib', () => { - return gulp.src('lib', { read: false, allowEmpty: true }).pipe(clean()); + return gulp.src('lib', { read: false, allowEmpty: true }).pipe(clean()); }); gulp.task('clean', gulp.parallel('clean:dist', 'clean:lib')); // Move font-awesome fonts into dist folder. gulp.task('builder-fonts', function builderFonts() { - return gulp.src('./node_modules/bootstrap-icons/font/fonts/*').pipe(gulp.dest('dist/fonts')); + return gulp + .src('./node_modules/bootstrap-icons/font/fonts/*') + .pipe(gulp.dest('dist/fonts')); }); // Generate styles const compileStyles = (styles, file) => { - const sassFilter = filter('**/*.scss', { restore: true }); - return gulp.src(styles) - .pipe(sassFilter) - .pipe(sass().on('error', sass.logError)) - .pipe(sassFilter.restore) - .pipe(concat(`${file}.css`)) - .pipe(replace(/\.\.\/\.\.\/icons\/\/?/g, 'icons/')) - /* eslint-disable quotes */ - .pipe(replace('icons/cross.svg', `'data:image/svg+xml;charset=utf8,%3Csvg width="21" height="21" viewBox="0 0 21 21" xmlns="http://www.w3.org/2000/svg"%3E%3Cg fill="%23FFF" fill-rule="evenodd"%3E%3Cpath d="M2.592.044l18.364 18.364-2.548 2.548L.044 2.592z"/%3E%3Cpath d="M0 18.364L18.364 0l2.548 2.548L2.548 20.912z"/%3E%3C/g%3E%3C/svg%3E'`)) - .pipe(replace('icons/cross-inverse.svg', `'data:image/svg+xml;charset=utf8,%3Csvg width="21" height="21" viewBox="0 0 21 21" xmlns="http://www.w3.org/2000/svg"%3E%3Cg fill="%23000" fill-rule="evenodd"%3E%3Cpath d="M2.592.044l18.364 18.364-2.548 2.548L.044 2.592z"/%3E%3Cpath d="M0 18.364L18.364 0l2.548 2.548L2.548 20.912z"/%3E%3C/g%3E%3C/svg%3E'`)) - /* eslint-enable quotes */ - .pipe(replace(/\.\.\/fonts\/\/?/g, 'fonts/')) - .pipe(gulp.dest('dist')) - .pipe(rename(`${file}.min.css`)) - .pipe(cleanCSS({ compatibility: 'ie8' })) - .pipe(gulp.dest('dist')); + const sassFilter = filter('**/*.scss', { restore: true }); + return ( + gulp + .src(styles) + .pipe(sassFilter) + .pipe(sass().on('error', sass.logError)) + .pipe(sassFilter.restore) + .pipe(concat(`${file}.css`)) + .pipe(replace(/\.\.\/\.\.\/icons\/\/?/g, 'icons/')) + /* eslint-disable quotes */ + .pipe( + replace( + 'icons/cross.svg', + `'data:image/svg+xml;charset=utf8,%3Csvg width="21" height="21" viewBox="0 0 21 21" xmlns="http://www.w3.org/2000/svg"%3E%3Cg fill="%23FFF" fill-rule="evenodd"%3E%3Cpath d="M2.592.044l18.364 18.364-2.548 2.548L.044 2.592z"/%3E%3Cpath d="M0 18.364L18.364 0l2.548 2.548L2.548 20.912z"/%3E%3C/g%3E%3C/svg%3E'`, + ), + ) + .pipe( + replace( + 'icons/cross-inverse.svg', + `'data:image/svg+xml;charset=utf8,%3Csvg width="21" height="21" viewBox="0 0 21 21" xmlns="http://www.w3.org/2000/svg"%3E%3Cg fill="%23000" fill-rule="evenodd"%3E%3Cpath d="M2.592.044l18.364 18.364-2.548 2.548L.044 2.592z"/%3E%3Cpath d="M0 18.364L18.364 0l2.548 2.548L2.548 20.912z"/%3E%3C/g%3E%3C/svg%3E'`, + ), + ) + /* eslint-enable quotes */ + .pipe(replace(/\.\.\/fonts\/\/?/g, 'fonts/')) + .pipe(gulp.dest('dist')) + .pipe(rename(`${file}.min.css`)) + .pipe(cleanCSS({ compatibility: 'ie8' })) + .pipe(gulp.dest('dist')) + ); }; gulp.task('styles-embed', function embedStyles() { - return compileStyles([ - './src/sass/formio.embed.scss' - ], 'formio.embed'); + return compileStyles(['./src/sass/formio.embed.scss'], 'formio.embed'); }); gulp.task('styles-form', function formStyles() { - return compileStyles([ - './node_modules/@formio/choices.js/public/assets/styles/choices.css', - './node_modules/tippy.js/dist/tippy.css', - './node_modules/dialog-polyfill/dialog-polyfill.css', - './src/sass/formio.form.scss' - ], 'formio.form'); + return compileStyles( + [ + './node_modules/@formio/choices.js/public/assets/styles/choices.css', + './node_modules/tippy.js/dist/tippy.css', + './node_modules/dialog-polyfill/dialog-polyfill.css', + './src/sass/formio.form.scss', + ], + 'formio.form', + ); }); gulp.task('styles-builder', function builderStyles() { - return compileStyles([ - './node_modules/@formio/choices.js/public/assets/styles/choices.css', - './node_modules/tippy.js/dist/tippy.css', - './node_modules/dialog-polyfill/dialog-polyfill.css', - './node_modules/dragula/dist/dragula.css', - './src/sass/formio.form.scss', - './src/sass/formio.form.builder.scss' - ], 'formio.builder'); + return compileStyles( + [ + './node_modules/@formio/choices.js/public/assets/styles/choices.css', + './node_modules/tippy.js/dist/tippy.css', + './node_modules/dialog-polyfill/dialog-polyfill.css', + './node_modules/dragula/dist/dragula.css', + './src/sass/formio.form.scss', + './src/sass/formio.form.builder.scss', + ], + 'formio.builder', + ); }); -gulp.task('styles-full', gulp.series('builder-fonts', function fullStyles() { - return compileStyles([ - './node_modules/@formio/choices.js/public/assets/styles/choices.css', - './node_modules/tippy.js/dist/tippy.css', - './node_modules/dialog-polyfill/dialog-polyfill.css', - './node_modules/dragula/dist/dragula.css', - './node_modules/bootstrap-icons/font/bootstrap-icons.css', - './src/sass/formio.form.scss', - './src/sass/formio.form.builder.scss' - ], 'formio.full'); -})); +gulp.task( + 'styles-full', + gulp.series('builder-fonts', function fullStyles() { + return compileStyles( + [ + './node_modules/@formio/choices.js/public/assets/styles/choices.css', + './node_modules/tippy.js/dist/tippy.css', + './node_modules/dialog-polyfill/dialog-polyfill.css', + './node_modules/dragula/dist/dragula.css', + './node_modules/bootstrap-icons/font/bootstrap-icons.css', + './src/sass/formio.form.scss', + './src/sass/formio.form.builder.scss', + ], + 'formio.full', + ); + }), +); -gulp.task('clean:embed-css', () => gulp.src('./dist/formio.embed.css', { read: false, allowEmpty: true }).pipe(clean())); -gulp.task('embed-css', () => gulp.src('./dist/formio.embed.min.css').pipe(rename('formio.embed.css')).pipe(gulp.dest('./dist'))); -gulp.task('clean:embed-js', () => gulp.src('./dist/formio.embed.js', { read: false, allowEmpty: true }).pipe(clean())); -gulp.task('embed-js', () => gulp.src('./dist/formio.embed.min.js').pipe(rename('formio.embed.js')).pipe(gulp.dest('./dist'))); +gulp.task('clean:embed-css', () => + gulp + .src('./dist/formio.embed.css', { read: false, allowEmpty: true }) + .pipe(clean()), +); +gulp.task('embed-css', () => + gulp + .src('./dist/formio.embed.min.css') + .pipe(rename('formio.embed.css')) + .pipe(gulp.dest('./dist')), +); +gulp.task('clean:embed-js', () => + gulp + .src('./dist/formio.embed.js', { read: false, allowEmpty: true }) + .pipe(clean()), +); +gulp.task('embed-js', () => + gulp + .src('./dist/formio.embed.min.js') + .pipe(rename('formio.embed.js')) + .pipe(gulp.dest('./dist')), +); // Copy over the moment-timezones to the resource folder. -gulp.task('timezones', () => gulp.src('./node_modules/moment-timezone/data/packed/latest.json').pipe(gulp.dest('./resources'))); +gulp.task('timezones', () => + gulp + .src('./node_modules/moment-timezone/data/packed/latest.json') + .pipe(gulp.dest('./resources')), +); // Create a new build. -gulp.task('build', gulp.series( - gulp.parallel( - 'timezones' - ), - gulp.parallel( - 'styles-embed', - 'styles-form', - 'styles-builder', - 'styles-full' - ), - gulp.parallel( - 'clean:embed-css', - 'clean:embed-js' - ), - gulp.parallel( - 'embed-css', - 'embed-js' - ) -)); +gulp.task( + 'build', + gulp.series( + gulp.parallel('timezones'), + gulp.parallel( + 'styles-embed', + 'styles-form', + 'styles-builder', + 'styles-full', + ), + gulp.parallel('clean:embed-css', 'clean:embed-js'), + gulp.parallel('embed-css', 'embed-js'), + ), +); diff --git a/libpackage.js b/libpackage.js index 974dfd1827..e5d868a5e7 100644 --- a/libpackage.js +++ b/libpackage.js @@ -1,8 +1,14 @@ const fs = require('fs'); const path = require('path'); -fs.writeFileSync(path.join(__dirname, 'lib', 'cjs', 'package.json'), `{ +fs.writeFileSync( + path.join(__dirname, 'lib', 'cjs', 'package.json'), + `{ "type": "commonjs" -}`); -fs.writeFileSync(path.join(__dirname, 'lib', 'mjs', 'package.json'), `{ +}`, +); +fs.writeFileSync( + path.join(__dirname, 'lib', 'mjs', 'package.json'), + `{ "type": "module" -}`); \ No newline at end of file +}`, +); diff --git a/resources/latest.json b/resources/latest.json index 2ae3340fe5..c0b07f5188 100644 --- a/resources/latest.json +++ b/resources/latest.json @@ -1,852 +1,852 @@ { - "version": "2023c", - "zones": [ - "Africa/Abidjan|LMT GMT|g.8 0|01|-2ldXH.Q|48e5", - "Africa/Nairobi|LMT +0230 EAT +0245|-2r.g -2u -30 -2J|012132|-2ua2r.g N6nV.g 3Fbu h1cu dzbJ|47e5", - "Africa/Algiers|LMT PMT WET WEST CET CEST|-c.c -9.l 0 -10 -10 -20|01232323232323232454542423234542324|-3bQ0c.c MDA2.P cNb9.l HA0 19A0 1iM0 11c0 1oo0 Wo0 1rc0 QM0 1EM0 UM0 DA0 Imo0 rd0 De0 9Xz0 1fb0 1ap0 16K0 2yo0 mEp0 hwL0 jxA0 11A0 dDd0 17b0 11B0 1cN0 2Dy0 1cN0 1fB0 1cL0|26e5", - "Africa/Lagos|LMT GMT +0030 WAT|-d.z 0 -u -10|01023|-2B40d.z 7iod.z dnXK.p dLzH.z|17e6", - "Africa/Bissau|LMT -01 GMT|12.k 10 0|012|-2ldX0 2xoo0|39e4", - "Africa/Maputo|LMT CAT|-2a.k -20|01|-2GJea.k|26e5", - "Africa/Cairo|LMT EET EEST|-25.9 -20 -30|01212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-2MBC5.9 1AQM5.9 vb0 1ip0 11z0 1iN0 1nz0 12p0 1pz0 10N0 1pz0 16p0 1jz0 s3d0 Vz0 1oN0 11b0 1oO0 10N0 1pz0 10N0 1pb0 10N0 1pb0 10N0 1pb0 10N0 1pz0 10N0 1pb0 10N0 1pb0 11d0 1oL0 11d0 1pb0 11d0 1oL0 11d0 1oL0 11d0 1oL0 11d0 1pb0 11d0 1oL0 11d0 1oL0 11d0 1oL0 11d0 1pb0 11d0 1oL0 11d0 1oL0 11d0 1oL0 11d0 1pb0 11d0 1oL0 11d0 1WL0 rd0 1Rz0 wp0 1pb0 11d0 1oL0 11d0 1oL0 11d0 1oL0 11d0 1pb0 11d0 1qL0 Xd0 1oL0 11d0 1oL0 11d0 1pb0 11d0 1oL0 11d0 1oL0 11d0 1ny0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 11z0 1o10 11z0 1o10 WL0 1qN0 Rb0 1wp0 On0 1zd0 Lz0 1EN0 Fb0 c10 8n0 8Nd0 gL0 e10 mn0 kSp0 1cL0 1cN0 1fz0 1a10 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1a10 1fz0|15e6", - "Africa/Casablanca|LMT +00 +01|u.k 0 -10|01212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212|-2gMnt.E 130Lt.E rb0 Dd0 dVb0 b6p0 TX0 EoB0 LL0 gnd0 rz0 43d0 AL0 1Nd0 XX0 1Cp0 pz0 dEp0 4mn0 SyN0 AL0 1Nd0 wn0 1FB0 Db0 1zd0 Lz0 1Nf0 wM0 co0 go0 1o00 s00 dA0 vc0 11A0 A00 e00 y00 11A0 uM0 e00 Dc0 11A0 s00 e00 IM0 WM0 mo0 gM0 LA0 WM0 jA0 e00 28M0 e00 2600 gM0 2600 e00 2600 gM0 2600 e00 28M0 e00 2600 gM0 2600 e00 28M0 e00 2600 gM0 2600 e00 2600 gM0 2600 e00 28M0 e00 2600 gM0 2600 e00 2600 gM0 2600 gM0 2600 e00 2600 gM0 2600 e00 28M0 e00 2600 gM0 2600 e00 2600 gM0 2600 gM0 2600 e00 2600 gM0 2600 e00 28M0 e00 2600 gM0 2600 e00 2600 gM0 2600 gM0 2600 e00 2600 gM0 2600 e00 28M0 e00 2600 gM0 2600 e00 2600 gM0 2600 gM0 2600 e00 2600 gM0 2600 e00 28M0 e00 2600 gM0 2600 e00 2600 gM0 2600 gM0 2600 e00 2600 gM0 2600 e00 28M0 e00 2600 gM0 2600 e00 2600 gM0 2600 gM0 2600 e00 2600 gM0 2600 e00 28M0 e00 2600 gM0 2600 e00 2600 gM0 2600 e00 28M0 e00 2600 gM0 2600 e00 28M0 e00 2600 gM0|32e5", - "Africa/Ceuta|LMT WET WEST CET CEST|l.g 0 -10 -10 -20|0121212121212121212121343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343|-2M0M0 GdX0 11z0 drd0 18p0 3HX0 17d0 1fz0 1a10 1io0 1a00 1y7o0 LL0 gnd0 rz0 43d0 AL0 1Nd0 XX0 1Cp0 pz0 dEp0 4VB0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|85e3", - "Africa/El_Aaiun|LMT -01 +00 +01|Q.M 10 0 -10|012323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323|-1rDz7.c 1GVA7.c 6L0 AL0 1Nd0 XX0 1Cp0 pz0 1cBB0 AL0 1Nd0 wn0 1FB0 Db0 1zd0 Lz0 1Nf0 wM0 co0 go0 1o00 s00 dA0 vc0 11A0 A00 e00 y00 11A0 uM0 e00 Dc0 11A0 s00 e00 IM0 WM0 mo0 gM0 LA0 WM0 jA0 e00 28M0 e00 2600 gM0 2600 e00 2600 gM0 2600 e00 28M0 e00 2600 gM0 2600 e00 28M0 e00 2600 gM0 2600 e00 2600 gM0 2600 e00 28M0 e00 2600 gM0 2600 e00 2600 gM0 2600 gM0 2600 e00 2600 gM0 2600 e00 28M0 e00 2600 gM0 2600 e00 2600 gM0 2600 gM0 2600 e00 2600 gM0 2600 e00 28M0 e00 2600 gM0 2600 e00 2600 gM0 2600 gM0 2600 e00 2600 gM0 2600 e00 28M0 e00 2600 gM0 2600 e00 2600 gM0 2600 gM0 2600 e00 2600 gM0 2600 e00 28M0 e00 2600 gM0 2600 e00 2600 gM0 2600 gM0 2600 e00 2600 gM0 2600 e00 28M0 e00 2600 gM0 2600 e00 2600 gM0 2600 gM0 2600 e00 2600 gM0 2600 e00 28M0 e00 2600 gM0 2600 e00 2600 gM0 2600 e00 28M0 e00 2600 gM0 2600 e00 28M0 e00 2600 gM0|20e4", - "Africa/Johannesburg|LMT SAST SAST SAST|-1Q -1u -20 -30|0123232|-39EpQ qTcm 1Ajdu 1cL0 1cN0 1cL0|84e5", - "Africa/Juba|LMT CAT CAST EAT|-26.s -20 -30 -30|012121212121212121212121212121212131|-1yW26.s 1zK06.s 16L0 1iN0 17b0 1jd0 17b0 1ip0 17z0 1i10 17X0 1hB0 18n0 1hd0 19b0 1gp0 19z0 1iN0 17b0 1ip0 17z0 1i10 18n0 1hd0 18L0 1gN0 19b0 1gp0 19z0 1iN0 17z0 1i10 17X0 yGd0 PeX0|", - "Africa/Khartoum|LMT CAT CAST EAT|-2a.8 -20 -30 -30|012121212121212121212121212121212131|-1yW2a.8 1zK0a.8 16L0 1iN0 17b0 1jd0 17b0 1ip0 17z0 1i10 17X0 1hB0 18n0 1hd0 19b0 1gp0 19z0 1iN0 17b0 1ip0 17z0 1i10 18n0 1hd0 18L0 1gN0 19b0 1gp0 19z0 1iN0 17z0 1i10 17X0 yGd0 HjL0|51e5", - "Africa/Monrovia|LMT MMT MMT GMT|H.8 H.8 I.u 0|0123|-3ygng.Q 1usM0 28G01.m|11e5", - "Africa/Ndjamena|LMT WAT WAST|-10.c -10 -20|0121|-2le10.c 2J3c0.c Wn0|13e5", - "Africa/Sao_Tome|LMT LMT GMT WAT|-q.U A.J 0 -10|01232|-3tooq.U 18aoq.U 4i6N0 2q00|", - "Africa/Tripoli|LMT CET CEST EET|-Q.I -10 -20 -20|012121213121212121212121213123123|-21JcQ.I 1hnBQ.I vx0 4iP0 xx0 4eN0 Bb0 7ip0 U0n0 A10 1db0 1cN0 1db0 1dd0 1db0 1eN0 1bb0 1e10 1cL0 1c10 1db0 1dd0 1db0 1cN0 1db0 1q10 fAn0 1ep0 1db0 AKq0 TA0 1o00|11e5", - "Africa/Tunis|LMT PMT CET CEST|-E.I -9.l -10 -20|01232323232323232323232323232323232|-3zO0E.I 1cBAv.n 18pa9.l 1qM0 DA0 3Tc0 11B0 1ze0 WM0 7z0 3d0 14L0 1cN0 1f90 1ar0 16J0 1gXB0 WM0 1rA0 11c0 nwo0 Ko0 1cM0 1cM0 1rA0 10M0 zuM0 10N0 1aN0 1qM0 WM0 1qM0 11A0 1o00|20e5", - "Africa/Windhoek|LMT +0130 SAST SAST CAT WAT|-18.o -1u -20 -30 -20 -10|012324545454545454545454545454545454545454545454545454|-39Ep8.o qTbC.o 1Ajdu 1cL0 1SqL0 9Io0 16P0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0|32e4", - "America/Adak|LMT LMT NST NWT NPT BST BDT AHST HST HDT|-cd.m bK.C b0 a0 a0 b0 a0 a0 a0 90|01234256565656565656565656565656565678989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898|-48Pzs.L 1jVzf.p 1EX1d.m 8wW0 iB0 Qlb0 52O0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 cm0 10q0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|326", - "America/Anchorage|LMT LMT AST AWT APT AHST AHDT YST AKST AKDT|-e0.o 9X.A a0 90 90 a0 90 90 90 80|01234256565656565656565656565656565678989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898|-48Pzs.L 1jVxs.n 1EX20.o 8wX0 iA0 Qlb0 52O0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 cm0 10q0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|30e4", - "America/Puerto_Rico|LMT AST AWT APT|4o.p 40 30 30|01231|-2Qi7z.z 1IUbz.z 7XT0 iu0|24e5", - "America/Araguaina|LMT -03 -02|3c.M 30 20|0121212121212121212121212121212121212121212121212121|-2glwL.c HdKL.c 1cc0 1e10 1bX0 Ezd0 So0 1vA0 Mn0 1BB0 ML0 1BB0 zX0 qe10 xb0 2ep0 nz0 1C10 zX0 1C10 LX0 1C10 Mn0 H210 Rb0 1tB0 IL0 1Fd0 FX0 1EN0 FX0 1HB0 Lz0 dMN0 Lz0 1zd0 Rb0 1wN0 Wn0 1tB0 Rb0 1tB0 WL0 1tB0 Rb0 1zd0 On0 1HB0 FX0 ny10 Lz0|14e4", - "America/Argentina/Buenos_Aires|LMT CMT -04 -03 -02|3R.M 4g.M 40 30 20|012323232323232323232323232323232323232323234343434343434343|-331U6.c 125cn pKnH.c Mn0 1iN0 Tb0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 MN0 2jz0 MN0 4lX0 u10 5Lb0 1pB0 Fnz0 u10 uL0 1vd0 SL0 1vd0 SL0 1vd0 17z0 1cN0 1fz0 1cN0 1cL0 1cN0 asn0 Db0 zvd0 Bz0 1tB0 TX0 1wp0 Rb0 1wp0 Rb0 1wp0 TX0 A4p0 uL0 1qN0 WL0|", - "America/Argentina/Catamarca|LMT CMT -04 -03 -02|4n.8 4g.M 40 30 20|012323232323232323232323232323232323232323234343434243432343|-331TA.Q 125bR.E pKnH.c Mn0 1iN0 Tb0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 MN0 2jz0 MN0 4lX0 u10 5Lb0 1pB0 Fnz0 u10 uL0 1vd0 SL0 1vd0 SL0 1vd0 17z0 1cN0 1fz0 1cN0 1cL0 1cN0 asn0 Db0 zvd0 Bz0 1tB0 TX0 1wp0 Rb0 1wq0 Ra0 1wp0 TX0 rlB0 7B0 8zb0 uL0|", - "America/Argentina/Cordoba|LMT CMT -04 -03 -02|4g.M 4g.M 40 30 20|012323232323232323232323232323232323232323234343434243434343|-331TH.c 125c0 pKnH.c Mn0 1iN0 Tb0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 MN0 2jz0 MN0 4lX0 u10 5Lb0 1pB0 Fnz0 u10 uL0 1vd0 SL0 1vd0 SL0 1vd0 17z0 1cN0 1fz0 1cN0 1cL0 1cN0 asn0 Db0 zvd0 Bz0 1tB0 TX0 1wp0 Rb0 1wq0 Ra0 1wp0 TX0 A4p0 uL0 1qN0 WL0|", - "America/Argentina/Jujuy|LMT CMT -04 -03 -02|4l.c 4g.M 40 30 20|0123232323232323232323232323232323232323232343434232434343|-331TC.M 125bT.A pKnH.c Mn0 1iN0 Tb0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 MN0 2jz0 MN0 4lX0 u10 5Lb0 1pB0 Fnz0 u10 uL0 1vd0 SL0 1vd0 SL0 1vd0 17z0 1cN0 1fz0 1cN0 1cL0 1cN0 asn0 Db0 zvd0 Bz0 1tB0 TX0 1ze0 TX0 1ld0 WK0 1wp0 TX0 A4p0 uL0|", - "America/Argentina/La_Rioja|LMT CMT -04 -03 -02|4r.o 4g.M 40 30 20|0123232323232323232323232323232323232323232343434342343432343|-331Tw.A 125bN.o pKnH.c Mn0 1iN0 Tb0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 MN0 2jz0 MN0 4lX0 u10 5Lb0 1pB0 Fnz0 u10 uL0 1vd0 SL0 1vd0 SL0 1vd0 17z0 1cN0 1fz0 1cN0 1cL0 1cN0 asn0 Db0 zvd0 Bz0 1tB0 TX0 1wp0 Qn0 qO0 16n0 Rb0 1wp0 TX0 rlB0 7B0 8zb0 uL0|", - "America/Argentina/Mendoza|LMT CMT -04 -03 -02|4z.g 4g.M 40 30 20|012323232323232323232323232323232323232323234343423232432343|-331To.I 125bF.w pKnH.c Mn0 1iN0 Tb0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 MN0 2jz0 MN0 4lX0 u10 5Lb0 1pB0 Fnz0 u10 uL0 1vd0 SL0 1vd0 SL0 1vd0 17z0 1cN0 1fz0 1cN0 1cL0 1cN0 asn0 Db0 zvd0 Bz0 1tB0 TX0 1u20 SL0 1vd0 Tb0 1wp0 TW0 ri10 Op0 7TX0 uL0|", - "America/Argentina/Rio_Gallegos|LMT CMT -04 -03 -02|4A.Q 4g.M 40 30 20|012323232323232323232323232323232323232323234343434343432343|-331Tn.8 125bD.U pKnH.c Mn0 1iN0 Tb0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 MN0 2jz0 MN0 4lX0 u10 5Lb0 1pB0 Fnz0 u10 uL0 1vd0 SL0 1vd0 SL0 1vd0 17z0 1cN0 1fz0 1cN0 1cL0 1cN0 asn0 Db0 zvd0 Bz0 1tB0 TX0 1wp0 Rb0 1wp0 Rb0 1wp0 TX0 rlB0 7B0 8zb0 uL0|", - "America/Argentina/Salta|LMT CMT -04 -03 -02|4l.E 4g.M 40 30 20|0123232323232323232323232323232323232323232343434342434343|-331TC.k 125bT.8 pKnH.c Mn0 1iN0 Tb0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 MN0 2jz0 MN0 4lX0 u10 5Lb0 1pB0 Fnz0 u10 uL0 1vd0 SL0 1vd0 SL0 1vd0 17z0 1cN0 1fz0 1cN0 1cL0 1cN0 asn0 Db0 zvd0 Bz0 1tB0 TX0 1wp0 Rb0 1wq0 Ra0 1wp0 TX0 A4p0 uL0|", - "America/Argentina/San_Juan|LMT CMT -04 -03 -02|4y.4 4g.M 40 30 20|0123232323232323232323232323232323232323232343434342343432343|-331Tp.U 125bG.I pKnH.c Mn0 1iN0 Tb0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 MN0 2jz0 MN0 4lX0 u10 5Lb0 1pB0 Fnz0 u10 uL0 1vd0 SL0 1vd0 SL0 1vd0 17z0 1cN0 1fz0 1cN0 1cL0 1cN0 asn0 Db0 zvd0 Bz0 1tB0 TX0 1wp0 Qn0 qO0 16n0 Rb0 1wp0 TX0 rld0 m10 8lb0 uL0|", - "America/Argentina/San_Luis|LMT CMT -04 -03 -02|4p.o 4g.M 40 30 20|0123232323232323232323232323232323232323232343434232323432323|-331Ty.A 125bP.o pKnH.c Mn0 1iN0 Tb0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 MN0 2jz0 MN0 4lX0 u10 5Lb0 1pB0 Fnz0 u10 uL0 1vd0 SL0 1vd0 SL0 1vd0 17z0 1cN0 1fz0 1cN0 1cL0 1cN0 asn0 Db0 zvd0 Bz0 1tB0 XX0 1q20 SL0 AN0 vDb0 m10 8lb0 8L0 jd0 1qN0 WL0 1qN0|", - "America/Argentina/Tucuman|LMT CMT -04 -03 -02|4k.Q 4g.M 40 30 20|01232323232323232323232323232323232323232323434343424343234343|-331TD.8 125bT.U pKnH.c Mn0 1iN0 Tb0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 MN0 2jz0 MN0 4lX0 u10 5Lb0 1pB0 Fnz0 u10 uL0 1vd0 SL0 1vd0 SL0 1vd0 17z0 1cN0 1fz0 1cN0 1cL0 1cN0 asn0 Db0 zvd0 Bz0 1tB0 TX0 1wp0 Rb0 1wq0 Ra0 1wp0 TX0 rlB0 4N0 8BX0 uL0 1qN0 WL0|", - "America/Argentina/Ushuaia|LMT CMT -04 -03 -02|4x.c 4g.M 40 30 20|012323232323232323232323232323232323232323234343434343432343|-331Tq.M 125bH.A pKnH.c Mn0 1iN0 Tb0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 MN0 2jz0 MN0 4lX0 u10 5Lb0 1pB0 Fnz0 u10 uL0 1vd0 SL0 1vd0 SL0 1vd0 17z0 1cN0 1fz0 1cN0 1cL0 1cN0 asn0 Db0 zvd0 Bz0 1tB0 TX0 1wp0 Rb0 1wp0 Rb0 1wp0 TX0 rkN0 8p0 8zb0 uL0|", - "America/Asuncion|LMT AMT -04 -03|3O.E 3O.E 40 30|0123232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323|-3eLw9.k 1FGo0 1DKM9.k 3CL0 3Dd0 10L0 1pB0 10n0 1pB0 10n0 1pB0 1cL0 1dd0 1db0 1dd0 1cL0 1dd0 1cL0 1dd0 1cL0 1dd0 1db0 1dd0 1cL0 1dd0 1cL0 1dd0 1cL0 1dd0 1db0 1dd0 1cL0 1lB0 14n0 1dd0 1cL0 1fd0 WL0 1rd0 1aL0 1dB0 Xz0 1qp0 Xb0 1qN0 10L0 1rB0 TX0 1tB0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1qN0 1cL0 WN0 1qL0 11B0 1nX0 1ip0 WL0 1qN0 WL0 1qN0 WL0 1tB0 TX0 1tB0 TX0 1tB0 19X0 1a10 1fz0 1a10 1fz0 1cN0 17b0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 17b0 1ip0|28e5", - "America/Panama|LMT CMT EST|5i.8 5j.A 50|012|-3eLuF.Q Iy01.s|15e5", - "America/Bahia_Banderas|LMT MST CST MDT PST CDT|71 70 60 60 80 50|0121312141313131313131313131313131313152525252525252525252525252|-1UQF0 deL0 8lc0 17c0 10M0 1dd0 otX0 gmN0 P2N0 13Vd0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 1fB0 WL0 1fB0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nW0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0|84e3", - "America/Bahia|LMT -03 -02|2y.4 30 20|01212121212121212121212121212121212121212121212121212121212121|-2glxp.U HdLp.U 1cc0 1e10 1bX0 Ezd0 So0 1vA0 Mn0 1BB0 ML0 1BB0 zX0 qe10 xb0 2ep0 nz0 1C10 zX0 1C10 LX0 1C10 Mn0 H210 Rb0 1tB0 IL0 1Fd0 FX0 1EN0 FX0 1HB0 Lz0 1EN0 Lz0 1C10 IL0 1HB0 Db0 1HB0 On0 1zd0 On0 1zd0 Lz0 1zd0 Rb0 1wN0 Wn0 1tB0 Rb0 1tB0 WL0 1tB0 Rb0 1zd0 On0 1HB0 FX0 l5B0 Rb0|27e5", - "America/Barbados|LMT AST ADT -0330|3W.t 40 30 3u|0121213121212121|-2m4k1.v 1eAN1.v RB0 1Bz0 Op0 1rb0 11d0 1jJc0 IL0 1ip0 17b0 1ip0 17b0 1ld0 13b0|28e4", - "America/Belem|LMT -03 -02|3d.U 30 20|012121212121212121212121212121|-2glwK.4 HdKK.4 1cc0 1e10 1bX0 Ezd0 So0 1vA0 Mn0 1BB0 ML0 1BB0 zX0 qe10 xb0 2ep0 nz0 1C10 zX0 1C10 LX0 1C10 Mn0 H210 Rb0 1tB0 IL0 1Fd0 FX0|20e5", - "America/Belize|LMT CST -0530 CWT CPT CDT|5Q.M 60 5u 50 50 50|012121212121212121212121212121212121212121212121213412121212121212121212121212121212121212121215151|-2kBu7.c fPA7.c Onu 1zcu Rbu 1wou Rbu 1wou Rbu 1zcu Onu 1zcu Onu 1zcu Rbu 1wou Rbu 1wou Rbu 1wou Rbu 1zcu Onu 1zcu Onu 1zcu Rbu 1wou Rbu 1wou Rbu 1zcu Onu 1zcu Onu 1zcu Onu 1zcu Rbu 1wou Rbu 1wou Rbu 1zcu Onu 1zcu Onu 1zcu Rbu Rcu 7Bt0 Ni0 4nd0 Rbu 1wou Rbu 1wou Rbu 1zcu Onu 1zcu Onu 1zcu Rbu 1wou Rbu 1wou Rbu 1wou Rbu 1zcu Onu 1zcu Onu 1zcu Rbu 1wou Rbu 1wou Rbu 1zcu Onu 1zcu Onu 1zcu Onu 1zcu Rbu 1wou Rbu 1wou Rbu 1zcu Onu e9Au qn0 lxB0 mn0|57e3", - "America/Boa_Vista|LMT -04 -03|42.E 40 30|0121212121212121212121212121212121|-2glvV.k HdKV.k 1cc0 1e10 1bX0 Ezd0 So0 1vA0 Mn0 1BB0 ML0 1BB0 zX0 qe10 xb0 2ep0 nz0 1C10 zX0 1C10 LX0 1C10 Mn0 H210 Rb0 1tB0 IL0 1Fd0 FX0 smp0 WL0 1tB0 2L0|62e2", - "America/Bogota|LMT BMT -05 -04|4U.g 4U.g 50 40|01232|-3sTv3.I 1eIo0 38yo3.I 1PX0|90e5", - "America/Boise|LMT PST PDT MST MWT MPT MDT|7I.N 80 70 70 60 60 60|01212134536363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363|-3tFE0 1nEe0 1nX0 11B0 1nX0 8C10 JCL0 8x20 ix0 QwN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 Dd0 1Kn0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|21e4", - "America/Cambridge_Bay|-00 MST MWT MPT MDT CST CDT EST|0 70 60 60 60 60 50 50|012314141414141414141414141414141414141414141414141414141414567541414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141|-21Jc0 RO90 8x20 ix0 14HB0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11A0 1nX0 2K0 WQ0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|15e2", - "America/Campo_Grande|LMT -04 -03|3C.s 40 30|01212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-2glwl.w HdLl.w 1cc0 1e10 1bX0 Ezd0 So0 1vA0 Mn0 1BB0 ML0 1BB0 zX0 qe10 xb0 2ep0 nz0 1C10 zX0 1C10 LX0 1C10 Mn0 H210 Rb0 1tB0 IL0 1Fd0 FX0 1EN0 FX0 1HB0 Lz0 1EN0 Lz0 1C10 IL0 1HB0 Db0 1HB0 On0 1zd0 On0 1zd0 Lz0 1zd0 Rb0 1wN0 Wn0 1tB0 Rb0 1tB0 WL0 1tB0 Rb0 1zd0 On0 1HB0 FX0 1C10 Lz0 1Ip0 HX0 1zd0 On0 1HB0 IL0 1wp0 On0 1C10 Lz0 1C10 On0 1zd0 On0 1zd0 Rb0 1zd0 Lz0 1C10 Lz0 1C10 On0 1zd0 On0 1zd0 On0 1zd0 On0 1HB0 FX0|77e4", - "America/Cancun|LMT CST EST EDT CDT|5L.4 60 50 40 50|0123232341414141414141414141414141414141412|-1UQG0 2q2o0 yLB0 1lb0 14p0 1lb0 14p0 Lz0 xB0 14p0 1nX0 11B0 1nX0 1fB0 WL0 1fB0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 Dd0|63e4", - "America/Caracas|LMT CMT -0430 -04|4r.I 4r.E 4u 40|012323|-3eLvw.g ROnX.U 28KM2.k 1IwOu kqo0|29e5", - "America/Cayenne|LMT -04 -03|3t.k 40 30|012|-2mrwu.E 2gWou.E|58e3", - "America/Chicago|LMT CST CDT EST CWT CPT|5O.A 60 50 50 50 50|012121212121212121212121212121212121213121212121214512121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-3tFG0 1nEe0 1nX0 11B0 1nX0 1wp0 TX0 WN0 1qL0 1cN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 11z0 1o10 11z0 11B0 1Hz0 14p0 11z0 1o10 11z0 1qN0 WL0 1qN0 11z0 1o10 11z0 RB0 8x30 iw0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|92e5", - "America/Chihuahua|LMT MST CST MDT CDT|74.k 70 60 60 50|0121312424231313131313131313131313131313131313131313131313132|-1UQF0 deL0 8lc0 17c0 10M0 1dd0 2zQN0 1lb0 14p0 1lb0 14q0 1lb0 14p0 1nX0 11B0 1nX0 1fB0 WL0 1fB0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0|81e4", - "America/Ciudad_Juarez|LMT MST CST MDT CDT|75.U 70 60 60 50|0121312424231313131313131313131313131313131313131313131313132131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131|-1UQF0 deL0 8lc0 17c0 10M0 1dd0 2zQN0 1lb0 14p0 1lb0 14q0 1lb0 14p0 1nX0 11B0 1nX0 1fB0 WL0 1fB0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 U10 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1wn0 cm0 EP0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0|", - "America/Costa_Rica|LMT SJMT CST CDT|5A.d 5A.d 60 50|01232323232|-3eLun.L 1fyo0 2lu0n.L Db0 1Kp0 Db0 pRB0 15b0 1kp0 mL0|12e5", - "America/Phoenix|LMT MST MDT MWT|7s.i 70 60 60|012121313121|-3tFF0 1nEe0 1nX0 11B0 1nX0 SgN0 4Al1 Ap0 1db0 SWqX 1cL0|42e5", - "America/Cuiaba|LMT -04 -03|3I.k 40 30|012121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-2glwf.E HdLf.E 1cc0 1e10 1bX0 Ezd0 So0 1vA0 Mn0 1BB0 ML0 1BB0 zX0 qe10 xb0 2ep0 nz0 1C10 zX0 1C10 LX0 1C10 Mn0 H210 Rb0 1tB0 IL0 1Fd0 FX0 1EN0 FX0 1HB0 Lz0 1EN0 Lz0 1C10 IL0 1HB0 Db0 1HB0 On0 1zd0 On0 1zd0 Lz0 1zd0 Rb0 1wN0 Wn0 1tB0 Rb0 1tB0 WL0 1tB0 Rb0 1zd0 On0 1HB0 FX0 4a10 HX0 1zd0 On0 1HB0 IL0 1wp0 On0 1C10 Lz0 1C10 On0 1zd0 On0 1zd0 Rb0 1zd0 Lz0 1C10 Lz0 1C10 On0 1zd0 On0 1zd0 On0 1zd0 On0 1HB0 FX0|54e4", - "America/Danmarkshavn|LMT -03 -02 GMT|1e.E 30 20 0|01212121212121212121212121212121213|-2a5WJ.k 2z5fJ.k 19U0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 DC0|8", - "America/Dawson_Creek|LMT PST PDT PWT PPT MST|80.U 80 70 70 70 70|01213412121212121212121212121212121212121212121212121212125|-3tofX.4 1nspX.4 1in0 UGp0 8x10 iy0 3NB0 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 ML0|12e3", - "America/Dawson|LMT YST YDT YWT YPT YDDT PST PDT MST|9h.E 90 80 80 80 70 80 70 70|0121213415167676767676767676767676767676767676767676767676767676767676767676767676767676767678|-2MSeG.k GWpG.k 1in0 1o10 13V0 Ser0 8x00 iz0 LCL0 1fA0 jrA0 fNd0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1z90|13e2", - "America/Denver|LMT MST MDT MWT MPT|6X.U 70 60 60 60|012121212134121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-3tFF0 1nEe0 1nX0 11B0 1nX0 11B0 1qL0 WN0 mn0 Ord0 8x20 ix0 LCN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|26e5", - "America/Detroit|LMT CST EST EWT EPT EDT|5w.b 60 50 40 40 40|0123425252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252|-2Cgir.N peqr.N 156L0 8x40 iv0 6fd0 11z0 JxX1 SMX 1cN0 1cL0 aW10 1cL0 s10 1Vz0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|37e5", - "America/Edmonton|LMT MST MDT MWT MPT|7x.Q 70 60 60 60|0121212121212134121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-2yd4q.8 shdq.8 1in0 17d0 hz0 2dB0 1fz0 1a10 11z0 1qN0 WL0 1qN0 11z0 IGN0 8x20 ix0 3NB0 11z0 XQp0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|10e5", - "America/Eirunepe|LMT -05 -04|4D.s 50 40|0121212121212121212121212121212121|-2glvk.w HdLk.w 1cc0 1e10 1bX0 Ezd0 So0 1vA0 Mn0 1BB0 ML0 1BB0 zX0 qe10 xb0 2ep0 nz0 1C10 zX0 1C10 LX0 1C10 Mn0 H210 Rb0 1tB0 IL0 1Fd0 FX0 dPB0 On0 yTd0 d5X0|31e3", - "America/El_Salvador|LMT CST CDT|5U.M 60 50|012121|-1XiG3.c 2Fvc3.c WL0 1qN0 WL0|11e5", - "America/Tijuana|LMT MST PST PDT PWT PPT|7M.4 70 80 70 70 70|012123245232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232|-1UQF0 4Q00 8mM0 8lc0 SN0 1cL0 pHB0 83r0 zI0 5O10 1Rz0 cOO0 11A0 1o00 11A0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1o00 11A0 BUp0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 U10 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|20e5", - "America/Fort_Nelson|LMT PST PDT PWT PPT MST|8a.L 80 70 70 70 70|012134121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121215|-3tofN.d 1nspN.d 1in0 UGp0 8x10 iy0 3NB0 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0|39e2", - "America/Fort_Wayne|LMT CST CDT CWT CPT EST EDT|5I.C 60 50 50 50 50 40|0121212134121212121212121212151565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565|-3tFG0 1nEe0 1nX0 11B0 1nX0 QI10 Db0 RB0 8x30 iw0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 5Tz0 1o10 qLb0 1cL0 1cN0 1cL0 1qhd0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|", - "America/Fortaleza|LMT -03 -02|2y 30 20|0121212121212121212121212121212121212121|-2glxq HdLq 1cc0 1e10 1bX0 Ezd0 So0 1vA0 Mn0 1BB0 ML0 1BB0 zX0 qe10 xb0 2ep0 nz0 1C10 zX0 1C10 LX0 1C10 Mn0 H210 Rb0 1tB0 IL0 1Fd0 FX0 1EN0 FX0 1HB0 Lz0 nsp0 WL0 1tB0 5z0 2mN0 On0|34e5", - "America/Glace_Bay|LMT AST ADT AWT APT|3X.M 40 30 30 30|012134121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-2IsI0.c CwO0.c 1in0 UGp0 8x50 iu0 iq10 11z0 Jg10 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|19e3", - "America/Godthab|LMT -03 -02 -01|3q.U 30 20 10|012121212121212121212121212121212121212121212121212121212121212121212121212121212121212123232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232|-2a5Ux.4 2z5dx.4 19U0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 2so0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|17e3", - "America/Goose_Bay|LMT NST NDT NST NDT NWT NPT AST ADT ADDT|41.E 3u.Q 2u.Q 3u 2u 2u 2u 40 30 20|0121343434343434356343434343434343434343434343434343434343437878787878787878787878787878787878787878787879787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787|-3tojW.k 1nspt.c 1in0 DXb0 2HbX.8 WL0 1qN0 WL0 1qN0 WL0 1tB0 TX0 1tB0 WL0 1qN0 WL0 1qN0 7UHu itu 1tB0 WL0 1qN0 WL0 1qN0 WL0 1qN0 WL0 1tB0 WL0 1ld0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 S10 g0u 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14n1 1lb0 14p0 1nW0 11C0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zcX Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|76e2", - "America/Grand_Turk|LMT KMT EST EDT AST|4I.w 57.a 50 40 40|01232323232323232323232323232323232323232323232323232323232323232323232323243232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232|-3eLvf.s RK0m.C 2HHBQ.O 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 7jA0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|37e2", - "America/Guatemala|LMT CST CDT|62.4 60 50|0121212121|-24KhV.U 2efXV.U An0 mtd0 Nz0 ifB0 17b0 zDB0 11z0|13e5", - "America/Guayaquil|LMT QMT -05 -04|5j.k 5e 50 40|01232|-3eLuE.E 1DNzS.E 2uILK rz0|27e5", - "America/Guyana|LMT -04 -0345 -03|3Q.D 40 3J 30|01231|-2mf87.l 8Hc7.l 2r7bJ Ey0f|80e4", - "America/Halifax|LMT AST ADT AWT APT|4e.o 40 30 30 30|0121212121212121212121212121212121212121212121212134121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-2IsHJ.A xzzJ.A 1db0 3I30 1in0 3HX0 IL0 1E10 ML0 1yN0 Pb0 1Bd0 Mn0 1Bd0 Rz0 1w10 Xb0 1w10 LX0 1w10 Xb0 1w10 Lz0 1C10 Jz0 1E10 OL0 1yN0 Un0 1qp0 Xb0 1qp0 11X0 1w10 Lz0 1HB0 LX0 1C10 FX0 1w10 Xb0 1qp0 Xb0 1BB0 LX0 1td0 Xb0 1qp0 Xb0 Rf0 8x50 iu0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 3Qp0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 3Qp0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 6i10 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|39e4", - "America/Havana|LMT HMT CST CDT|5t.s 5t.A 50 40|0123232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232|-3eLuu.w 1qx00.8 72zu.o ML0 sld0 An0 1Nd0 Db0 1Nd0 An0 6Ep0 An0 1Nd0 An0 JDd0 Mn0 1Ap0 On0 1fd0 11X0 1qN0 WL0 1wp0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 14n0 1ld0 14L0 1kN0 15b0 1kp0 1cL0 1cN0 1fz0 1a10 1fz0 1fB0 11z0 14p0 1nX0 11B0 1nX0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 14n0 1ld0 14n0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 1a10 1in0 1a10 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1cM0 1cM0 1fA0 17c0 1o00 11A0 1qM0 11A0 1o00 11A0 1o00 14o0 1lc0 14o0 1lc0 11A0 6i00 Rc0 1wo0 U00 1tA0 Rc0 1wo0 U00 1wo0 U00 1zc0 U00 1qM0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0|21e5", - "America/Hermosillo|LMT MST CST MDT PST|7n.Q 70 60 60 80|0121312141313131|-1UQF0 deL0 8lc0 17c0 10M0 1dd0 otX0 gmN0 P2N0 13Vd0 1lb0 14p0 1lb0 14p0 1lb0|64e4", - "America/Indiana/Knox|LMT CST CDT CWT CPT EST|5K.u 60 50 50 50 50|01212134121212121212121212121212121212151212121212121212121212121212121212121212121212121252121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-3tFG0 1nEe0 1nX0 11B0 1nX0 SgN0 8x30 iw0 3NB0 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 1fz0 1cN0 1cL0 1cN0 11z0 1o10 11z0 1o10 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 3Cn0 8wp0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 z8o0 1o00 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|", - "America/Indiana/Marengo|LMT CST CDT CWT CPT EST EDT|5J.n 60 50 50 50 50 40|01212134121212121212121215656565656525656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565|-3tFG0 1nEe0 1nX0 11B0 1nX0 SgN0 8x30 iw0 dyN0 11z0 6fd0 11z0 1o10 11z0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 jrz0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1VA0 LA0 1BX0 1e6p0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|", - "America/Indiana/Petersburg|LMT CST CDT CWT CPT EST EDT|5N.7 60 50 50 50 50 40|01212134121212121212121212121512121212121212121212125212565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565|-3tFG0 1nEe0 1nX0 11B0 1nX0 SgN0 8x30 iw0 njX0 WN0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 3Fb0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 19co0 1o00 Rd0 1zb0 Oo0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|", - "America/Indiana/Tell_City|LMT CST CDT CWT CPT EST EDT|5L.3 60 50 50 50 50 40|012121341212121212121212121512165652121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-3tFG0 1nEe0 1nX0 11B0 1nX0 SgN0 8x30 iw0 njX0 WN0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 8wn0 1cN0 1cL0 1cN0 1cK0 1cN0 1cL0 1qhd0 1o00 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|", - "America/Indiana/Vevay|LMT CST CDT CWT CPT EST EDT|5E.g 60 50 50 50 50 40|0121213415656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565|-3tFG0 1nEe0 1nX0 11B0 1nX0 SgN0 8x30 iw0 kPB0 Awn0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1lnd0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|", - "America/Indiana/Vincennes|LMT CST CDT CWT CPT EST EDT|5O.7 60 50 50 50 50 40|01212134121212121212121212121212156565212565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565|-3tFG0 1nEe0 1nX0 11B0 1nX0 SgN0 8x30 iw0 1o10 11z0 g0p0 11z0 1o10 11z0 1qL0 WN0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 1fz0 1cN0 WL0 1qN0 1cL0 1cN0 1cL0 1cN0 caL0 1cL0 1cN0 1cL0 1qhd0 1o00 Rd0 1zb0 Oo0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|", - "America/Indiana/Winamac|LMT CST CDT CWT CPT EST EDT|5K.p 60 50 50 50 50 40|012121341212121212121212121212121212121565652165656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565|-3tFG0 1nEe0 1nX0 11B0 1nX0 SgN0 8x30 iw0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 1fz0 1cN0 1cL0 1cN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 jrz0 1cL0 1cN0 1cL0 1qhd0 1o00 Rd0 1za0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|", - "America/Inuvik|-00 PST PDT MDT MST|0 80 70 60 70|01212121212121213434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434|-FnA0 L3K0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cK0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|35e2", - "America/Iqaluit|-00 EWT EPT EST EDT CST CDT|0 40 40 50 40 60 50|0123434343434343434343434343434343434343434343434343434343456343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343|-16K00 7nX0 iv0 14HB0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11C0 1nX0 11A0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|67e2", - "America/Jamaica|LMT KMT EST EDT|57.a 57.a 50 40|01232323232323232323232|-3eLuQ.O RK00 2uM1Q.O 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0|94e4", - "America/Juneau|LMT LMT PST PWT PPT PDT YDT YST AKST AKDT|-f2.j 8V.F 80 70 70 70 80 90 90 80|0123425252525252525252525252625252578989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898|-48Pzs.L 1jVwq.s 1EX12.j 8x10 iy0 Vo10 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cM0 1cM0 1cL0 1cN0 1fz0 1a10 1fz0 co0 10q0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|33e3", - "America/Kentucky/Louisville|LMT CST CDT CWT CPT EST EDT|5H.2 60 50 50 50 50 40|01212121213412121212121212121212121212565656565656525656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565|-3tFG0 1nEe0 1nX0 11B0 1nX0 3Fd0 Nb0 LPd0 11z0 RB0 8x30 iw0 1nX1 e0X 9vd0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 xz0 gso0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1VA0 LA0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|", - "America/Kentucky/Monticello|LMT CST CDT CWT CPT EST EDT|5D.o 60 50 50 50 50 40|01212134121212121212121212121212121212121212121212121212121212121212121212565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565|-3tFG0 1nEe0 1nX0 11B0 1nX0 SgN0 8x30 iw0 SWp0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11A0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|", - "America/La_Paz|LMT CMT BST -04|4w.A 4w.A 3w.A 40|0123|-3eLvr.o 1FIo0 13b0|19e5", - "America/Lima|LMT LMT -05 -04|58.c 58.A 50 40|01232323232323232|-3eLuP.M JcM0.o 1bDzP.o zX0 1aN0 1cL0 1cN0 1cL0 1PrB0 zX0 1O10 zX0 6Gp0 zX0 98p0 zX0|11e6", - "America/Los_Angeles|LMT PST PDT PWT PPT|7Q.W 80 70 70 70|0121213412121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-3tFE0 1nEe0 1nX0 11B0 1nX0 SgN0 8x10 iy0 5Wp1 1VaX 3dA0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1a00 1fA0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|15e6", - "America/Maceio|LMT -03 -02|2m.Q 30 20|012121212121212121212121212121212121212121|-2glxB.8 HdLB.8 1cc0 1e10 1bX0 Ezd0 So0 1vA0 Mn0 1BB0 ML0 1BB0 zX0 qe10 xb0 2ep0 nz0 1C10 zX0 1C10 LX0 1C10 Mn0 H210 Rb0 1tB0 IL0 1Fd0 FX0 1EN0 FX0 1HB0 Lz0 dMN0 Lz0 8Q10 WL0 1tB0 5z0 2mN0 On0|93e4", - "America/Managua|LMT MMT CST EST CDT|5J.8 5J.c 60 50 50|01232424232324242|-3eLue.Q 1Mhc0.4 1yAMe.M 4mn0 9Up0 Dz0 1K10 Dz0 s3F0 1KH0 DB0 9In0 k8p0 19X0 1o30 11y0|22e5", - "America/Manaus|LMT -04 -03|40.4 40 30|01212121212121212121212121212121|-2glvX.U HdKX.U 1cc0 1e10 1bX0 Ezd0 So0 1vA0 Mn0 1BB0 ML0 1BB0 zX0 qe10 xb0 2ep0 nz0 1C10 zX0 1C10 LX0 1C10 Mn0 H210 Rb0 1tB0 IL0 1Fd0 FX0 dPB0 On0|19e5", - "America/Martinique|LMT FFMT AST ADT|44.k 44.k 40 30|01232|-3eLvT.E PTA0 2LPbT.E 19X0|39e4", - "America/Matamoros|LMT CST CDT|6u 60 50|0121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-1UQG0 2FjC0 1nX0 i6p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 1fB0 WL0 1fB0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 U10 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|45e4", - "America/Mazatlan|LMT MST CST MDT PST|75.E 70 60 60 80|0121312141313131313131313131313131313131313131313131313131313131|-1UQF0 deL0 8lc0 17c0 10M0 1dd0 otX0 gmN0 P2N0 13Vd0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 1fB0 WL0 1fB0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0|44e4", - "America/Menominee|LMT CST CDT CWT CPT EST|5O.r 60 50 50 50 50|012121341212152121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-3pdG9.x 1jce9.x 1nX0 11B0 1nX0 SgN0 8x30 iw0 1o10 11z0 LCN0 1fz0 6410 9Jb0 1cM0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|85e2", - "America/Merida|LMT CST EST CDT|5W.s 60 50 50|0121313131313131313131313131313131313131313131313131313131|-1UQG0 2q2o0 2hz0 wu30 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 1fB0 WL0 1fB0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0|11e5", - "America/Metlakatla|LMT LMT PST PWT PPT PDT AKST AKDT|-fd.G 8K.i 80 70 70 70 90 80|0123425252525252525252525252525252526767672676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676|-48Pzs.L 1jVwf.5 1EX1d.G 8x10 iy0 Vo10 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1hU10 Rd0 1zb0 Op0 1zb0 Op0 1zb0 uM0 jB0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|14e2", - "America/Mexico_City|LMT MST CST MDT CDT CWT|6A.A 70 60 60 50 50|012131242425242424242424242424242424242424242424242424242424242424242|-1UQF0 deL0 8lc0 17c0 10M0 1dd0 gEn0 TX0 3xd0 Jb0 6zB0 SL0 e5d0 17b0 1Pff0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 1fB0 WL0 1fB0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0|20e6", - "America/Miquelon|LMT AST -03 -02|3I.E 40 30 20|012323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232|-2mKkf.k 2LTAf.k gQ10 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|61e2", - "America/Moncton|LMT EST AST ADT AWT APT|4j.8 50 40 30 30 30|0123232323232323232323245232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232|-3txvE.Q J4ME.Q CwN0 1in0 zAo0 An0 1Nd0 An0 1Nd0 An0 1Nd0 An0 1Nd0 An0 1Nd0 An0 1K10 Lz0 1zB0 NX0 1u10 Wn0 S20 8x50 iu0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 11z0 1o10 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 3Cp0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14n1 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 ReX 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|64e3", - "America/Monterrey|LMT CST CDT|6F.g 60 50|0121212121212121212121212121212121212121212121212121212121|-1UQG0 2FjC0 1nX0 i6p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 1fB0 WL0 1fB0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0|41e5", - "America/Montevideo|LMT MMT -04 -03 -0330 -0230 -02 -0130|3I.P 3I.P 40 30 3u 2u 20 1u|012343434343434343434343435353636353636375363636363636363636363636363636363636363636363|-2tRUf.9 sVc0 8jcf.9 1db0 1dcu 1cLu 1dcu 1cLu ircu 11zu 1o0u 11zu 1o0u 11zu 1o0u 11zu 1qMu WLu 1qMu WLu 1fAu 1cLu 1o0u 11zu NAu 3jXu zXu Dq0u 19Xu pcu jz0 cm10 19X0 6tB0 1fbu 3o0u jX0 4vB0 xz0 3Cp0 mmu 1a10 IMu Db0 4c10 uL0 1Nd0 An0 1SN0 uL0 mp0 28L0 iPB0 un0 1SN0 xz0 1zd0 Lz0 1zd0 Rb0 1zd0 On0 1wp0 Rb0 s8p0 1fB0 1ip0 11z0 1ld0 14n0 1o10 11z0 1o10 11z0 1o10 14n0 1ld0 14n0 1ld0 14n0 1o10 11z0 1o10 11z0 1o10 11z0|17e5", - "America/Toronto|LMT EST EDT EWT EPT|5h.w 50 40 40 40|012121212121212121212121212121212121212121212123412121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-32B6G.s UFdG.s 1in0 11Wu 1nzu 1fD0 WJ0 1wr0 Nb0 1Ap0 On0 1zd0 On0 1wp0 TX0 1tB0 TX0 1tB0 TX0 1tB0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 4kM0 8x40 iv0 1o10 11z0 1nX0 11z0 1o10 11z0 1o10 1qL0 11D0 1nX0 11B0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 11z0 1o10 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|65e5", - "America/New_York|LMT EST EDT EWT EPT|4U.2 50 40 40 40|012121212121212121212121212121212121212121212121213412121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-3tFH0 1nEe0 1nX0 11B0 1nX0 11B0 1qL0 1a10 11z0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 11z0 1o10 11z0 RB0 8x40 iv0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|21e6", - "America/Nome|LMT LMT NST NWT NPT BST BDT YST AKST AKDT|-cW.m b1.C b0 a0 a0 b0 a0 90 90 80|01234256565656565656565656565656565678989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898|-48Pzs.L 1jVyu.p 1EX1W.m 8wW0 iB0 Qlb0 52O0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 cl0 10q0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|38e2", - "America/Noronha|LMT -02 -01|29.E 20 10|0121212121212121212121212121212121212121|-2glxO.k HdKO.k 1cc0 1e10 1bX0 Ezd0 So0 1vA0 Mn0 1BB0 ML0 1BB0 zX0 qe10 xb0 2ep0 nz0 1C10 zX0 1C10 LX0 1C10 Mn0 H210 Rb0 1tB0 IL0 1Fd0 FX0 1EN0 FX0 1HB0 Lz0 nsp0 WL0 1tB0 2L0 2pB0 On0|30e2", - "America/North_Dakota/Beulah|LMT MST MDT MWT MPT CST CDT|6L.7 70 60 60 60 60 50|012121341212121212121212121212121212121212121212121212121212121212121212121212121212121212121212565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565|-3tFF0 1nEe0 1nX0 11B0 1nX0 SgN0 8x20 ix0 QwN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Oo0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0|", - "America/North_Dakota/Center|LMT MST MDT MWT MPT CST CDT|6J.c 70 60 60 60 60 50|0121213412121212121212121212121212121212121212121212121212125656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565|-3tFF0 1nEe0 1nX0 11B0 1nX0 SgN0 8x20 ix0 QwN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14o0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|", - "America/North_Dakota/New_Salem|LMT MST MDT MWT MPT CST CDT|6J.D 70 60 60 60 60 50|0121213412121212121212121212121212121212121212121212121212121212121212121212121212565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565|-3tFF0 1nEe0 1nX0 11B0 1nX0 SgN0 8x20 ix0 QwN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14o0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|", - "America/Ojinaga|LMT MST CST MDT CDT|6V.E 70 60 60 50|0121312424231313131313131313131313131313131313131313131313132424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242|-1UQF0 deL0 8lc0 17c0 10M0 1dd0 2zQN0 1lb0 14p0 1lb0 14q0 1lb0 14p0 1nX0 11B0 1nX0 1fB0 WL0 1fB0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 U10 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1wn0 Rc0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|23e3", - "America/Paramaribo|LMT PMT PMT -0330 -03|3E.E 3E.Q 3E.A 3u 30|01234|-2nDUj.k Wqo0.c qanX.I 1yVXN.o|24e4", - "America/Port-au-Prince|LMT PPMT EST EDT|4N.k 4N 50 40|012323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232|-3eLva.E 15RLX.E 2FnMb 19X0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14q0 1o00 11A0 1o00 11A0 1o00 14o0 1lc0 14o0 1lc0 14o0 1o00 11A0 1o00 11A0 1o00 14o0 1lc0 14o0 1lc0 i6n0 1nX0 11B0 1nX0 d430 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 3iN0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|23e5", - "America/Rio_Branco|LMT -05 -04|4v.c 50 40|01212121212121212121212121212121|-2glvs.M HdLs.M 1cc0 1e10 1bX0 Ezd0 So0 1vA0 Mn0 1BB0 ML0 1BB0 zX0 qe10 xb0 2ep0 nz0 1C10 zX0 1C10 LX0 1C10 Mn0 H210 Rb0 1tB0 IL0 1Fd0 FX0 NBd0 d5X0|31e4", - "America/Porto_Velho|LMT -04 -03|4f.A 40 30|012121212121212121212121212121|-2glvI.o HdKI.o 1cc0 1e10 1bX0 Ezd0 So0 1vA0 Mn0 1BB0 ML0 1BB0 zX0 qe10 xb0 2ep0 nz0 1C10 zX0 1C10 LX0 1C10 Mn0 H210 Rb0 1tB0 IL0 1Fd0 FX0|37e4", - "America/Punta_Arenas|LMT SMT -05 -04 -03|4H.E 4G.J 50 40 30|01213132323232323232343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434|-3eLvg.k MJbX.5 fJAh.f 5knG.J 1Vzh.f jRAG.J 1pbh.f 11d0 1oL0 11d0 1oL0 11d0 1oL0 11d0 1pb0 11d0 nHX0 op0 blz0 ko0 Qeo0 WL0 1zd0 On0 1ip0 11z0 1o10 11z0 1qN0 WL0 1ld0 14n0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 WL0 1qN0 1cL0 1cN0 11z0 1o10 11z0 1qN0 WL0 1fB0 19X0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 17b0 1ip0 11z0 1ip0 1fz0 1fB0 11z0 1qN0 WL0 1qN0 WL0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 17b0 1ip0 11z0 1o10 19X0 1fB0 1nX0 G10 1EL0 Op0 1zb0 Rd0 1wn0 Rd0 46n0 Ap0|", - "America/Winnipeg|LMT CST CDT CWT CPT|6s.A 60 50 50 50|0121212134121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-3kLtv.o 1a3bv.o WL0 3ND0 1in0 Jap0 Rb0 aCN0 8x30 iw0 1tB0 11z0 1ip0 11z0 1o10 11z0 1o10 11z0 1rd0 10L0 1op0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 1cL0 1cN0 11z0 6i10 WL0 6i10 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1a00 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1a00 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 14o0 1lc0 14o0 1o00 11A0 1o00 11A0 1o00 14o0 1lc0 14o0 1lc0 14o0 1o00 11A0 1o00 11A0 1o00 14o0 1lc0 14o0 1lc0 14o0 1lc0 14o0 1o00 11A0 1o00 11A0 1o00 14o0 1lc0 14o0 1lc0 14o0 1o00 11A0 1o00 11A0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|66e4", - "America/Rankin_Inlet|-00 CST CDT EST|0 60 50 50|01212121212121212121212121212121212121212121212121212121212321212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-vDc0 Bjk0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|26e2", - "America/Recife|LMT -03 -02|2j.A 30 20|0121212121212121212121212121212121212121|-2glxE.o HdLE.o 1cc0 1e10 1bX0 Ezd0 So0 1vA0 Mn0 1BB0 ML0 1BB0 zX0 qe10 xb0 2ep0 nz0 1C10 zX0 1C10 LX0 1C10 Mn0 H210 Rb0 1tB0 IL0 1Fd0 FX0 1EN0 FX0 1HB0 Lz0 nsp0 WL0 1tB0 2L0 2pB0 On0|33e5", - "America/Regina|LMT MST MDT MWT MPT CST|6W.A 70 60 60 60 60|012121212121212121212121341212121212121212121212121215|-2AD51.o uHe1.o 1in0 s2L0 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 11z0 66N0 1cL0 1cN0 19X0 1fB0 1cL0 1fB0 1cL0 1cN0 1cL0 M30 8x20 ix0 1ip0 1cL0 1ip0 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 11z0 1o10 11z0 3NB0 1cL0 1cN0|19e4", - "America/Resolute|-00 CST CDT EST|0 60 50 50|01212121212121212121212121212121212121212121212121212121212321212121212321212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-SnA0 103I0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|229", - "America/Santarem|LMT -04 -03|3C.M 40 30|0121212121212121212121212121212|-2glwl.c HdLl.c 1cc0 1e10 1bX0 Ezd0 So0 1vA0 Mn0 1BB0 ML0 1BB0 zX0 qe10 xb0 2ep0 nz0 1C10 zX0 1C10 LX0 1C10 Mn0 H210 Rb0 1tB0 IL0 1Fd0 FX0 NBd0|21e4", - "America/Santiago|LMT SMT -05 -04 -03|4G.J 4G.J 50 40 30|0121313232323232323432343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434|-3eLvh.f MJc0 fJAh.f 5knG.J 1Vzh.f jRAG.J 1pbh.f 11d0 1oL0 11d0 1oL0 11d0 1oL0 11d0 1pb0 11d0 nHX0 op0 9Bz0 hX0 1q10 ko0 Qeo0 WL0 1zd0 On0 1ip0 11z0 1o10 11z0 1qN0 WL0 1ld0 14n0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 WL0 1qN0 1cL0 1cN0 11z0 1o10 11z0 1qN0 WL0 1fB0 19X0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 17b0 1ip0 11z0 1ip0 1fz0 1fB0 11z0 1qN0 WL0 1qN0 WL0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 17b0 1ip0 11z0 1o10 19X0 1fB0 1nX0 G10 1EL0 Op0 1zb0 Rd0 1wn0 Rd0 46n0 Ap0 1Nb0 Ap0 1Nb0 Ap0 1zb0 11B0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 11B0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0|62e5", - "America/Santo_Domingo|LMT SDMT EST EDT -0430 AST|4D.A 4E 50 40 4u 40|012324242424242525|-3eLvk.o 1Jic0.o 1lJMk Mn0 6sp0 Lbu 1Cou yLu 1RAu wLu 1QMu xzu 1Q0u xXu 1PAu 13jB0 e00|29e5", - "America/Sao_Paulo|LMT -03 -02|36.s 30 20|01212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-2glwR.w HdKR.w 1cc0 1e10 1bX0 Ezd0 So0 1vA0 Mn0 1BB0 ML0 1BB0 zX0 pTd0 PX0 2ep0 nz0 1C10 zX0 1C10 LX0 1C10 Mn0 H210 Rb0 1tB0 IL0 1Fd0 FX0 1EN0 FX0 1HB0 Lz0 1EN0 Lz0 1C10 IL0 1HB0 Db0 1HB0 On0 1zd0 On0 1zd0 Lz0 1zd0 Rb0 1wN0 Wn0 1tB0 Rb0 1tB0 WL0 1tB0 Rb0 1zd0 On0 1HB0 FX0 1C10 Lz0 1Ip0 HX0 1zd0 On0 1HB0 IL0 1wp0 On0 1C10 Lz0 1C10 On0 1zd0 On0 1zd0 Rb0 1zd0 Lz0 1C10 Lz0 1C10 On0 1zd0 On0 1zd0 On0 1zd0 On0 1HB0 FX0|20e6", - "America/Scoresbysund|LMT -02 -01 +00|1r.Q 20 10 0|0121323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232|-2a5Ww.8 2z5ew.8 1a00 1cK0 1cL0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|452", - "America/Sitka|LMT LMT PST PWT PPT PDT YST AKST AKDT|-eW.L 91.d 80 70 70 70 90 90 80|0123425252525252525252525252525252567878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787|-48Pzs.L 1jVwu 1EX0W.L 8x10 iy0 Vo10 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 co0 10q0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|90e2", - "America/St_Johns|LMT NST NDT NST NDT NWT NPT NDDT|3u.Q 3u.Q 2u.Q 3u 2u 2u 2u 1u|012121212121212121212121212121212121213434343434343435634343434343434343434343434343434343434343434343434343434343434343434343434343434343437343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343|-3tokt.8 1l020 14L0 1nB0 1in0 1gm0 Dz0 1JB0 1cL0 1cN0 1cL0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1fB0 1cL0 1cN0 1cL0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1fB0 1cL0 1fB0 19X0 1fB0 19X0 10O0 eKX.8 19X0 1iq0 WL0 1qN0 WL0 1qN0 WL0 1tB0 TX0 1tB0 WL0 1qN0 WL0 1qN0 7UHu itu 1tB0 WL0 1qN0 WL0 1qN0 WL0 1qN0 WL0 1tB0 WL0 1ld0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14n1 1lb0 14p0 1nW0 11C0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zcX Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|11e4", - "America/Swift_Current|LMT MST MDT MWT MPT CST|7b.k 70 60 60 60 60|012134121212121212121215|-2AD4M.E uHdM.E 1in0 UGp0 8x20 ix0 1o10 17b0 1ip0 11z0 1o10 11z0 1o10 11z0 isN0 1cL0 3Cp0 1cL0 1cN0 11z0 1qN0 WL0 pMp0|16e3", - "America/Tegucigalpa|LMT CST CDT|5M.Q 60 50|01212121|-1WGGb.8 2ETcb.8 WL0 1qN0 WL0 GRd0 AL0|11e5", - "America/Thule|LMT AST ADT|4z.8 40 30|012121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-2a5To.Q 31NBo.Q 1cL0 1cN0 1cL0 1fB0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|656", - "America/Vancouver|LMT PST PDT PWT PPT|8c.s 80 70 70 70|01213412121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-3tofL.w 1nspL.w 1in0 UGp0 8x10 iy0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|23e5", - "America/Whitehorse|LMT YST YDT YWT YPT YDDT PST PDT MST|90.c 90 80 80 80 70 80 70 70|0121213415167676767676767676767676767676767676767676767676767676767676767676767676767676767678|-2MSeX.M GWpX.M 1in0 1o10 13V0 Ser0 8x00 iz0 LCL0 1fA0 LA0 ytd0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1z90|23e3", - "America/Yakutat|LMT LMT YST YWT YPT YDT AKST AKDT|-eF.5 9i.T 90 80 80 80 90 80|0123425252525252525252525252525252526767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676|-48Pzs.L 1jVwL.G 1EX1F.5 8x00 iz0 Vo10 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 cn0 10q0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|642", - "Antarctica/Casey|-00 +08 +11|0 -80 -b0|0121212121212|-2q00 1DjS0 T90 40P0 KL0 blz0 3m10 1o30 14k0 1kr0 12l0 1o01|10", - "Antarctica/Davis|-00 +07 +05|0 -70 -50|01012121|-vyo0 iXt0 alj0 1D7v0 VB0 3Wn0 KN0|70", - "Pacific/Port_Moresby|LMT PMMT +10|-9M.E -9M.w -a0|012|-3D8VM.E AvA0.8|25e4", - "Antarctica/Macquarie|-00 AEST AEDT|0 -a0 -b0|0121012121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212|-2OPc0 Fb40 1a00 4SK0 1ayy0 Lvs0 1cM0 1o00 Rc0 1wo0 Rc0 1wo0 U00 1wo0 LA0 1C00 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 11A0 1qM0 WM0 1qM0 Oo0 1zc0 Oo0 1zc0 Oo0 1wo0 WM0 1tA0 WM0 1tA0 U00 1tA0 U00 1tA0 11A0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 11A0 1o00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1cM0 1a00 1io0 1cM0 1cM0 1cM0 1cM0 3Co0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0|1", - "Antarctica/Mawson|-00 +06 +05|0 -60 -50|012|-CEo0 2fyk0|60", - "Pacific/Auckland|LMT NZMT NZST NZST NZDT|-bD.4 -bu -cu -c0 -d0|012131313131313131313131313134343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434|-46jLD.4 2nEO9.4 Lz0 1tB0 11zu 1o0u 11zu 1o0u 11zu 1o0u 14nu 1lcu 14nu 1lcu 1lbu 11Au 1nXu 11Au 1nXu 11Au 1nXu 11Au 1nXu 11Au 1qLu WMu 1qLu 11Au 1n1bu IM0 1C00 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1qM0 14o0 1lc0 14o0 1lc0 14o0 1lc0 17c0 1io0 17c0 1io0 17c0 1io0 17c0 1lc0 14o0 1lc0 14o0 1lc0 17c0 1io0 17c0 1io0 17c0 1lc0 14o0 1lc0 14o0 1lc0 17c0 1io0 17c0 1io0 17c0 1io0 17c0 1io0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00|14e5", - "Antarctica/Palmer|-00 -03 -04 -02|0 30 40 20|0121212121213121212121212121212121212121212121212121212121212121212121212121212121|-cao0 nD0 1vd0 SL0 1vd0 17z0 1cN0 1fz0 1cN0 1cL0 1cN0 asn0 Db0 jsN0 14N0 11z0 1o10 11z0 1qN0 WL0 1qN0 WL0 1qN0 1cL0 1cN0 11z0 1o10 11z0 1qN0 WL0 1fB0 19X0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 17b0 1ip0 11z0 1ip0 1fz0 1fB0 11z0 1qN0 WL0 1qN0 WL0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 17b0 1ip0 11z0 1o10 19X0 1fB0 1nX0 G10 1EL0 Op0 1zb0 Rd0 1wn0 Rd0 46n0 Ap0|40", - "Antarctica/Rothera|-00 -03|0 30|01|gOo0|130", - "Asia/Riyadh|LMT +03|-36.Q -30|01|-TvD6.Q|57e5", - "Antarctica/Troll|-00 +00 +02|0 0 -20|01212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|1puo0 hd0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|40", - "Asia/Urumqi|LMT +06|-5O.k -60|01|-1GgtO.k|32e5", - "Europe/Berlin|LMT CET CEST CEMT|-R.s -10 -20 -30|012121212121212321212321212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-36RcR.s UbWR.s 11d0 1iO0 11A0 1o00 11A0 Qrc0 6i00 WM0 1fA0 1cM0 1cM0 1cM0 kL0 Nc0 m10 WM0 1ao0 1cp0 dX0 jz0 Dd0 1io0 17c0 1fA0 1a00 1ehA0 1a00 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|41e5", - "Asia/Almaty|LMT +05 +06 +07|-57.M -50 -60 -70|012323232323232323232321232323232323232323232323232|-1Pc57.M eUo7.M 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 2pB0 IM0 rX0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0|15e5", - "Asia/Amman|LMT EET EEST +03|-2n.I -20 -30 -30|0121212121212121212121212121212121212121212121212121212121212121212121212121212121212123|-1yW2n.I 1HiMn.I KL0 1oN0 11b0 1oN0 11b0 1pd0 1dz0 1cp0 11b0 1op0 11b0 fO10 1db0 1e10 1cL0 1cN0 1cL0 1cN0 1fz0 1pd0 10n0 1ld0 14n0 1hB0 15b0 1ip0 19X0 1cN0 1cL0 1cN0 17b0 1ld0 14o0 1lc0 17c0 1io0 17c0 1io0 17c0 1So0 y00 1fc0 1dc0 1co0 1dc0 1cM0 1cM0 1cM0 1o00 11A0 1lc0 17c0 1cM0 1cM0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 4bX0 Dd0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 LA0 1C00|25e5", - "Asia/Anadyr|LMT +12 +13 +14 +11|-bN.U -c0 -d0 -e0 -b0|01232121212121212121214121212121212121212121212121212121212141|-1PcbN.U eUnN.U 23CL0 1db0 2q10 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 2pB0 IM0 rX0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 2sp0 WM0|13e3", - "Asia/Aqtau|LMT +04 +05 +06|-3l.4 -40 -50 -60|012323232323232323232123232312121212121212121212|-1Pc3l.4 eUnl.4 24PX0 2pX0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 2pB0 IM0 rX0 1cM0 1cM0 1cM0 1cM0 1cM0 1cN0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0|15e4", - "Asia/Aqtobe|LMT +04 +05 +06|-3M.E -40 -50 -60|0123232323232323232321232323232323232323232323232|-1Pc3M.E eUnM.E 23CL0 3Db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 2pB0 IM0 rX0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0|27e4", - "Asia/Ashgabat|LMT +04 +05 +06|-3R.w -40 -50 -60|0123232323232323232323212|-1Pc3R.w eUnR.w 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 2pB0 IM0|41e4", - "Asia/Atyrau|LMT +03 +05 +06 +04|-3r.I -30 -50 -60 -40|01232323232323232323242323232323232324242424242|-1Pc3r.I eUor.I 24PW0 2pX0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 2pB0 IM0 rX0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 2sp0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0|", - "Asia/Baghdad|LMT BMT +03 +04|-2V.E -2V.A -30 -40|0123232323232323232323232323232323232323232323232323232|-3eLCV.E 18ao0.4 2ACnV.A 11b0 1cp0 1dz0 1dd0 1db0 1cN0 1cp0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1de0 1dc0 1dc0 1dc0 1cM0 1dc0 1cM0 1dc0 1cM0 1dc0 1dc0 1dc0 1cM0 1dc0 1cM0 1dc0 1cM0 1dc0 1dc0 1dc0 1cM0 1dc0 1cM0 1dc0 1cM0 1dc0 1dc0 1dc0 1cM0 1dc0 1cM0 1dc0 1cM0 1dc0|66e5", - "Asia/Qatar|LMT +04 +03|-3q.8 -40 -30|012|-21Jfq.8 27BXq.8|96e4", - "Asia/Baku|LMT +03 +04 +05|-3j.o -30 -40 -50|01232323232323232323232123232323232323232323232323232323232323232|-1Pc3j.o 1jUoj.o WCL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 2pB0 1cM0 9Je0 1o00 11z0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|27e5", - "Asia/Bangkok|LMT BMT +07|-6G.4 -6G.4 -70|012|-3D8SG.4 1C000|15e6", - "Asia/Barnaul|LMT +06 +07 +08|-5z -60 -70 -80|0123232323232323232323212323232321212121212121212121212121212121212|-21S5z pCnz 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 2pB0 IM0 rX0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 p90 LE0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 8Hz0 3rd0|", - "Asia/Beirut|LMT EET EEST|-2m -20 -30|0121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-3D8Om 1BWom 1on0 1410 1db0 19B0 1in0 1ip0 WL0 1lQp0 11b0 1oN0 11b0 1oN0 11b0 1pd0 11b0 1oN0 11b0 q6N0 En0 1oN0 11b0 1oN0 11b0 1oN0 11b0 1pd0 11b0 1oN0 11b0 1op0 11b0 dA10 17b0 1iN0 17b0 1iN0 17b0 1iN0 17b0 1vB0 SL0 1mp0 13z0 1iN0 17b0 1iN0 17b0 1jd0 12n0 1a10 1cL0 1cN0 1cL0 1cN0 1cL0 1fB0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0|22e5", - "Asia/Bishkek|LMT +05 +06 +07|-4W.o -50 -60 -70|012323232323232323232321212121212121212121212121212|-1Pc4W.o eUnW.o 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 2e00 1tX0 17b0 1ip0 17b0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1cPu 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0|87e4", - "Asia/Brunei|LMT +0730 +08 +0820 +09|-7l.k -7u -80 -8k -90|0123232323232323242|-1KITl.k gDbP.k 6ynu AnE 1O0k AnE 1NAk AnE 1NAk AnE 1NAk AnE 1O0k AnE 1NAk AnE pAk 8Fz0|42e4", - "Asia/Kolkata|LMT HMT MMT IST +0630|-5R.s -5R.k -5l.a -5u -6u|01234343|-4Fg5R.s BKo0.8 1rDcw.a 1r2LP.a 1un0 HB0 7zX0|15e6", - "Asia/Chita|LMT +08 +09 +10|-7x.Q -80 -90 -a0|012323232323232323232321232323232323232323232323232323232323232312|-21Q7x.Q pAnx.Q 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 2pB0 IM0 rX0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 8Hz0 3re0|33e4", - "Asia/Choibalsan|LMT +07 +08 +10 +09|-7C -70 -80 -a0 -90|0123434343434343434343434343434343434343434343424242|-2APHC 2UkoC cKn0 1da0 1dd0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1fB0 1cL0 1cN0 1cL0 1cN0 1cL0 6hD0 11z0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 3Db0 h1f0 1cJ0 1cP0 1cJ0|38e3", - "Asia/Shanghai|LMT CST CDT|-85.H -80 -90|012121212121212121212121212121|-2M0U5.H Iuo5.H 18n0 OjB0 Rz0 11d0 1wL0 A10 8HX0 1G10 Tz0 1ip0 1jX0 1cN0 11b0 1oN0 aL0 1tU30 Rb0 1o10 11z0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0|23e6", - "Asia/Colombo|LMT MMT +0530 +06 +0630|-5j.o -5j.w -5u -60 -6u|012342432|-3D8Rj.o 13inX.Q 1rFbN.w 1zzu 7Apu 23dz0 11zu n3cu|22e5", - "Asia/Dhaka|LMT HMT +0630 +0530 +06 +07|-61.E -5R.k -6u -5u -60 -70|01232454|-3eLG1.E 26008.k 1unn.k HB0 m6n0 2kxbu 1i00|16e6", - "Asia/Damascus|LMT EET EEST +03|-2p.c -20 -30 -30|01212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212123|-21Jep.c Hep.c 17b0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1xRB0 11X0 1oN0 10L0 1pB0 11b0 1oN0 10L0 1mp0 13X0 1oN0 11b0 1pd0 11b0 1oN0 11b0 1oN0 11b0 1oN0 11b0 1pd0 11b0 1oN0 11b0 1oN0 11b0 1oN0 11b0 1pd0 11b0 1oN0 Nb0 1AN0 Nb0 bcp0 19X0 1gp0 19X0 3ld0 1xX0 Vd0 1Bz0 Sp0 1vX0 10p0 1dz0 1cN0 1cL0 1db0 1db0 1g10 1an0 1ap0 1db0 1fd0 1db0 1cN0 1db0 1dd0 1db0 1cp0 1dz0 1c10 1dX0 1cN0 1db0 1dd0 1db0 1cN0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1db0 1cN0 1db0 1cN0 19z0 1fB0 1qL0 11B0 1on0 Wp0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0|26e5", - "Asia/Dili|LMT +08 +09|-8m.k -80 -90|01212|-2le8m.k 1dnXm.k 1nfA0 Xld0|19e4", - "Asia/Dubai|LMT +04|-3F.c -40|01|-21JfF.c|39e5", - "Asia/Dushanbe|LMT +05 +06 +07|-4z.c -50 -60 -70|012323232323232323232321|-1Pc4z.c eUnz.c 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 2hB0|76e4", - "Asia/Famagusta|LMT EET EEST +03|-2f.M -20 -30 -30|0121212121212121212121212121212121212121212121212121212121212121212121212121212121212312121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-1Vc2f.M 2a3cf.M 1cL0 1qp0 Xz0 19B0 19X0 1fB0 1db0 1cp0 1cL0 1fB0 19X0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1fB0 1cL0 1cN0 1cL0 1cN0 1o30 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 15U0 2Ks0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|", - "Asia/Gaza|LMT EET EEST IST IDT|-2h.Q -20 -30 -20 -30|01212121212121212121212121212121212343434343434343434343434343434312121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-2MBCh.Q 1Azeh.Q MM0 iM0 4JA0 10o0 1pA0 10M0 1pA0 16o0 1jA0 16o0 1jA0 pBa0 Vz0 1oN0 11b0 1oO0 10N0 1pz0 10N0 1pb0 10N0 1pb0 10N0 1pb0 10N0 1pz0 10N0 1pb0 10N0 1pb0 11d0 1oL0 dW0 hfB0 Db0 1fB0 Rb0 bXB0 gM0 8Q00 IM0 1wo0 TX0 1HB0 IL0 1s10 10n0 1o10 WL0 1zd0 On0 1ld0 11z0 1o10 14n0 1o10 14n0 1nd0 12n0 1nd0 Xz0 1q10 12n0 M10 C00 17c0 1io0 17c0 1io0 17c0 1o00 1cL0 1fB0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 17c0 1io0 18N0 1bz0 19z0 1gp0 1610 1iL0 11z0 1o10 14o0 1lA1 SKX 1xd1 MKX 1AN0 1a00 1fA0 1cL0 1cN0 1nX0 1210 1nA0 1210 1qL0 WN0 1qL0 WN0 1qL0 11c0 1on0 11B0 1o00 11A0 1qo0 XA0 1qp0 1cN0 1cL0 17d0 1in0 14p0 1lb0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1lb0 14p0 1in0 17d0 1cL0 1cN0 19X0 e10 2L0 WN0 14n0 gN0 5z0 11B0 WL0 e10 bb0 11B0 TX0 e10 dX0 11B0 On0 gN0 gL0 11B0 Lz0 e10 pb0 WN0 IL0 e10 rX0 WN0 Db0 gN0 uL0 11B0 xz0 e10 An0 11B0 rX0 gN0 Db0 11B0 pb0 e10 Lz0 WN0 mn0 e10 On0 WN0 gL0 gN0 Rb0 11B0 bb0 e10 WL0 11B0 5z0 gN0 11z0 11B0 2L0 gN0 14n0 1fB0 1cL0 1a10 1fz0 14p0 1lb0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1nX0 14p0 1in0 17d0 1fz0 1a10 19X0 1fB0 17b0 e10 28L0 e10 25X0 gN0 25X0 e10 gL0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0|18e5", - "Asia/Hebron|LMT EET EEST IST IDT|-2k.n -20 -30 -20 -30|0121212121212121212121212121212121234343434343434343434343434343431212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-2MBCk.n 1Azek.n MM0 iM0 4JA0 10o0 1pA0 10M0 1pA0 16o0 1jA0 16o0 1jA0 pBa0 Vz0 1oN0 11b0 1oO0 10N0 1pz0 10N0 1pb0 10N0 1pb0 10N0 1pb0 10N0 1pz0 10N0 1pb0 10N0 1pb0 11d0 1oL0 dW0 hfB0 Db0 1fB0 Rb0 bXB0 gM0 8Q00 IM0 1wo0 TX0 1HB0 IL0 1s10 10n0 1o10 WL0 1zd0 On0 1ld0 11z0 1o10 14n0 1o10 14n0 1nd0 12n0 1nd0 Xz0 1q10 12n0 M10 C00 17c0 1io0 17c0 1io0 17c0 1o00 1cL0 1fB0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 17c0 1io0 18N0 1bz0 19z0 1gp0 1610 1iL0 12L0 1mN0 14o0 1lc0 Tb0 1xd1 MKX bB0 cn0 1cN0 1a00 1fA0 1cL0 1cN0 1nX0 1210 1nA0 1210 1qL0 WN0 1qL0 WN0 1qL0 11c0 1on0 11B0 1o00 11A0 1qo0 XA0 1qp0 1cN0 1cL0 17d0 1in0 14p0 1lb0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1lb0 14p0 1in0 17d0 1cL0 1cN0 19X0 e10 2L0 WN0 14n0 gN0 5z0 11B0 WL0 e10 bb0 11B0 TX0 e10 dX0 11B0 On0 gN0 gL0 11B0 Lz0 e10 pb0 WN0 IL0 e10 rX0 WN0 Db0 gN0 uL0 11B0 xz0 e10 An0 11B0 rX0 gN0 Db0 11B0 pb0 e10 Lz0 WN0 mn0 e10 On0 WN0 gL0 gN0 Rb0 11B0 bb0 e10 WL0 11B0 5z0 gN0 11z0 11B0 2L0 gN0 14n0 1fB0 1cL0 1a10 1fz0 14p0 1lb0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1nX0 14p0 1in0 17d0 1fz0 1a10 19X0 1fB0 17b0 e10 28L0 e10 25X0 gN0 25X0 e10 gL0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0|25e4", - "Asia/Ho_Chi_Minh|LMT PLMT +07 +08 +09|-76.u -76.u -70 -80 -90|0123423232|-2yC76.u bK00 1h7b6.u 5lz0 18o0 3Oq0 k5b0 aW00 BAM0|90e5", - "Asia/Hong_Kong|LMT HKT HKST HKWT JST|-7A.G -80 -90 -8u -90|0123412121212121212121212121212121212121212121212121212121212121212121|-2CFH0 1taO0 Hc0 xUu 9tBu 11z0 1tDu Rc0 1wo0 11A0 1cM0 11A0 1o00 11A0 1o00 11A0 1o00 14o0 1o00 11A0 1nX0 U10 1tz0 U10 1wn0 Rd0 1wn0 U10 1tz0 U10 1tz0 U10 1tz0 U10 1wn0 Rd0 1wn0 Rd0 1wn0 U10 1tz0 U10 1tz0 17d0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 s10 1Vz0 1cN0 1cL0 1cN0 1cL0 6fd0 14n0|73e5", - "Asia/Hovd|LMT +06 +07 +08|-66.A -60 -70 -80|012323232323232323232323232323232323232323232323232|-2APG6.A 2Uko6.A cKn0 1db0 1dd0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1fB0 1cL0 1cN0 1cL0 1cN0 1cL0 6hD0 11z0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 kEp0 1cJ0 1cP0 1cJ0|81e3", - "Asia/Irkutsk|LMT IMT +07 +08 +09|-6V.5 -6V.5 -70 -80 -90|012343434343434343434343234343434343434343434343434343434343434343|-3D8SV.5 1Bxc0 pjXV.5 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 2pB0 IM0 rX0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 8Hz0|60e4", - "Europe/Istanbul|LMT IMT EET EEST +03 +04|-1T.Q -1U.U -20 -30 -30 -40|01232323232323232323232323232323232323232323232345423232323232323232323232323232323232323232323232323232323232323234|-3D8NT.Q 1ePXW.U dzzU.U 11b0 8tB0 1on0 1410 1db0 19B0 1in0 3Rd0 Un0 1oN0 11b0 zSN0 CL0 mp0 1Vz0 1gN0 8yn0 1yp0 ML0 1kp0 17b0 1ip0 17b0 1fB0 19X0 1ip0 19X0 1ip0 17b0 qdB0 38L0 1jd0 Tz0 l6O0 11A0 WN0 1qL0 TB0 1tX0 U10 1tz0 11B0 1in0 17d0 z90 cne0 pb0 2Cp0 1800 14o0 1dc0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1a00 1fA0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WO0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 Xc0 1qo0 WM0 1qM0 11A0 1o00 1200 1nA0 11A0 1tA0 U00 15w0|13e6", - "Asia/Jakarta|LMT BMT +0720 +0730 +09 +08 WIB|-77.c -77.c -7k -7u -90 -80 -70|012343536|-49jH7.c 2hiLL.c luM0 mPzO 8vWu 6kpu 4PXu xhcu|31e6", - "Asia/Jayapura|LMT +09 +0930 WIT|-9m.M -90 -9u -90|0123|-1uu9m.M sMMm.M L4nu|26e4", - "Asia/Jerusalem|LMT JMT IST IDT IDDT|-2k.S -2k.E -20 -30 -40|012323232323232432323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232|-3D8Ok.S 1wvA0.e SyOk.E MM0 iM0 4JA0 10o0 1pA0 10M0 1pA0 16o0 1jA0 16o0 1jA0 3LA0 Eo0 oo0 1co0 1dA0 16o0 10M0 1jc0 1tA0 14o0 1cM0 1a00 11A0 1Nc0 Ao0 1Nc0 Ao0 1Ko0 LA0 1o00 WM0 EQK0 Db0 1fB0 Rb0 bXB0 gM0 8Q00 IM0 1wo0 TX0 1HB0 IL0 1s10 10n0 1o10 WL0 1zd0 On0 1ld0 11z0 1o10 14n0 1o10 14n0 1nd0 12n0 1nd0 Xz0 1q10 12n0 1hB0 1dX0 1ep0 1aL0 1eN0 17X0 1nf0 11z0 1tB0 19W0 1e10 17b0 1ep0 1gL0 18N0 1fz0 1eN0 17b0 1gq0 1gn0 19d0 1dz0 1c10 17X0 1hB0 1gn0 19d0 1dz0 1c10 17X0 1kp0 1dz0 1c10 1aL0 1eN0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0|81e4", - "Asia/Kabul|LMT +04 +0430|-4A.M -40 -4u|012|-3eLEA.M 2dTcA.M|46e5", - "Asia/Kamchatka|LMT +11 +12 +13|-ay.A -b0 -c0 -d0|012323232323232323232321232323232323232323232323232323232323212|-1SLKy.A ivXy.A 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 2pB0 IM0 rX0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 2sp0 WM0|18e4", - "Asia/Karachi|LMT +0530 +0630 +05 PKT PKST|-4s.c -5u -6u -50 -50 -60|012134545454|-2xoss.c 1qOKW.c 7zX0 eup0 LqMu 1fy00 1cL0 dK10 11b0 1610 1jX0|24e6", - "Asia/Kathmandu|LMT +0530 +0545|-5F.g -5u -5J|012|-21JhF.g 2EGMb.g|12e5", - "Asia/Khandyga|LMT +08 +09 +10 +11|-92.d -80 -90 -a0 -b0|0123232323232323232323212323232323232323232323232343434343434343432|-21Q92.d pAp2.d 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 2pB0 IM0 rX0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 qK0 yN0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 17V0 7zD0|66e2", - "Asia/Krasnoyarsk|LMT +06 +07 +08|-6b.q -60 -70 -80|01232323232323232323232123232323232323232323232323232323232323232|-21Hib.q prAb.q 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 2pB0 IM0 rX0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 8Hz0|10e5", - "Asia/Kuala_Lumpur|LMT SMT +07 +0720 +0730 +09 +08|-6T.p -6T.p -70 -7k -7u -90 -80|01234546|-2M0ST.p aIM0 17anT.p l5XE 17bO 8Fyu 1so10|71e5", - "Asia/Macau|LMT CST +09 +10 CDT|-7y.a -80 -90 -a0 -90|012323214141414141414141414141414141414141414141414141414141414141414141|-2CFHy.a 1uqKy.a PX0 1kn0 15B0 11b0 4Qq0 1oM0 11c0 1ko0 1u00 11A0 1cM0 11c0 1o00 11A0 1o00 11A0 1oo0 1400 1o00 11A0 1o00 U00 1tA0 U00 1wo0 Rc0 1wru U10 1tz0 U10 1tz0 U10 1tz0 U10 1wn0 Rd0 1wn0 Rd0 1wn0 U10 1tz0 U10 1tz0 17d0 1cK0 1cO0 1cK0 1cO0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 s10 1Vz0 1cN0 1cL0 1cN0 1cL0 6fd0 14n0|57e4", - "Asia/Magadan|LMT +10 +11 +12|-a3.c -a0 -b0 -c0|012323232323232323232321232323232323232323232323232323232323232312|-1Pca3.c eUo3.c 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 2pB0 IM0 rX0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 8Hz0 3Cq0|95e3", - "Asia/Makassar|LMT MMT +08 +09 WITA|-7V.A -7V.A -80 -90 -80|01234|-21JjV.A vfc0 myLV.A 8ML0|15e5", - "Asia/Manila|LMT LMT PST PDT JST|fU -84 -80 -90 -90|01232423232|-54m84 2clc0 1vfc4 AL0 cK10 65X0 mXB0 vX0 VK10 1db0|24e6", - "Asia/Nicosia|LMT EET EEST|-2d.s -20 -30|01212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-1Vc2d.s 2a3cd.s 1cL0 1qp0 Xz0 19B0 19X0 1fB0 1db0 1cp0 1cL0 1fB0 19X0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1fB0 1cL0 1cN0 1cL0 1cN0 1o30 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|32e4", - "Asia/Novokuznetsk|LMT +06 +07 +08|-5M.M -60 -70 -80|012323232323232323232321232323232323232323232323232323232323212|-1PctM.M eULM.M 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 2pB0 IM0 rX0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 2sp0 WM0|55e4", - "Asia/Novosibirsk|LMT +06 +07 +08|-5v.E -60 -70 -80|0123232323232323232323212323212121212121212121212121212121212121212|-21Qnv.E pAFv.E 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 2pB0 IM0 rX0 1cM0 1cM0 ml0 Os0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 8Hz0 4eN0|15e5", - "Asia/Omsk|LMT +05 +06 +07|-4R.u -50 -60 -70|01232323232323232323232123232323232323232323232323232323232323232|-224sR.u pMLR.u 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 2pB0 IM0 rX0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 8Hz0|12e5", - "Asia/Oral|LMT +03 +05 +06 +04|-3p.o -30 -50 -60 -40|01232323232323232424242424242424242424242424242|-1Pc3p.o eUop.o 23CK0 3Db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 2pB0 1cM0 1fA0 1cM0 1cM0 IM0 1EM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0|27e4", - "Asia/Pontianak|LMT PMT +0730 +09 +08 WITA WIB|-7h.k -7h.k -7u -90 -80 -80 -70|012324256|-2ua7h.k XE00 munL.k 8Rau 6kpu 4PXu xhcu Wqnu|23e4", - "Asia/Pyongyang|LMT KST JST KST|-8n -8u -90 -90|012313|-2um8n 97XR 1lTzu 2Onc0 6BA0|29e5", - "Asia/Qostanay|LMT +04 +05 +06|-4e.s -40 -50 -60|012323232323232323232123232323232323232323232323|-1Pc4e.s eUoe.s 23CL0 3Db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 2pB0 IM0 rX0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0|", - "Asia/Qyzylorda|LMT +04 +05 +06|-4l.Q -40 -50 -60|01232323232323232323232323232323232323232323232|-1Pc4l.Q eUol.Q 23CL0 3Db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 3ao0 1EM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 zQl0|73e4", - "Asia/Rangoon|LMT RMT +0630 +09|-6o.L -6o.L -6u -90|01232|-3D8So.L 1BnA0 SmnS.L 7j9u|48e5", - "Asia/Sakhalin|LMT +09 +11 +12 +10|-9u.M -90 -b0 -c0 -a0|01232323232323232323232423232323232424242424242424242424242424242|-2AGVu.M 1BoMu.M 1qFa0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 2pB0 IM0 rX0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 2pB0 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 8Hz0 3rd0|58e4", - "Asia/Samarkand|LMT +04 +05 +06|-4r.R -40 -50 -60|01232323232323232323232|-1Pc4r.R eUor.R 23CL0 3Db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0|36e4", - "Asia/Seoul|LMT KST JST KST KDT KDT|-8r.Q -8u -90 -90 -a0 -9u|012343434343151515151515134343|-2um8r.Q 97XV.Q 1m1zu 6CM0 Fz0 1kN0 14n0 1kN0 14L0 1zd0 On0 69B0 2I0u OL0 1FB0 Rb0 1qN0 TX0 1tB0 TX0 1tB0 TX0 1tB0 TX0 2ap0 12FBu 11A0 1o00 11A0|23e6", - "Asia/Srednekolymsk|LMT +10 +11 +12|-ae.Q -a0 -b0 -c0|01232323232323232323232123232323232323232323232323232323232323232|-1Pcae.Q eUoe.Q 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 2pB0 IM0 rX0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 8Hz0|35e2", - "Asia/Taipei|LMT CST JST CDT|-86 -80 -90 -90|012131313131313131313131313131313131313131|-30bk6 1FDc6 joM0 1yo0 Tz0 1ip0 1jX0 1cN0 11b0 1oN0 11b0 1oN0 11b0 1oN0 11b0 10N0 1BX0 10p0 1pz0 10p0 1pz0 10p0 1db0 1dd0 1db0 1cN0 1db0 1cN0 1db0 1cN0 1db0 1BB0 ML0 1Bd0 ML0 uq10 1db0 1cN0 1db0 97B0 AL0|74e5", - "Asia/Tashkent|LMT +05 +06 +07|-4B.b -50 -60 -70|012323232323232323232321|-1Pc4B.b eUnB.b 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 2pB0|23e5", - "Asia/Tbilisi|LMT TBMT +03 +04 +05|-2X.b -2X.b -30 -40 -50|01234343434343434343434323232343434343434343434323|-3D8OX.b 1LUM0 1jUnX.b WCL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 2pB0 1cK0 1cL0 1cN0 1cL0 1cN0 2pz0 1cL0 1fB0 3Nz0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 An0 Os0 WM0|11e5", - "Asia/Tehran|LMT TMT +0330 +0430 +04 +05|-3p.I -3p.I -3u -4u -40 -50|012345423232323232323232323232323232323232323232323232323232323232323232|-2btDp.I Llc0 1FHaT.I 1pc0 120u Rc0 XA0 Wou JX0 1dB0 1en0 pNB0 UL0 1cN0 1dz0 1cp0 1dz0 1cp0 1dz0 1cp0 1dz0 1cp0 1dz0 1cN0 1dz0 1cp0 1dz0 1cp0 1dz0 1cp0 1dz0 1cN0 1dz0 1cp0 1dz0 1cp0 1dz0 1cp0 1dz0 1cN0 1dz0 64p0 1dz0 1cN0 1dz0 1cp0 1dz0 1cp0 1dz0 1cp0 1dz0 1cN0 1dz0 1cp0 1dz0 1cp0 1dz0 1cp0 1dz0 1cN0 1dz0 1cp0 1dz0 1cp0 1dz0 1cp0 1dz0 1cN0 1dz0 1cp0 1dz0|14e6", - "Asia/Thimphu|LMT +0530 +06|-5W.A -5u -60|012|-Su5W.A 1BGMs.A|79e3", - "Asia/Tokyo|LMT JST JDT|-9i.X -90 -a0|0121212121|-3jE90 2qSo0 Rc0 1lc0 14o0 1zc0 Oo0 1zc0 Oo0|38e6", - "Asia/Tomsk|LMT +06 +07 +08|-5D.P -60 -70 -80|0123232323232323232323212323232323232323232323212121212121212121212|-21NhD.P pxzD.P 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 2pB0 IM0 rX0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 co0 1bB0 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 8Hz0 3Qp0|10e5", - "Asia/Ulaanbaatar|LMT +07 +08 +09|-77.w -70 -80 -90|012323232323232323232323232323232323232323232323232|-2APH7.w 2Uko7.w cKn0 1db0 1dd0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1fB0 1cL0 1cN0 1cL0 1cN0 1cL0 6hD0 11z0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 kEp0 1cJ0 1cP0 1cJ0|12e5", - "Asia/Ust-Nera|LMT +08 +09 +12 +11 +10|-9w.S -80 -90 -c0 -b0 -a0|012343434343434343434345434343434343434343434343434343434343434345|-21Q9w.S pApw.S 23CL0 1d90 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 2pB0 IM0 rX0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 17V0 7zD0|65e2", - "Asia/Vladivostok|LMT +09 +10 +11|-8L.v -90 -a0 -b0|01232323232323232323232123232323232323232323232323232323232323232|-1SJIL.v itXL.v 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 2pB0 IM0 rX0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 8Hz0|60e4", - "Asia/Yakutsk|LMT +08 +09 +10|-8C.W -80 -90 -a0|01232323232323232323232123232323232323232323232323232323232323232|-21Q8C.W pAoC.W 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 2pB0 IM0 rX0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 8Hz0|28e4", - "Asia/Yekaterinburg|LMT PMT +04 +05 +06|-42.x -3J.5 -40 -50 -60|012343434343434343434343234343434343434343434343434343434343434343|-2ag42.x 7mQh.s qBvJ.5 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 2pB0 IM0 rX0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 8Hz0|14e5", - "Asia/Yerevan|LMT +03 +04 +05|-2W -30 -40 -50|0123232323232323232323212121212323232323232323232323232323232|-1Pc2W 1jUnW WCL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 2pB0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 4RX0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0|13e5", - "Atlantic/Azores|LMT HMT -02 -01 +00 WET|1G.E 1S.w 20 10 0 0|01232323232323232323232323232323232323232323234323432343234323232323232323232323232323232323232323232343434343434343434343434343434345434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343|-3tomh.k 18aoh.k aPX0 Sp0 LX0 1vc0 Tc0 1uM0 SM0 1vc0 Tc0 1vc0 SM0 1vc0 6600 1co0 3E00 17c0 1fA0 1a00 1io0 1a00 1io0 17c0 3I00 17c0 1cM0 1cM0 3Fc0 1cM0 1a00 1fA0 1io0 17c0 1cM0 1cM0 1a00 1fA0 1io0 1qM0 Dc0 1tA0 1cM0 1dc0 1400 gL0 IM0 s10 U00 dX0 Rc0 pd0 Rc0 gL0 Oo0 pd0 Rc0 gL0 Oo0 pd0 14o0 1cM0 1cP0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 qIl0 1cM0 1fA0 1cM0 1cM0 1cN0 1cL0 1cN0 1cM0 1cM0 1cM0 1cM0 1cN0 1cL0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cL0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|25e4", - "Atlantic/Bermuda|LMT BMT BST AST ADT|4j.i 4j.i 3j.i 40 30|0121213434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343|-3eLvE.G 16mo0 1bb0 1i10 11X0 ru30 thbE.G 1PX0 11B0 1tz0 Rd0 1zb0 Op0 1zb0 3I10 Lz0 1EN0 FX0 1HB0 FX0 1Kp0 Db0 1Kp0 Db0 1Kp0 FX0 93d0 11z0 GAp0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|65e3", - "Atlantic/Canary|LMT -01 WET WEST|11.A 10 0 -10|01232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232|-1UtaW.o XPAW.o 1lAK0 1a10 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|54e4", - "Atlantic/Cape_Verde|LMT -02 -01|1y.4 20 10|01212|-2ldW0 1eEo0 7zX0 1djf0|50e4", - "Atlantic/Faroe|LMT WET WEST|r.4 0 -10|01212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-2uSnw.U 2Wgow.U 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|49e3", - "Atlantic/Madeira|LMT FMT -01 +00 +01 WET WEST|17.A 17.A 10 0 -10 0 -10|01232323232323232323232323232323232323232323234323432343234323232323232323232323232323232323232323232565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565|-3tomQ.o 18anQ.o aPX0 Sp0 LX0 1vc0 Tc0 1uM0 SM0 1vc0 Tc0 1vc0 SM0 1vc0 6600 1co0 3E00 17c0 1fA0 1a00 1io0 1a00 1io0 17c0 3I00 17c0 1cM0 1cM0 3Fc0 1cM0 1a00 1fA0 1io0 17c0 1cM0 1cM0 1a00 1fA0 1io0 1qM0 Dc0 1tA0 1cM0 1dc0 1400 gL0 IM0 s10 U00 dX0 Rc0 pd0 Rc0 gL0 Oo0 pd0 Rc0 gL0 Oo0 pd0 14o0 1cM0 1cP0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 qIl0 1cM0 1fA0 1cM0 1cM0 1cN0 1cL0 1cN0 1cM0 1cM0 1cM0 1cM0 1cN0 1cL0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|27e4", - "Atlantic/South_Georgia|LMT -02|2q.8 20|01|-3eLxx.Q|30", - "Atlantic/Stanley|LMT SMT -04 -03 -02|3P.o 3P.o 40 30 20|0123232323232323434323232323232323232323232323232323232323232323232323|-3eLw8.A S200 12bA8.A 19X0 1fB0 19X0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 Cn0 1Cc10 WL0 1qL0 U10 1tz0 2mN0 WN0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1tz0 U10 1tz0 WN0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1tz0 WN0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qN0 U10 1wn0 Rd0 1wn0 U10 1tz0 U10 1tz0 U10 1tz0 U10 1tz0 U10 1wn0 U10 1tz0 U10 1tz0 U10|21e2", - "Australia/Sydney|LMT AEST AEDT|-a4.Q -a0 -b0|01212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212|-32oW4.Q RlC4.Q xc0 10jc0 yM0 1cM0 1cM0 1fA0 1a00 17c00 LA0 1C00 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 14o0 1o00 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 U00 1qM0 WM0 1tA0 WM0 1tA0 U00 1tA0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 11A0 1o00 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 11A0 1o00 WM0 1qM0 14o0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0|40e5", - "Australia/Adelaide|LMT ACST ACST ACDT|-9e.k -90 -9u -au|012323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323|-32oVe.k ak0e.k H1Bu xc0 10jc0 yM0 1cM0 1cM0 1fA0 1a00 17c00 LA0 1C00 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 U00 1qM0 WM0 1tA0 WM0 1tA0 U00 1tA0 U00 1tA0 Oo0 1zc0 WM0 1qM0 Rc0 1zc0 U00 1tA0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 11A0 1o00 WM0 1qM0 14o0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0|11e5", - "Australia/Brisbane|LMT AEST AEDT|-ac.8 -a0 -b0|012121212121212121|-32Bmc.8 Ry2c.8 xc0 10jc0 yM0 1cM0 1cM0 1fA0 1a00 17c00 LA0 H1A0 Oo0 1zc0 Oo0 1zc0 Oo0|20e5", - "Australia/Broken_Hill|LMT AEST ACST ACST ACDT|-9p.M -a0 -90 -9u -au|0123434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434|-32oVp.M 3Lzp.M 6wp0 H1Bu xc0 10jc0 yM0 1cM0 1cM0 1fA0 1a00 17c00 LA0 1C00 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 14o0 1o00 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 U00 1qM0 WM0 1tA0 WM0 1tA0 U00 1tA0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 11A0 1o00 WM0 1qM0 14o0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0|18e3", - "Australia/Hobart|LMT AEST AEDT|-9N.g -a0 -b0|01212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212|-3109N.g Pk1N.g 1a00 1qM0 Oo0 1zc0 Oo0 TAo0 yM0 1cM0 1cM0 1fA0 1a00 VfA0 1cM0 1o00 Rc0 1wo0 Rc0 1wo0 U00 1wo0 LA0 1C00 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 11A0 1qM0 WM0 1qM0 Oo0 1zc0 Oo0 1zc0 Oo0 1wo0 WM0 1tA0 WM0 1tA0 U00 1tA0 U00 1tA0 11A0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 11A0 1o00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1cM0 1a00 1io0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0|21e4", - "Australia/Darwin|LMT ACST ACST ACDT|-8H.k -90 -9u -au|01232323232|-32oUH.k ajXH.k H1Bu xc0 10jc0 yM0 1cM0 1cM0 1fA0 1a00|12e4", - "Australia/Eucla|LMT +0845 +0945|-8z.s -8J -9J|01212121212121212121|-30nIz.s PkpO.s xc0 10jc0 yM0 1cM0 1cM0 1gSo0 Oo0 l5A0 Oo0 iJA0 G00 zU00 IM0 1qM0 11A0 1o00 11A0|368", - "Australia/Lord_Howe|LMT AEST +1030 +1130 +11|-aA.k -a0 -au -bu -b0|01232323232424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424|-32oWA.k 3tzAA.k 1zdu Rb0 1zd0 On0 1zd0 On0 1zd0 On0 1zd0 TXu 1qMu WLu 1tAu WLu 1tAu TXu 1tAu Onu 1zcu Onu 1zcu Onu 1zcu Rbu 1zcu Onu 1zcu Onu 1zcu 11zu 1o0u 11zu 1o0u 11zu 1o0u 11zu 1qMu WLu 11Au 1nXu 1qMu 11zu 1o0u 11zu 1o0u 11zu 1qMu WLu 1qMu 11zu 1o0u WLu 1qMu 14nu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1fzu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1fzu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1fzu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1fzu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1fzu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1fzu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1fzu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1fzu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1fzu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1fzu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1fzu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1fzu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1fzu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1fzu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1fzu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1fzu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu|347", - "Australia/Lindeman|LMT AEST AEDT|-9T.U -a0 -b0|0121212121212121212121|-32BlT.U Ry1T.U xc0 10jc0 yM0 1cM0 1cM0 1fA0 1a00 17c00 LA0 H1A0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0|10", - "Australia/Melbourne|LMT AEST AEDT|-9D.Q -a0 -b0|01212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212|-32oVD.Q RlBD.Q xc0 10jc0 yM0 1cM0 1cM0 1fA0 1a00 17c00 LA0 1C00 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 U00 1qM0 WM0 1qM0 11A0 1tA0 U00 1tA0 U00 1tA0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 11A0 1o00 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 11A0 1o00 WM0 1qM0 14o0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0|39e5", - "Australia/Perth|LMT AWST AWDT|-7H.o -80 -90|01212121212121212121|-30nHH.o PkpH.o xc0 10jc0 yM0 1cM0 1cM0 1gSo0 Oo0 l5A0 Oo0 iJA0 G00 zU00 IM0 1qM0 11A0 1o00 11A0|18e5", - "CET|CET CEST|-10 -20|01010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-2aFe0 11d0 1iO0 11A0 1o00 11A0 Qrc0 6i00 WM0 1fA0 1cM0 1cM0 1cM0 16M0 1gMM0 1a00 1fA0 1cM0 1cM0 1cM0 1fA0 1a00 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|", - "Pacific/Easter|LMT EMT -07 -06 -05|7h.s 7h.s 70 60 50|0123232323232323232323232323234343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434|-3eLsG.w 1HRc0 1s4IG.w WL0 1zd0 On0 1ip0 11z0 1o10 11z0 1qN0 WL0 1ld0 14n0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 WL0 1qN0 11z0 1o10 2pA0 11z0 1o10 11z0 1qN0 WL0 1qN0 WL0 1qN0 1cL0 1cN0 11z0 1o10 11z0 1qN0 WL0 1fB0 19X0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 17b0 1ip0 11z0 1ip0 1fz0 1fB0 11z0 1qN0 WL0 1qN0 WL0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 17b0 1ip0 11z0 1o10 19X0 1fB0 1nX0 G10 1EL0 Op0 1zb0 Rd0 1wn0 Rd0 46n0 Ap0 1Nb0 Ap0 1Nb0 Ap0 1zb0 11B0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 11B0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0|30e2", - "CST6CDT|CST CDT CWT CPT|60 50 50 50|010102301010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-261s0 1nX0 11B0 1nX0 SgN0 8x30 iw0 QwN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|", - "EET|EET EEST|-20 -30|010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|hDB0 1a00 1fA0 1cM0 1cM0 1cM0 1fA0 1a00 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|", - "Europe/Dublin|LMT DMT IST GMT BST IST|p.l p.l -y.D 0 -10 -10|012343434343435353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353|-3BHby.D 1ra20 Rc0 1fzy.D 14M0 1fc0 1g00 1co0 1dc0 1co0 1oo0 1400 1dc0 19A0 1io0 1io0 WM0 1o00 14o0 1o00 17c0 1io0 17c0 1fA0 1a00 1lc0 17c0 1io0 17c0 1fA0 1a00 1io0 17c0 1io0 17c0 1fA0 1cM0 1io0 17c0 1fA0 1a00 1io0 17c0 1io0 17c0 1fA0 1a00 1io0 1qM0 Dc0 g600 14o0 1wo0 17c0 1io0 11A0 1o00 17c0 1fA0 1a00 1fA0 1cM0 1fA0 1a00 17c0 1fA0 1a00 1io0 17c0 1lc0 17c0 1fA0 1a00 1io0 17c0 1io0 17c0 1fA0 1a00 1a00 1qM0 WM0 1qM0 11A0 1o00 WM0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1tA0 IM0 90o0 U00 1tA0 U00 1tA0 U00 1tA0 U00 1tA0 WM0 1qM0 WM0 1qM0 WM0 1tA0 U00 1tA0 U00 1tA0 11z0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1o00 14o0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|12e5", - "EST|EST|50|0||", - "EST5EDT|EST EDT EWT EPT|50 40 40 40|010102301010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-261t0 1nX0 11B0 1nX0 SgN0 8x40 iv0 QwN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|", - "Etc/GMT-0|GMT|0|0||", - "Etc/GMT-1|+01|-10|0||", - "Etc/GMT-10|+10|-a0|0||", - "Etc/GMT-11|+11|-b0|0||", - "Etc/GMT-12|+12|-c0|0||", - "Etc/GMT-13|+13|-d0|0||", - "Etc/GMT-14|+14|-e0|0||", - "Etc/GMT-2|+02|-20|0||", - "Etc/GMT-3|+03|-30|0||", - "Etc/GMT-4|+04|-40|0||", - "Etc/GMT-5|+05|-50|0||", - "Etc/GMT-6|+06|-60|0||", - "Etc/GMT-7|+07|-70|0||", - "Etc/GMT-8|+08|-80|0||", - "Etc/GMT-9|+09|-90|0||", - "Etc/GMT+1|-01|10|0||", - "Etc/GMT+10|-10|a0|0||", - "Etc/GMT+11|-11|b0|0||", - "Etc/GMT+12|-12|c0|0||", - "Etc/GMT+2|-02|20|0||", - "Etc/GMT+3|-03|30|0||", - "Etc/GMT+4|-04|40|0||", - "Etc/GMT+5|-05|50|0||", - "Etc/GMT+6|-06|60|0||", - "Etc/GMT+7|-07|70|0||", - "Etc/GMT+8|-08|80|0||", - "Etc/GMT+9|-09|90|0||", - "Etc/UTC|UTC|0|0||", - "Europe/Brussels|LMT BMT WET CET CEST WEST|-h.u -h.u 0 -10 -20 -10|012343434325252525252525252525252525252525252525252525434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343|-3D8Mh.u u1Ah.u SO00 3zX0 11c0 1iO0 11A0 1o00 11A0 my0 Ic0 1qM0 Rc0 1EM0 UM0 1u00 10o0 1io0 1io0 17c0 1a00 1fA0 1cM0 1cM0 1io0 17c0 1fA0 1a00 1io0 1a30 1io0 17c0 1fA0 1a00 1io0 17c0 1cM0 1cM0 1a00 1io0 1cM0 1cM0 1a00 1fA0 1io0 17c0 1cM0 1cM0 1a00 1fA0 1io0 1qM0 Dc0 y00 5Wn0 WM0 1fA0 1cM0 16M0 1iM0 16M0 1C00 Uo0 1eeo0 1a00 1fA0 1cM0 1cM0 1cM0 1fA0 1a00 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|21e5", - "Europe/Andorra|LMT WET CET CEST|-6.4 0 -10 -20|0123232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232|-2M0M6.4 1Pnc6.4 1xIN0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|79e3", - "Europe/Astrakhan|LMT +03 +04 +05|-3c.c -30 -40 -50|012323232323232323212121212121212121212121212121212121212121212|-1Pcrc.c eUMc.c 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 2pB0 1cM0 1fA0 1cM0 3Co0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 8Hz0 3rd0|10e5", - "Europe/Athens|LMT AMT EET EEST CEST CET|-1y.Q -1y.Q -20 -30 -20 -10|0123234545232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232|-30SNy.Q OMM1 CNbx.Q mn0 kU10 9b0 3Es0 Xa0 1fb0 1dd0 k3X0 Nz0 SCp0 1vc0 SO0 1cM0 1a00 1ao0 1fc0 1a10 1fG0 1cg0 1dX0 1bX0 1cQ0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|35e5", - "Europe/London|LMT GMT BST BDST|1.f 0 -10 -20|01212121212121212121212121212121212121212121212121232323232321212321212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-4VgnW.J 2KHdW.J Rc0 1fA0 14M0 1fc0 1g00 1co0 1dc0 1co0 1oo0 1400 1dc0 19A0 1io0 1io0 WM0 1o00 14o0 1o00 17c0 1io0 17c0 1fA0 1a00 1lc0 17c0 1io0 17c0 1fA0 1a00 1io0 17c0 1io0 17c0 1fA0 1cM0 1io0 17c0 1fA0 1a00 1io0 17c0 1io0 17c0 1fA0 1a00 1io0 1qM0 Dc0 2Rz0 Dc0 1zc0 Oo0 1zc0 Rc0 1wo0 17c0 1iM0 FA0 xB0 1fA0 1a00 14o0 bb0 LA0 xB0 Rc0 1wo0 11A0 1o00 17c0 1fA0 1a00 1fA0 1cM0 1fA0 1a00 17c0 1fA0 1a00 1io0 17c0 1lc0 17c0 1fA0 1a00 1io0 17c0 1io0 17c0 1fA0 1a00 1a00 1qM0 WM0 1qM0 11A0 1o00 WM0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1tA0 IM0 90o0 U00 1tA0 U00 1tA0 U00 1tA0 U00 1tA0 WM0 1qM0 WM0 1qM0 WM0 1tA0 U00 1tA0 U00 1tA0 11z0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1o00 14o0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|10e6", - "Europe/Belgrade|LMT CET CEST|-1m -10 -20|012121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-3topm 2juLm 3IP0 WM0 1fA0 1cM0 1cM0 1rc0 Qo0 1vmo0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|12e5", - "Europe/Prague|LMT PMT CET CEST GMT|-V.I -V.I -10 -20 0|0123232323232323232423232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232|-4QbAV.I 1FDc0 XPaV.I 11d0 1iO0 11A0 1o00 11A0 Qrc0 6i00 WM0 1fA0 1cM0 1cM0 1cM0 1cM0 1qM0 11c0 mp0 xA0 mn0 17c0 1io0 17c0 1fc0 1ao0 1bNc0 1cM0 1fA0 1a00 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|13e5", - "Europe/Bucharest|LMT BMT EET EEST|-1I.o -1I.o -20 -30|01232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232|-3awpI.o 1AU00 20LI.o RA0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1Axc0 On0 1fA0 1a10 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cK0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cL0 1cN0 1cL0 1fB0 1nX0 11E0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|19e5", - "Europe/Budapest|LMT CET CEST|-1g.k -10 -20|01212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-3cK1g.k 124Lg.k 11d0 1iO0 11A0 1o00 11A0 1oo0 11c0 1lc0 17c0 O1V0 3Nf0 WM0 1fA0 1cM0 1cM0 1oJ0 1dd0 1020 1fX0 1cp0 1cM0 1cM0 1cM0 1fA0 1a00 bhy0 Rb0 1wr0 Rc0 1C00 LA0 1C00 LA0 SNW0 1a00 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cO0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|17e5", - "Europe/Zurich|LMT BMT CET CEST|-y.8 -t.K -10 -20|0123232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232|-4HyMy.8 1Dw04.m 1SfAt.K 11A0 1o00 11A0 1xG10 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|38e4", - "Europe/Chisinau|LMT CMT BMT EET EEST CEST CET MSK MSD|-1T.k -1T -1I.o -20 -30 -20 -10 -30 -40|0123434343434343434345656578787878787878787878434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343|-3D8NT.k 1wNA0.k wGMa.A 20LI.o RA0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 27A0 2en0 39g0 WM0 1fA0 1cM0 V90 1t7z0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 gL0 WO0 1cM0 1cM0 1cK0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1fB0 1nX0 11D0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|67e4", - "Europe/Gibraltar|LMT GMT BST BDST CET CEST|l.o 0 -10 -20 -10 -20|0121212121212121212121212121212121212121212121212123232323232121232121212121212121212145454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454|-3BHbC.A 1ra1C.A Rc0 1fA0 14M0 1fc0 1g00 1co0 1dc0 1co0 1oo0 1400 1dc0 19A0 1io0 1io0 WM0 1o00 14o0 1o00 17c0 1io0 17c0 1fA0 1a00 1lc0 17c0 1io0 17c0 1fA0 1a00 1io0 17c0 1io0 17c0 1fA0 1cM0 1io0 17c0 1fA0 1a00 1io0 17c0 1io0 17c0 1fA0 1a00 1io0 1qM0 Dc0 2Rz0 Dc0 1zc0 Oo0 1zc0 Rc0 1wo0 17c0 1iM0 FA0 xB0 1fA0 1a00 14o0 bb0 LA0 xB0 Rc0 1wo0 11A0 1o00 17c0 1fA0 1a00 1fA0 1cM0 1fA0 1a00 17c0 1fA0 1a00 1io0 17c0 1lc0 17c0 1fA0 10Jz0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|30e3", - "Europe/Helsinki|LMT HMT EET EEST|-1D.N -1D.N -20 -30|01232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232|-3H0ND.N 1Iu00 OULD.N 1dA0 1xGq0 1cM0 1cM0 1cM0 1cN0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|12e5", - "Europe/Kaliningrad|LMT CET CEST EET EEST MSK MSD +03|-1m -10 -20 -20 -30 -30 -40 -30|012121212121212343565656565656565654343434343434343434343434343434343434343434373|-36Rdm UbXm 11d0 1iO0 11A0 1o00 11A0 Qrc0 6i00 WM0 1fA0 1cM0 1cM0 1cM0 390 7A0 1en0 12N0 1pbb0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cN0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 8Hz0|44e4", - "Europe/Kiev|LMT KMT EET MSK CEST CET MSD EEST|-22.4 -22.4 -20 -30 -20 -10 -40 -30|01234545363636363636363636367272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272|-3D8O2.4 1LUM0 eUo2.4 rnz0 2Hg0 WM0 1fA0 da0 1v4m0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 Db0 3220 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o10 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|34e5", - "Europe/Kirov|LMT +03 +04 +05 MSD MSK MSK|-3i.M -30 -40 -50 -40 -30 -40|0123232323232323232454524545454545454545454545454545454545454565|-22WM0 qH90 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cN0 1cM0 1fA0 1cM0 2pz0 1cN0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 8Hz0|48e4", - "Europe/Lisbon|LMT WET WEST WEMT CET CEST|A.J 0 -10 -20 -10 -20|01212121212121212121212121212121212121212121232123212321232121212121212121212121212121212121212121214121212121212121212121212121212124545454212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-2le00 aPX0 Sp0 LX0 1vc0 Tc0 1uM0 SM0 1vc0 Tc0 1vc0 SM0 1vc0 6600 1co0 3E00 17c0 1fA0 1a00 1io0 1a00 1io0 17c0 3I00 17c0 1cM0 1cM0 3Fc0 1cM0 1a00 1fA0 1io0 17c0 1cM0 1cM0 1a00 1fA0 1io0 1qM0 Dc0 1tA0 1cM0 1dc0 1400 gL0 IM0 s10 U00 dX0 Rc0 pd0 Rc0 gL0 Oo0 pd0 Rc0 gL0 Oo0 pd0 14o0 1cM0 1cP0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 pvy0 1cM0 1cM0 1fA0 1cM0 1cM0 1cN0 1cL0 1cN0 1cM0 1cM0 1cM0 1cM0 1cN0 1cL0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|27e5", - "Europe/Madrid|LMT WET WEST WEMT CET CEST|e.I 0 -10 -20 -10 -20|0121212121212121212321454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454|-2M0M0 G5z0 19B0 1cL0 1dd0 b1z0 18p0 3HX0 17d0 1fz0 1a10 1io0 1a00 1in0 17d0 iIn0 Hd0 1cL0 bb0 1200 2s20 14n0 5aL0 Mp0 1vz0 17d0 1in0 17d0 1in0 17d0 1in0 17d0 6hX0 11B0 XHX0 1a10 1fz0 1a10 19X0 1cN0 1fz0 1a10 1fC0 1cM0 1cM0 1cM0 1fA0 1a00 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|62e5", - "Europe/Malta|LMT CET CEST|-W.4 -10 -20|01212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-35rcW.4 SXzW.4 Lz0 1cN0 1db0 1410 1on0 Wp0 1qL0 17d0 1cL0 M3B0 5M20 WM0 1fA0 1co0 17c0 1iM0 16m0 1de0 1lc0 14m0 1lc0 WO0 1qM0 GTW0 On0 1C10 LA0 1C00 LA0 1EM0 LA0 1C00 LA0 1zc0 Oo0 1C00 Oo0 1co0 1cM0 1lA0 Xc0 1qq0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1iN0 19z0 1fB0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|42e4", - "Europe/Minsk|LMT MMT EET MSK CEST CET MSD EEST +03|-1O.g -1O -20 -30 -20 -10 -40 -30 -30|012345454363636363636363636372727272727272727272727272727272727272728|-3D8NO.g 1LUM0.g eUnO qNX0 3gQ0 WM0 1fA0 1cM0 Al0 1tsn0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 3Fc0 1cN0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0|19e5", - "Europe/Paris|LMT PMT WET WEST CEST CET WEMT|-9.l -9.l 0 -10 -20 -10 -20|01232323232323232323232323232323232323232323232323234545463654545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545|-3bQ09.l MDA0 cNb9.l HA0 19A0 1iM0 11c0 1oo0 Wo0 1rc0 QM0 1EM0 UM0 1u00 10o0 1io0 1wo0 Rc0 1a00 1fA0 1cM0 1cM0 1io0 17c0 1fA0 1a00 1io0 1a00 1io0 17c0 1fA0 1a00 1io0 17c0 1cM0 1cM0 1a00 1io0 1cM0 1cM0 1a00 1fA0 1io0 17c0 1cM0 1cM0 1a00 1fA0 1io0 1qM0 Df0 Ik0 5M30 WM0 1fA0 1cM0 Vx0 hB0 1aq0 16M0 1ekn0 1cL0 1fC0 1a00 1fA0 1cM0 1cM0 1cM0 1fA0 1a00 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|11e6", - "Europe/Moscow|LMT MMT MMT MST MDST MSD MSK +05 EET EEST MSK|-2u.h -2u.h -2v.j -3v.j -4v.j -40 -30 -50 -20 -30 -40|01232434565756865656565656565656565698656565656565656565656565656565656565656a6|-3D8Ou.h 1sQM0 2pyW.W 1bA0 11X0 GN0 1Hb0 c4v.j ik0 3DA0 dz0 15A0 c10 2q10 iM10 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cN0 IM0 rX0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 8Hz0|16e6", - "Europe/Riga|LMT RMT LST EET MSK CEST CET MSD EEST|-1A.y -1A.y -2A.y -20 -30 -20 -10 -40 -30|0121213456565647474747474747474838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383|-3D8NA.y 1xde0 11A0 1iM0 ko0 gWm0 yDXA.y 2bX0 3fE0 WM0 1fA0 1cM0 1cM0 4m0 1sLy0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cN0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cN0 1o00 11A0 1o00 11A0 1qM0 3oo0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|64e4", - "Europe/Rome|LMT RMT CET CEST|-N.U -N.U -10 -20|012323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232|-4aU0N.U 15snN.U T000 Lz0 1cN0 1db0 1410 1on0 Wp0 1qL0 17d0 1cL0 M3B0 5M20 WM0 1fA0 1cM0 16M0 1iM0 16m0 1de0 1lc0 14m0 1lc0 WO0 1qM0 GTW0 On0 1C10 LA0 1C00 LA0 1EM0 LA0 1C00 LA0 1zc0 Oo0 1C00 Oo0 1C00 LA0 1zc0 Oo0 1C00 LA0 1C00 LA0 1zc0 Oo0 1C00 Oo0 1zc0 Oo0 1fC0 1a00 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|39e5", - "Europe/Samara|LMT +03 +04 +05|-3k.k -30 -40 -50|0123232323232323232121232323232323232323232323232323232323212|-22WM0 qH90 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 2pB0 1cM0 1fA0 2y10 14m0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 2sp0 WM0|12e5", - "Europe/Saratov|LMT +03 +04 +05|-34.i -30 -40 -50|012323232323232321212121212121212121212121212121212121212121212|-22WM0 qH90 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 2pB0 1cM0 1cM0 1cM0 1fA0 1cM0 3Co0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 8Hz0 5810|", - "Europe/Simferopol|LMT SMT EET MSK CEST CET MSD EEST MSK|-2g.o -2g -20 -30 -20 -10 -40 -30 -40|0123454543636363636363636363272727636363727272727272727272727272727272727283|-3D8Og.o 1LUM0.o eUog rEn0 2qs0 WM0 1fA0 1cM0 3V0 1u0L0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1Q00 4eN0 1cM0 1cM0 1cM0 1cM0 dV0 WO0 1cM0 1cM0 1fy0 1o30 11B0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11z0 1nW0|33e4", - "Europe/Sofia|LMT IMT EET CET CEST EEST|-1x.g -1U.U -20 -10 -20 -30|0123434325252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252|-3D8Nx.g AiLA.k 1UFeU.U WM0 1fA0 1cM0 1cM0 1cN0 1mKH0 1dd0 1fb0 1ap0 1fb0 1a20 1fy0 1a30 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cK0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1fB0 1nX0 11E0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|12e5", - "Europe/Tallinn|LMT TMT CET CEST EET MSK MSD EEST|-1D -1D -10 -20 -20 -30 -40 -30|0123214532323565656565656565657474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474|-3D8ND 1wI00 teD 11A0 1Ta0 4rXl KSLD 2FX0 2Jg0 WM0 1fA0 1cM0 18J0 1sTX0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cN0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o10 11A0 1qM0 5QM0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|41e4", - "Europe/Tirane|LMT CET CEST|-1j.k -10 -20|01212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-2glBj.k 14pcj.k 5LC0 WM0 4M0 1fCK0 10n0 1op0 11z0 1pd0 11z0 1qN0 WL0 1qp0 Xb0 1qp0 Xb0 1qp0 11z0 1lB0 11z0 1qN0 11z0 1iN0 16n0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|42e4", - "Europe/Ulyanovsk|LMT +03 +04 +05 +02|-3d.A -30 -40 -50 -20|01232323232323232321214121212121212121212121212121212121212121212|-22WM0 qH90 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 2pB0 1cM0 1fA0 2pB0 IM0 rX0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 8Hz0 3rd0|13e5", - "Europe/Vienna|LMT CET CEST|-15.l -10 -20|01212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-36Rd5.l UbX5.l 11d0 1iO0 11A0 1o00 11A0 3KM0 14o0 LA00 6i00 WM0 1fA0 1cM0 1cM0 1cM0 400 2qM0 1ao0 1co0 1cM0 1io0 17c0 1gHa0 19X0 1cP0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|18e5", - "Europe/Vilnius|LMT WMT KMT CET EET MSK CEST MSD EEST|-1F.g -1o -1z.A -10 -20 -30 -20 -40 -30|0123435636365757575757575757584848484848484848463648484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484|-3D8NF.g 1u5Ah.g 6ILM.o 1Ooz.A zz0 Mfd0 29W0 3is0 WM0 1fA0 1cM0 LV0 1tgL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cN0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11B0 1o00 11A0 1qM0 8io0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|54e4", - "Europe/Volgograd|LMT +03 +04 +05 MSD MSK MSK|-2V.E -30 -40 -50 -40 -30 -40|012323232323232324545452454545454545454545454545454545454545456525|-21IqV.E psLV.E 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cN0 1cM0 1cM0 1cM0 1fA0 1cM0 2pz0 1cN0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 8Hz0 9Jd0 5gn0|10e5", - "Europe/Warsaw|LMT WMT CET CEST EET EEST|-1o -1o -10 -20 -20 -30|0123232345423232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232|-3D8No 1qDA0 1LXo 11d0 1iO0 11A0 1o00 11A0 1on0 11A0 6zy0 HWP0 5IM0 WM0 1fA0 1cM0 1dz0 1mL0 1en0 15B0 1aq0 1nA0 11A0 1io0 17c0 1fA0 1a00 iDX0 LA0 1cM0 1cM0 1C00 Oo0 1cM0 1cM0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1C00 LA0 uso0 1a00 1fA0 1cM0 1cM0 1cM0 1fA0 1a00 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cN0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|17e5", - "HST|HST|a0|0||", - "Indian/Chagos|LMT +05 +06|-4N.E -50 -60|012|-2xosN.E 3AGLN.E|30e2", - "Indian/Maldives|LMT MMT +05|-4S -4S -50|012|-3D8QS 3eLA0|35e4", - "Indian/Mauritius|LMT +04 +05|-3O -40 -50|012121|-2xorO 34unO 14L0 12kr0 11z0|15e4", - "Pacific/Kwajalein|LMT +11 +10 +09 -12 +12|-b9.k -b0 -a0 -90 c0 -c0|0123145|-2M0X9.k 1rDA9.k akp0 6Up0 12ry0 Wan0|14e3", - "MET|MET MEST|-10 -20|01010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-2aFe0 11d0 1iO0 11A0 1o00 11A0 Qrc0 6i00 WM0 1fA0 1cM0 1cM0 1cM0 16M0 1gMM0 1a00 1fA0 1cM0 1cM0 1cM0 1fA0 1a00 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|", - "MST|MST|70|0||", - "MST7MDT|MST MDT MWT MPT|70 60 60 60|010102301010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-261r0 1nX0 11B0 1nX0 SgN0 8x20 ix0 QwN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|", - "Pacific/Chatham|LMT +1215 +1245 +1345|-cd.M -cf -cJ -dJ|0123232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323|-46jMd.M 37RbW.M 1adef IM0 1C00 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1qM0 14o0 1lc0 14o0 1lc0 14o0 1lc0 17c0 1io0 17c0 1io0 17c0 1io0 17c0 1lc0 14o0 1lc0 14o0 1lc0 17c0 1io0 17c0 1io0 17c0 1lc0 14o0 1lc0 14o0 1lc0 17c0 1io0 17c0 1io0 17c0 1io0 17c0 1io0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00|600", - "Pacific/Apia|LMT LMT -1130 -11 -10 +14 +13|-cx.4 bq.U bu b0 a0 -e0 -d0|012343456565656565656565656|-38Fox.4 J1A0 1yW03.4 2rRbu 1ff0 1a00 CI0 AQ0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0|37e3", - "Pacific/Bougainville|LMT PMMT +10 +09 +11|-am.g -9M.w -a0 -90 -b0|012324|-3D8Wm.g AvAx.I 1TCLM.w 7CN0 2MQp0|18e4", - "Pacific/Efate|LMT +11 +12|-bd.g -b0 -c0|012121212121212121212121|-2l9nd.g 2uNXd.g Dc0 n610 1cL0 1cN0 1cL0 1fB0 19X0 1fB0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1fB0 Lz0 1Nd0 An0|66e3", - "Pacific/Enderbury|-00 -12 -11 +13|0 c0 b0 -d0|0123|-1iIo0 1GsA0 B7X0|1", - "Pacific/Fakaofo|LMT -11 +13|bo.U b0 -d0|012|-2M0Az.4 4ufXz.4|483", - "Pacific/Fiji|LMT +12 +13|-bT.I -c0 -d0|012121212121212121212121212121|-2bUzT.I 3m8NT.I LA0 1EM0 IM0 nJc0 LA0 1o00 Rc0 1wo0 Ao0 1Nc0 Ao0 1Q00 xz0 1SN0 uM0 1SM0 uM0 1VA0 s00 1VA0 s00 1VA0 s00 20o0 pc0 2hc0 bc0|88e4", - "Pacific/Tarawa|LMT +12|-bw.4 -c0|01|-2M0Xw.4|29e3", - "Pacific/Galapagos|LMT -05 -06|5W.o 50 60|01212|-1yVS1.A 2dTz1.A gNd0 rz0|25e3", - "Pacific/Gambier|LMT -09|8X.M 90|01|-2jof0.c|125", - "Pacific/Guadalcanal|LMT +11|-aD.M -b0|01|-2joyD.M|11e4", - "Pacific/Guam|LMT LMT GST +09 GDT ChST|el -9D -a0 -90 -b0 -a0|0123242424242424242425|-54m9D 2glc0 1DFbD 6pB0 AhB0 3QL0 g2p0 3p91 WOX rX0 1zd0 Rb0 1wp0 Rb0 5xd0 rX0 5sN0 zb1 1C0X On0 ULb0|17e4", - "Pacific/Honolulu|LMT HST HDT HWT HPT HST|av.q au 9u 9u 9u a0|01213415|-3061s.y 1uMdW.y 8x0 lef0 8wWu iAu 46p0|37e4", - "Pacific/Kiritimati|LMT -1040 -10 +14|at.k aE a0 -e0|0123|-2M0Bu.E 3bIMa.E B7Xk|51e2", - "Pacific/Kosrae|LMT LMT +11 +09 +10 +12|d8.4 -aP.U -b0 -90 -a0 -c0|0123243252|-54maP.U 2glc0 xsnP.U axC0 HBy0 akp0 axd0 WOK0 1bdz0|66e2", - "Pacific/Marquesas|LMT -0930|9i 9u|01|-2joeG|86e2", - "Pacific/Pago_Pago|LMT LMT SST|-cB.c bm.M b0|012|-38FoB.c J1A0|37e2", - "Pacific/Nauru|LMT +1130 +09 +12|-b7.E -bu -90 -c0|01213|-1Xdn7.E QCnB.E 7mqu 1lnbu|10e3", - "Pacific/Niue|LMT -1120 -11|bj.E bk b0|012|-FScE.k suo0.k|12e2", - "Pacific/Norfolk|LMT +1112 +1130 +1230 +11 +12|-bb.Q -bc -bu -cu -b0 -c0|0123245454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545|-2M0Xb.Q 21ILX.Q W01G Oo0 1COo0 9Jcu 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0|25e4", - "Pacific/Noumea|LMT +11 +12|-b5.M -b0 -c0|01212121|-2l9n5.M 2EqM5.M xX0 1PB0 yn0 HeP0 Ao0|98e3", - "Pacific/Palau|LMT LMT +09|f2.4 -8V.U -90|012|-54m8V.U 2glc0|21e3", - "Pacific/Pitcairn|LMT -0830 -08|8E.k 8u 80|012|-2M0Dj.E 3UVXN.E|56", - "Pacific/Rarotonga|LMT LMT -1030 -0930 -10|-dk.U aD.4 au 9u a0|01234343434343434343434343434|-2Otpk.U 28zc0 13tbO.U IL0 1zcu Onu 1zcu Onu 1zcu Rbu 1zcu Onu 1zcu Onu 1zcu Onu 1zcu Onu 1zcu Onu 1zcu Rbu 1zcu Onu 1zcu Onu 1zcu Onu|13e3", - "Pacific/Tahiti|LMT -10|9W.g a0|01|-2joe1.I|18e4", - "Pacific/Tongatapu|LMT +1220 +13 +14|-cj.c -ck -d0 -e0|01232323232|-XbMj.c BgLX.c 1yndk 15A0 1wo0 xz0 1Q10 xz0 zWN0 s00|75e3", - "PST8PDT|PST PDT PWT PPT|80 70 70 70|010102301010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-261q0 1nX0 11B0 1nX0 SgN0 8x10 iy0 QwN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|", - "WET|WET WEST|0 -10|010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|hDB0 1a00 1fA0 1cM0 1cM0 1cM0 1fA0 1a00 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|" - ], - "links": [ - "Africa/Abidjan|Africa/Accra", - "Africa/Abidjan|Africa/Bamako", - "Africa/Abidjan|Africa/Banjul", - "Africa/Abidjan|Africa/Conakry", - "Africa/Abidjan|Africa/Dakar", - "Africa/Abidjan|Africa/Freetown", - "Africa/Abidjan|Africa/Lome", - "Africa/Abidjan|Africa/Nouakchott", - "Africa/Abidjan|Africa/Ouagadougou", - "Africa/Abidjan|Africa/Timbuktu", - "Africa/Abidjan|Atlantic/Reykjavik", - "Africa/Abidjan|Atlantic/St_Helena", - "Africa/Abidjan|Iceland", - "Africa/Cairo|Egypt", - "Africa/Johannesburg|Africa/Maseru", - "Africa/Johannesburg|Africa/Mbabane", - "Africa/Lagos|Africa/Bangui", - "Africa/Lagos|Africa/Brazzaville", - "Africa/Lagos|Africa/Douala", - "Africa/Lagos|Africa/Kinshasa", - "Africa/Lagos|Africa/Libreville", - "Africa/Lagos|Africa/Luanda", - "Africa/Lagos|Africa/Malabo", - "Africa/Lagos|Africa/Niamey", - "Africa/Lagos|Africa/Porto-Novo", - "Africa/Maputo|Africa/Blantyre", - "Africa/Maputo|Africa/Bujumbura", - "Africa/Maputo|Africa/Gaborone", - "Africa/Maputo|Africa/Harare", - "Africa/Maputo|Africa/Kigali", - "Africa/Maputo|Africa/Lubumbashi", - "Africa/Maputo|Africa/Lusaka", - "Africa/Nairobi|Africa/Addis_Ababa", - "Africa/Nairobi|Africa/Asmara", - "Africa/Nairobi|Africa/Asmera", - "Africa/Nairobi|Africa/Dar_es_Salaam", - "Africa/Nairobi|Africa/Djibouti", - "Africa/Nairobi|Africa/Kampala", - "Africa/Nairobi|Africa/Mogadishu", - "Africa/Nairobi|Indian/Antananarivo", - "Africa/Nairobi|Indian/Comoro", - "Africa/Nairobi|Indian/Mayotte", - "Africa/Tripoli|Libya", - "America/Adak|America/Atka", - "America/Adak|US/Aleutian", - "America/Anchorage|US/Alaska", - "America/Argentina/Buenos_Aires|America/Buenos_Aires", - "America/Argentina/Catamarca|America/Argentina/ComodRivadavia", - "America/Argentina/Catamarca|America/Catamarca", - "America/Argentina/Cordoba|America/Cordoba", - "America/Argentina/Cordoba|America/Rosario", - "America/Argentina/Jujuy|America/Jujuy", - "America/Argentina/Mendoza|America/Mendoza", - "America/Chicago|US/Central", - "America/Denver|America/Shiprock", - "America/Denver|Navajo", - "America/Denver|US/Mountain", - "America/Detroit|US/Michigan", - "America/Edmonton|America/Yellowknife", - "America/Edmonton|Canada/Mountain", - "America/Fort_Wayne|America/Indiana/Indianapolis", - "America/Fort_Wayne|America/Indianapolis", - "America/Fort_Wayne|US/East-Indiana", - "America/Godthab|America/Nuuk", - "America/Halifax|Canada/Atlantic", - "America/Havana|Cuba", - "America/Indiana/Knox|America/Knox_IN", - "America/Indiana/Knox|US/Indiana-Starke", - "America/Iqaluit|America/Pangnirtung", - "America/Jamaica|Jamaica", - "America/Kentucky/Louisville|America/Louisville", - "America/Los_Angeles|US/Pacific", - "America/Manaus|Brazil/West", - "America/Mazatlan|Mexico/BajaSur", - "America/Mexico_City|Mexico/General", - "America/New_York|US/Eastern", - "America/Noronha|Brazil/DeNoronha", - "America/Panama|America/Atikokan", - "America/Panama|America/Cayman", - "America/Panama|America/Coral_Harbour", - "America/Phoenix|America/Creston", - "America/Phoenix|US/Arizona", - "America/Puerto_Rico|America/Anguilla", - "America/Puerto_Rico|America/Antigua", - "America/Puerto_Rico|America/Aruba", - "America/Puerto_Rico|America/Blanc-Sablon", - "America/Puerto_Rico|America/Curacao", - "America/Puerto_Rico|America/Dominica", - "America/Puerto_Rico|America/Grenada", - "America/Puerto_Rico|America/Guadeloupe", - "America/Puerto_Rico|America/Kralendijk", - "America/Puerto_Rico|America/Lower_Princes", - "America/Puerto_Rico|America/Marigot", - "America/Puerto_Rico|America/Montserrat", - "America/Puerto_Rico|America/Port_of_Spain", - "America/Puerto_Rico|America/St_Barthelemy", - "America/Puerto_Rico|America/St_Kitts", - "America/Puerto_Rico|America/St_Lucia", - "America/Puerto_Rico|America/St_Thomas", - "America/Puerto_Rico|America/St_Vincent", - "America/Puerto_Rico|America/Tortola", - "America/Puerto_Rico|America/Virgin", - "America/Regina|Canada/Saskatchewan", - "America/Rio_Branco|America/Porto_Acre", - "America/Rio_Branco|Brazil/Acre", - "America/Santiago|Chile/Continental", - "America/Sao_Paulo|Brazil/East", - "America/St_Johns|Canada/Newfoundland", - "America/Tijuana|America/Ensenada", - "America/Tijuana|America/Santa_Isabel", - "America/Tijuana|Mexico/BajaNorte", - "America/Toronto|America/Montreal", - "America/Toronto|America/Nassau", - "America/Toronto|America/Nipigon", - "America/Toronto|America/Thunder_Bay", - "America/Toronto|Canada/Eastern", - "America/Vancouver|Canada/Pacific", - "America/Whitehorse|Canada/Yukon", - "America/Winnipeg|America/Rainy_River", - "America/Winnipeg|Canada/Central", - "Asia/Ashgabat|Asia/Ashkhabad", - "Asia/Bangkok|Asia/Phnom_Penh", - "Asia/Bangkok|Asia/Vientiane", - "Asia/Bangkok|Indian/Christmas", - "Asia/Brunei|Asia/Kuching", - "Asia/Dhaka|Asia/Dacca", - "Asia/Dubai|Asia/Muscat", - "Asia/Dubai|Indian/Mahe", - "Asia/Dubai|Indian/Reunion", - "Asia/Ho_Chi_Minh|Asia/Saigon", - "Asia/Hong_Kong|Hongkong", - "Asia/Jerusalem|Asia/Tel_Aviv", - "Asia/Jerusalem|Israel", - "Asia/Kathmandu|Asia/Katmandu", - "Asia/Kolkata|Asia/Calcutta", - "Asia/Kuala_Lumpur|Asia/Singapore", - "Asia/Kuala_Lumpur|Singapore", - "Asia/Macau|Asia/Macao", - "Asia/Makassar|Asia/Ujung_Pandang", - "Asia/Nicosia|Europe/Nicosia", - "Asia/Qatar|Asia/Bahrain", - "Asia/Rangoon|Asia/Yangon", - "Asia/Rangoon|Indian/Cocos", - "Asia/Riyadh|Antarctica/Syowa", - "Asia/Riyadh|Asia/Aden", - "Asia/Riyadh|Asia/Kuwait", - "Asia/Seoul|ROK", - "Asia/Shanghai|Asia/Chongqing", - "Asia/Shanghai|Asia/Chungking", - "Asia/Shanghai|Asia/Harbin", - "Asia/Shanghai|PRC", - "Asia/Taipei|ROC", - "Asia/Tehran|Iran", - "Asia/Thimphu|Asia/Thimbu", - "Asia/Tokyo|Japan", - "Asia/Ulaanbaatar|Asia/Ulan_Bator", - "Asia/Urumqi|Antarctica/Vostok", - "Asia/Urumqi|Asia/Kashgar", - "Atlantic/Faroe|Atlantic/Faeroe", - "Australia/Adelaide|Australia/South", - "Australia/Brisbane|Australia/Queensland", - "Australia/Broken_Hill|Australia/Yancowinna", - "Australia/Darwin|Australia/North", - "Australia/Hobart|Australia/Currie", - "Australia/Hobart|Australia/Tasmania", - "Australia/Lord_Howe|Australia/LHI", - "Australia/Melbourne|Australia/Victoria", - "Australia/Perth|Australia/West", - "Australia/Sydney|Australia/ACT", - "Australia/Sydney|Australia/Canberra", - "Australia/Sydney|Australia/NSW", - "Etc/GMT-0|Etc/GMT", - "Etc/GMT-0|Etc/GMT+0", - "Etc/GMT-0|Etc/GMT0", - "Etc/GMT-0|Etc/Greenwich", - "Etc/GMT-0|GMT", - "Etc/GMT-0|GMT+0", - "Etc/GMT-0|GMT-0", - "Etc/GMT-0|GMT0", - "Etc/GMT-0|Greenwich", - "Etc/UTC|Etc/UCT", - "Etc/UTC|Etc/Universal", - "Etc/UTC|Etc/Zulu", - "Etc/UTC|UCT", - "Etc/UTC|UTC", - "Etc/UTC|Universal", - "Etc/UTC|Zulu", - "Europe/Belgrade|Europe/Ljubljana", - "Europe/Belgrade|Europe/Podgorica", - "Europe/Belgrade|Europe/Sarajevo", - "Europe/Belgrade|Europe/Skopje", - "Europe/Belgrade|Europe/Zagreb", - "Europe/Berlin|Arctic/Longyearbyen", - "Europe/Berlin|Atlantic/Jan_Mayen", - "Europe/Berlin|Europe/Copenhagen", - "Europe/Berlin|Europe/Oslo", - "Europe/Berlin|Europe/Stockholm", - "Europe/Brussels|Europe/Amsterdam", - "Europe/Brussels|Europe/Luxembourg", - "Europe/Chisinau|Europe/Tiraspol", - "Europe/Dublin|Eire", - "Europe/Helsinki|Europe/Mariehamn", - "Europe/Istanbul|Asia/Istanbul", - "Europe/Istanbul|Turkey", - "Europe/Kiev|Europe/Kyiv", - "Europe/Kiev|Europe/Uzhgorod", - "Europe/Kiev|Europe/Zaporozhye", - "Europe/Lisbon|Portugal", - "Europe/London|Europe/Belfast", - "Europe/London|Europe/Guernsey", - "Europe/London|Europe/Isle_of_Man", - "Europe/London|Europe/Jersey", - "Europe/London|GB", - "Europe/London|GB-Eire", - "Europe/Moscow|W-SU", - "Europe/Paris|Europe/Monaco", - "Europe/Prague|Europe/Bratislava", - "Europe/Rome|Europe/San_Marino", - "Europe/Rome|Europe/Vatican", - "Europe/Warsaw|Poland", - "Europe/Zurich|Europe/Busingen", - "Europe/Zurich|Europe/Vaduz", - "Indian/Maldives|Indian/Kerguelen", - "Pacific/Auckland|Antarctica/McMurdo", - "Pacific/Auckland|Antarctica/South_Pole", - "Pacific/Auckland|NZ", - "Pacific/Chatham|NZ-CHAT", - "Pacific/Easter|Chile/EasterIsland", - "Pacific/Enderbury|Pacific/Kanton", - "Pacific/Guadalcanal|Pacific/Pohnpei", - "Pacific/Guadalcanal|Pacific/Ponape", - "Pacific/Guam|Pacific/Saipan", - "Pacific/Honolulu|Pacific/Johnston", - "Pacific/Honolulu|US/Hawaii", - "Pacific/Kwajalein|Kwajalein", - "Pacific/Pago_Pago|Pacific/Midway", - "Pacific/Pago_Pago|Pacific/Samoa", - "Pacific/Pago_Pago|US/Samoa", - "Pacific/Port_Moresby|Antarctica/DumontDUrville", - "Pacific/Port_Moresby|Pacific/Chuuk", - "Pacific/Port_Moresby|Pacific/Truk", - "Pacific/Port_Moresby|Pacific/Yap", - "Pacific/Tarawa|Pacific/Funafuti", - "Pacific/Tarawa|Pacific/Majuro", - "Pacific/Tarawa|Pacific/Wake", - "Pacific/Tarawa|Pacific/Wallis" - ], - "countries": [ - "AD|Europe/Andorra", - "AE|Asia/Dubai", - "AF|Asia/Kabul", - "AG|America/Puerto_Rico America/Antigua", - "AI|America/Puerto_Rico America/Anguilla", - "AL|Europe/Tirane", - "AM|Asia/Yerevan", - "AO|Africa/Lagos Africa/Luanda", - "AQ|Antarctica/Casey Antarctica/Davis Antarctica/Mawson Antarctica/Palmer Antarctica/Rothera Antarctica/Troll Asia/Urumqi Pacific/Auckland Pacific/Port_Moresby Asia/Riyadh Antarctica/McMurdo Antarctica/DumontDUrville Antarctica/Syowa Antarctica/Vostok", - "AR|America/Argentina/Buenos_Aires America/Argentina/Cordoba America/Argentina/Salta America/Argentina/Jujuy America/Argentina/Tucuman America/Argentina/Catamarca America/Argentina/La_Rioja America/Argentina/San_Juan America/Argentina/Mendoza America/Argentina/San_Luis America/Argentina/Rio_Gallegos America/Argentina/Ushuaia", - "AS|Pacific/Pago_Pago", - "AT|Europe/Vienna", - "AU|Australia/Lord_Howe Antarctica/Macquarie Australia/Hobart Australia/Melbourne Australia/Sydney Australia/Broken_Hill Australia/Brisbane Australia/Lindeman Australia/Adelaide Australia/Darwin Australia/Perth Australia/Eucla", - "AW|America/Puerto_Rico America/Aruba", - "AX|Europe/Helsinki Europe/Mariehamn", - "AZ|Asia/Baku", - "BA|Europe/Belgrade Europe/Sarajevo", - "BB|America/Barbados", - "BD|Asia/Dhaka", - "BE|Europe/Brussels", - "BF|Africa/Abidjan Africa/Ouagadougou", - "BG|Europe/Sofia", - "BH|Asia/Qatar Asia/Bahrain", - "BI|Africa/Maputo Africa/Bujumbura", - "BJ|Africa/Lagos Africa/Porto-Novo", - "BL|America/Puerto_Rico America/St_Barthelemy", - "BM|Atlantic/Bermuda", - "BN|Asia/Kuching Asia/Brunei", - "BO|America/La_Paz", - "BQ|America/Puerto_Rico America/Kralendijk", - "BR|America/Noronha America/Belem America/Fortaleza America/Recife America/Araguaina America/Maceio America/Bahia America/Sao_Paulo America/Campo_Grande America/Cuiaba America/Santarem America/Porto_Velho America/Boa_Vista America/Manaus America/Eirunepe America/Rio_Branco", - "BS|America/Toronto America/Nassau", - "BT|Asia/Thimphu", - "BW|Africa/Maputo Africa/Gaborone", - "BY|Europe/Minsk", - "BZ|America/Belize", - "CA|America/St_Johns America/Halifax America/Glace_Bay America/Moncton America/Goose_Bay America/Toronto America/Iqaluit America/Winnipeg America/Resolute America/Rankin_Inlet America/Regina America/Swift_Current America/Edmonton America/Cambridge_Bay America/Inuvik America/Dawson_Creek America/Fort_Nelson America/Whitehorse America/Dawson America/Vancouver America/Panama America/Puerto_Rico America/Phoenix America/Blanc-Sablon America/Atikokan America/Creston", - "CC|Asia/Yangon Indian/Cocos", - "CD|Africa/Maputo Africa/Lagos Africa/Kinshasa Africa/Lubumbashi", - "CF|Africa/Lagos Africa/Bangui", - "CG|Africa/Lagos Africa/Brazzaville", - "CH|Europe/Zurich", - "CI|Africa/Abidjan", - "CK|Pacific/Rarotonga", - "CL|America/Santiago America/Punta_Arenas Pacific/Easter", - "CM|Africa/Lagos Africa/Douala", - "CN|Asia/Shanghai Asia/Urumqi", - "CO|America/Bogota", - "CR|America/Costa_Rica", - "CU|America/Havana", - "CV|Atlantic/Cape_Verde", - "CW|America/Puerto_Rico America/Curacao", - "CX|Asia/Bangkok Indian/Christmas", - "CY|Asia/Nicosia Asia/Famagusta", - "CZ|Europe/Prague", - "DE|Europe/Zurich Europe/Berlin Europe/Busingen", - "DJ|Africa/Nairobi Africa/Djibouti", - "DK|Europe/Berlin Europe/Copenhagen", - "DM|America/Puerto_Rico America/Dominica", - "DO|America/Santo_Domingo", - "DZ|Africa/Algiers", - "EC|America/Guayaquil Pacific/Galapagos", - "EE|Europe/Tallinn", - "EG|Africa/Cairo", - "EH|Africa/El_Aaiun", - "ER|Africa/Nairobi Africa/Asmara", - "ES|Europe/Madrid Africa/Ceuta Atlantic/Canary", - "ET|Africa/Nairobi Africa/Addis_Ababa", - "FI|Europe/Helsinki", - "FJ|Pacific/Fiji", - "FK|Atlantic/Stanley", - "FM|Pacific/Kosrae Pacific/Port_Moresby Pacific/Guadalcanal Pacific/Chuuk Pacific/Pohnpei", - "FO|Atlantic/Faroe", - "FR|Europe/Paris", - "GA|Africa/Lagos Africa/Libreville", - "GB|Europe/London", - "GD|America/Puerto_Rico America/Grenada", - "GE|Asia/Tbilisi", - "GF|America/Cayenne", - "GG|Europe/London Europe/Guernsey", - "GH|Africa/Abidjan Africa/Accra", - "GI|Europe/Gibraltar", - "GL|America/Nuuk America/Danmarkshavn America/Scoresbysund America/Thule", - "GM|Africa/Abidjan Africa/Banjul", - "GN|Africa/Abidjan Africa/Conakry", - "GP|America/Puerto_Rico America/Guadeloupe", - "GQ|Africa/Lagos Africa/Malabo", - "GR|Europe/Athens", - "GS|Atlantic/South_Georgia", - "GT|America/Guatemala", - "GU|Pacific/Guam", - "GW|Africa/Bissau", - "GY|America/Guyana", - "HK|Asia/Hong_Kong", - "HN|America/Tegucigalpa", - "HR|Europe/Belgrade Europe/Zagreb", - "HT|America/Port-au-Prince", - "HU|Europe/Budapest", - "ID|Asia/Jakarta Asia/Pontianak Asia/Makassar Asia/Jayapura", - "IE|Europe/Dublin", - "IL|Asia/Jerusalem", - "IM|Europe/London Europe/Isle_of_Man", - "IN|Asia/Kolkata", - "IO|Indian/Chagos", - "IQ|Asia/Baghdad", - "IR|Asia/Tehran", - "IS|Africa/Abidjan Atlantic/Reykjavik", - "IT|Europe/Rome", - "JE|Europe/London Europe/Jersey", - "JM|America/Jamaica", - "JO|Asia/Amman", - "JP|Asia/Tokyo", - "KE|Africa/Nairobi", - "KG|Asia/Bishkek", - "KH|Asia/Bangkok Asia/Phnom_Penh", - "KI|Pacific/Tarawa Pacific/Kanton Pacific/Kiritimati", - "KM|Africa/Nairobi Indian/Comoro", - "KN|America/Puerto_Rico America/St_Kitts", - "KP|Asia/Pyongyang", - "KR|Asia/Seoul", - "KW|Asia/Riyadh Asia/Kuwait", - "KY|America/Panama America/Cayman", - "KZ|Asia/Almaty Asia/Qyzylorda Asia/Qostanay Asia/Aqtobe Asia/Aqtau Asia/Atyrau Asia/Oral", - "LA|Asia/Bangkok Asia/Vientiane", - "LB|Asia/Beirut", - "LC|America/Puerto_Rico America/St_Lucia", - "LI|Europe/Zurich Europe/Vaduz", - "LK|Asia/Colombo", - "LR|Africa/Monrovia", - "LS|Africa/Johannesburg Africa/Maseru", - "LT|Europe/Vilnius", - "LU|Europe/Brussels Europe/Luxembourg", - "LV|Europe/Riga", - "LY|Africa/Tripoli", - "MA|Africa/Casablanca", - "MC|Europe/Paris Europe/Monaco", - "MD|Europe/Chisinau", - "ME|Europe/Belgrade Europe/Podgorica", - "MF|America/Puerto_Rico America/Marigot", - "MG|Africa/Nairobi Indian/Antananarivo", - "MH|Pacific/Tarawa Pacific/Kwajalein Pacific/Majuro", - "MK|Europe/Belgrade Europe/Skopje", - "ML|Africa/Abidjan Africa/Bamako", - "MM|Asia/Yangon", - "MN|Asia/Ulaanbaatar Asia/Hovd Asia/Choibalsan", - "MO|Asia/Macau", - "MP|Pacific/Guam Pacific/Saipan", - "MQ|America/Martinique", - "MR|Africa/Abidjan Africa/Nouakchott", - "MS|America/Puerto_Rico America/Montserrat", - "MT|Europe/Malta", - "MU|Indian/Mauritius", - "MV|Indian/Maldives", - "MW|Africa/Maputo Africa/Blantyre", - "MX|America/Mexico_City America/Cancun America/Merida America/Monterrey America/Matamoros America/Chihuahua America/Ciudad_Juarez America/Ojinaga America/Mazatlan America/Bahia_Banderas America/Hermosillo America/Tijuana", - "MY|Asia/Kuching Asia/Singapore Asia/Kuala_Lumpur", - "MZ|Africa/Maputo", - "NA|Africa/Windhoek", - "NC|Pacific/Noumea", - "NE|Africa/Lagos Africa/Niamey", - "NF|Pacific/Norfolk", - "NG|Africa/Lagos", - "NI|America/Managua", - "NL|Europe/Brussels Europe/Amsterdam", - "NO|Europe/Berlin Europe/Oslo", - "NP|Asia/Kathmandu", - "NR|Pacific/Nauru", - "NU|Pacific/Niue", - "NZ|Pacific/Auckland Pacific/Chatham", - "OM|Asia/Dubai Asia/Muscat", - "PA|America/Panama", - "PE|America/Lima", - "PF|Pacific/Tahiti Pacific/Marquesas Pacific/Gambier", - "PG|Pacific/Port_Moresby Pacific/Bougainville", - "PH|Asia/Manila", - "PK|Asia/Karachi", - "PL|Europe/Warsaw", - "PM|America/Miquelon", - "PN|Pacific/Pitcairn", - "PR|America/Puerto_Rico", - "PS|Asia/Gaza Asia/Hebron", - "PT|Europe/Lisbon Atlantic/Madeira Atlantic/Azores", - "PW|Pacific/Palau", - "PY|America/Asuncion", - "QA|Asia/Qatar", - "RE|Asia/Dubai Indian/Reunion", - "RO|Europe/Bucharest", - "RS|Europe/Belgrade", - "RU|Europe/Kaliningrad Europe/Moscow Europe/Simferopol Europe/Kirov Europe/Volgograd Europe/Astrakhan Europe/Saratov Europe/Ulyanovsk Europe/Samara Asia/Yekaterinburg Asia/Omsk Asia/Novosibirsk Asia/Barnaul Asia/Tomsk Asia/Novokuznetsk Asia/Krasnoyarsk Asia/Irkutsk Asia/Chita Asia/Yakutsk Asia/Khandyga Asia/Vladivostok Asia/Ust-Nera Asia/Magadan Asia/Sakhalin Asia/Srednekolymsk Asia/Kamchatka Asia/Anadyr", - "RW|Africa/Maputo Africa/Kigali", - "SA|Asia/Riyadh", - "SB|Pacific/Guadalcanal", - "SC|Asia/Dubai Indian/Mahe", - "SD|Africa/Khartoum", - "SE|Europe/Berlin Europe/Stockholm", - "SG|Asia/Singapore", - "SH|Africa/Abidjan Atlantic/St_Helena", - "SI|Europe/Belgrade Europe/Ljubljana", - "SJ|Europe/Berlin Arctic/Longyearbyen", - "SK|Europe/Prague Europe/Bratislava", - "SL|Africa/Abidjan Africa/Freetown", - "SM|Europe/Rome Europe/San_Marino", - "SN|Africa/Abidjan Africa/Dakar", - "SO|Africa/Nairobi Africa/Mogadishu", - "SR|America/Paramaribo", - "SS|Africa/Juba", - "ST|Africa/Sao_Tome", - "SV|America/El_Salvador", - "SX|America/Puerto_Rico America/Lower_Princes", - "SY|Asia/Damascus", - "SZ|Africa/Johannesburg Africa/Mbabane", - "TC|America/Grand_Turk", - "TD|Africa/Ndjamena", - "TF|Asia/Dubai Indian/Maldives Indian/Kerguelen", - "TG|Africa/Abidjan Africa/Lome", - "TH|Asia/Bangkok", - "TJ|Asia/Dushanbe", - "TK|Pacific/Fakaofo", - "TL|Asia/Dili", - "TM|Asia/Ashgabat", - "TN|Africa/Tunis", - "TO|Pacific/Tongatapu", - "TR|Europe/Istanbul", - "TT|America/Puerto_Rico America/Port_of_Spain", - "TV|Pacific/Tarawa Pacific/Funafuti", - "TW|Asia/Taipei", - "TZ|Africa/Nairobi Africa/Dar_es_Salaam", - "UA|Europe/Simferopol Europe/Kyiv", - "UG|Africa/Nairobi Africa/Kampala", - "UM|Pacific/Pago_Pago Pacific/Tarawa Pacific/Midway Pacific/Wake", - "US|America/New_York America/Detroit America/Kentucky/Louisville America/Kentucky/Monticello America/Indiana/Indianapolis America/Indiana/Vincennes America/Indiana/Winamac America/Indiana/Marengo America/Indiana/Petersburg America/Indiana/Vevay America/Chicago America/Indiana/Tell_City America/Indiana/Knox America/Menominee America/North_Dakota/Center America/North_Dakota/New_Salem America/North_Dakota/Beulah America/Denver America/Boise America/Phoenix America/Los_Angeles America/Anchorage America/Juneau America/Sitka America/Metlakatla America/Yakutat America/Nome America/Adak Pacific/Honolulu", - "UY|America/Montevideo", - "UZ|Asia/Samarkand Asia/Tashkent", - "VA|Europe/Rome Europe/Vatican", - "VC|America/Puerto_Rico America/St_Vincent", - "VE|America/Caracas", - "VG|America/Puerto_Rico America/Tortola", - "VI|America/Puerto_Rico America/St_Thomas", - "VN|Asia/Bangkok Asia/Ho_Chi_Minh", - "VU|Pacific/Efate", - "WF|Pacific/Tarawa Pacific/Wallis", - "WS|Pacific/Apia", - "YE|Asia/Riyadh Asia/Aden", - "YT|Africa/Nairobi Indian/Mayotte", - "ZA|Africa/Johannesburg", - "ZM|Africa/Maputo Africa/Lusaka", - "ZW|Africa/Maputo Africa/Harare" - ] -} \ No newline at end of file + "version": "2023c", + "zones": [ + "Africa/Abidjan|LMT GMT|g.8 0|01|-2ldXH.Q|48e5", + "Africa/Nairobi|LMT +0230 EAT +0245|-2r.g -2u -30 -2J|012132|-2ua2r.g N6nV.g 3Fbu h1cu dzbJ|47e5", + "Africa/Algiers|LMT PMT WET WEST CET CEST|-c.c -9.l 0 -10 -10 -20|01232323232323232454542423234542324|-3bQ0c.c MDA2.P cNb9.l HA0 19A0 1iM0 11c0 1oo0 Wo0 1rc0 QM0 1EM0 UM0 DA0 Imo0 rd0 De0 9Xz0 1fb0 1ap0 16K0 2yo0 mEp0 hwL0 jxA0 11A0 dDd0 17b0 11B0 1cN0 2Dy0 1cN0 1fB0 1cL0|26e5", + "Africa/Lagos|LMT GMT +0030 WAT|-d.z 0 -u -10|01023|-2B40d.z 7iod.z dnXK.p dLzH.z|17e6", + "Africa/Bissau|LMT -01 GMT|12.k 10 0|012|-2ldX0 2xoo0|39e4", + "Africa/Maputo|LMT CAT|-2a.k -20|01|-2GJea.k|26e5", + "Africa/Cairo|LMT EET EEST|-25.9 -20 -30|01212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-2MBC5.9 1AQM5.9 vb0 1ip0 11z0 1iN0 1nz0 12p0 1pz0 10N0 1pz0 16p0 1jz0 s3d0 Vz0 1oN0 11b0 1oO0 10N0 1pz0 10N0 1pb0 10N0 1pb0 10N0 1pb0 10N0 1pz0 10N0 1pb0 10N0 1pb0 11d0 1oL0 11d0 1pb0 11d0 1oL0 11d0 1oL0 11d0 1oL0 11d0 1pb0 11d0 1oL0 11d0 1oL0 11d0 1oL0 11d0 1pb0 11d0 1oL0 11d0 1oL0 11d0 1oL0 11d0 1pb0 11d0 1oL0 11d0 1WL0 rd0 1Rz0 wp0 1pb0 11d0 1oL0 11d0 1oL0 11d0 1oL0 11d0 1pb0 11d0 1qL0 Xd0 1oL0 11d0 1oL0 11d0 1pb0 11d0 1oL0 11d0 1oL0 11d0 1ny0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 11z0 1o10 11z0 1o10 WL0 1qN0 Rb0 1wp0 On0 1zd0 Lz0 1EN0 Fb0 c10 8n0 8Nd0 gL0 e10 mn0 kSp0 1cL0 1cN0 1fz0 1a10 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1a10 1fz0|15e6", + "Africa/Casablanca|LMT +00 +01|u.k 0 -10|01212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212|-2gMnt.E 130Lt.E rb0 Dd0 dVb0 b6p0 TX0 EoB0 LL0 gnd0 rz0 43d0 AL0 1Nd0 XX0 1Cp0 pz0 dEp0 4mn0 SyN0 AL0 1Nd0 wn0 1FB0 Db0 1zd0 Lz0 1Nf0 wM0 co0 go0 1o00 s00 dA0 vc0 11A0 A00 e00 y00 11A0 uM0 e00 Dc0 11A0 s00 e00 IM0 WM0 mo0 gM0 LA0 WM0 jA0 e00 28M0 e00 2600 gM0 2600 e00 2600 gM0 2600 e00 28M0 e00 2600 gM0 2600 e00 28M0 e00 2600 gM0 2600 e00 2600 gM0 2600 e00 28M0 e00 2600 gM0 2600 e00 2600 gM0 2600 gM0 2600 e00 2600 gM0 2600 e00 28M0 e00 2600 gM0 2600 e00 2600 gM0 2600 gM0 2600 e00 2600 gM0 2600 e00 28M0 e00 2600 gM0 2600 e00 2600 gM0 2600 gM0 2600 e00 2600 gM0 2600 e00 28M0 e00 2600 gM0 2600 e00 2600 gM0 2600 gM0 2600 e00 2600 gM0 2600 e00 28M0 e00 2600 gM0 2600 e00 2600 gM0 2600 gM0 2600 e00 2600 gM0 2600 e00 28M0 e00 2600 gM0 2600 e00 2600 gM0 2600 gM0 2600 e00 2600 gM0 2600 e00 28M0 e00 2600 gM0 2600 e00 2600 gM0 2600 e00 28M0 e00 2600 gM0 2600 e00 28M0 e00 2600 gM0|32e5", + "Africa/Ceuta|LMT WET WEST CET CEST|l.g 0 -10 -10 -20|0121212121212121212121343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343|-2M0M0 GdX0 11z0 drd0 18p0 3HX0 17d0 1fz0 1a10 1io0 1a00 1y7o0 LL0 gnd0 rz0 43d0 AL0 1Nd0 XX0 1Cp0 pz0 dEp0 4VB0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|85e3", + "Africa/El_Aaiun|LMT -01 +00 +01|Q.M 10 0 -10|012323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323|-1rDz7.c 1GVA7.c 6L0 AL0 1Nd0 XX0 1Cp0 pz0 1cBB0 AL0 1Nd0 wn0 1FB0 Db0 1zd0 Lz0 1Nf0 wM0 co0 go0 1o00 s00 dA0 vc0 11A0 A00 e00 y00 11A0 uM0 e00 Dc0 11A0 s00 e00 IM0 WM0 mo0 gM0 LA0 WM0 jA0 e00 28M0 e00 2600 gM0 2600 e00 2600 gM0 2600 e00 28M0 e00 2600 gM0 2600 e00 28M0 e00 2600 gM0 2600 e00 2600 gM0 2600 e00 28M0 e00 2600 gM0 2600 e00 2600 gM0 2600 gM0 2600 e00 2600 gM0 2600 e00 28M0 e00 2600 gM0 2600 e00 2600 gM0 2600 gM0 2600 e00 2600 gM0 2600 e00 28M0 e00 2600 gM0 2600 e00 2600 gM0 2600 gM0 2600 e00 2600 gM0 2600 e00 28M0 e00 2600 gM0 2600 e00 2600 gM0 2600 gM0 2600 e00 2600 gM0 2600 e00 28M0 e00 2600 gM0 2600 e00 2600 gM0 2600 gM0 2600 e00 2600 gM0 2600 e00 28M0 e00 2600 gM0 2600 e00 2600 gM0 2600 gM0 2600 e00 2600 gM0 2600 e00 28M0 e00 2600 gM0 2600 e00 2600 gM0 2600 e00 28M0 e00 2600 gM0 2600 e00 28M0 e00 2600 gM0|20e4", + "Africa/Johannesburg|LMT SAST SAST SAST|-1Q -1u -20 -30|0123232|-39EpQ qTcm 1Ajdu 1cL0 1cN0 1cL0|84e5", + "Africa/Juba|LMT CAT CAST EAT|-26.s -20 -30 -30|012121212121212121212121212121212131|-1yW26.s 1zK06.s 16L0 1iN0 17b0 1jd0 17b0 1ip0 17z0 1i10 17X0 1hB0 18n0 1hd0 19b0 1gp0 19z0 1iN0 17b0 1ip0 17z0 1i10 18n0 1hd0 18L0 1gN0 19b0 1gp0 19z0 1iN0 17z0 1i10 17X0 yGd0 PeX0|", + "Africa/Khartoum|LMT CAT CAST EAT|-2a.8 -20 -30 -30|012121212121212121212121212121212131|-1yW2a.8 1zK0a.8 16L0 1iN0 17b0 1jd0 17b0 1ip0 17z0 1i10 17X0 1hB0 18n0 1hd0 19b0 1gp0 19z0 1iN0 17b0 1ip0 17z0 1i10 18n0 1hd0 18L0 1gN0 19b0 1gp0 19z0 1iN0 17z0 1i10 17X0 yGd0 HjL0|51e5", + "Africa/Monrovia|LMT MMT MMT GMT|H.8 H.8 I.u 0|0123|-3ygng.Q 1usM0 28G01.m|11e5", + "Africa/Ndjamena|LMT WAT WAST|-10.c -10 -20|0121|-2le10.c 2J3c0.c Wn0|13e5", + "Africa/Sao_Tome|LMT LMT GMT WAT|-q.U A.J 0 -10|01232|-3tooq.U 18aoq.U 4i6N0 2q00|", + "Africa/Tripoli|LMT CET CEST EET|-Q.I -10 -20 -20|012121213121212121212121213123123|-21JcQ.I 1hnBQ.I vx0 4iP0 xx0 4eN0 Bb0 7ip0 U0n0 A10 1db0 1cN0 1db0 1dd0 1db0 1eN0 1bb0 1e10 1cL0 1c10 1db0 1dd0 1db0 1cN0 1db0 1q10 fAn0 1ep0 1db0 AKq0 TA0 1o00|11e5", + "Africa/Tunis|LMT PMT CET CEST|-E.I -9.l -10 -20|01232323232323232323232323232323232|-3zO0E.I 1cBAv.n 18pa9.l 1qM0 DA0 3Tc0 11B0 1ze0 WM0 7z0 3d0 14L0 1cN0 1f90 1ar0 16J0 1gXB0 WM0 1rA0 11c0 nwo0 Ko0 1cM0 1cM0 1rA0 10M0 zuM0 10N0 1aN0 1qM0 WM0 1qM0 11A0 1o00|20e5", + "Africa/Windhoek|LMT +0130 SAST SAST CAT WAT|-18.o -1u -20 -30 -20 -10|012324545454545454545454545454545454545454545454545454|-39Ep8.o qTbC.o 1Ajdu 1cL0 1SqL0 9Io0 16P0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0|32e4", + "America/Adak|LMT LMT NST NWT NPT BST BDT AHST HST HDT|-cd.m bK.C b0 a0 a0 b0 a0 a0 a0 90|01234256565656565656565656565656565678989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898|-48Pzs.L 1jVzf.p 1EX1d.m 8wW0 iB0 Qlb0 52O0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 cm0 10q0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|326", + "America/Anchorage|LMT LMT AST AWT APT AHST AHDT YST AKST AKDT|-e0.o 9X.A a0 90 90 a0 90 90 90 80|01234256565656565656565656565656565678989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898|-48Pzs.L 1jVxs.n 1EX20.o 8wX0 iA0 Qlb0 52O0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 cm0 10q0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|30e4", + "America/Puerto_Rico|LMT AST AWT APT|4o.p 40 30 30|01231|-2Qi7z.z 1IUbz.z 7XT0 iu0|24e5", + "America/Araguaina|LMT -03 -02|3c.M 30 20|0121212121212121212121212121212121212121212121212121|-2glwL.c HdKL.c 1cc0 1e10 1bX0 Ezd0 So0 1vA0 Mn0 1BB0 ML0 1BB0 zX0 qe10 xb0 2ep0 nz0 1C10 zX0 1C10 LX0 1C10 Mn0 H210 Rb0 1tB0 IL0 1Fd0 FX0 1EN0 FX0 1HB0 Lz0 dMN0 Lz0 1zd0 Rb0 1wN0 Wn0 1tB0 Rb0 1tB0 WL0 1tB0 Rb0 1zd0 On0 1HB0 FX0 ny10 Lz0|14e4", + "America/Argentina/Buenos_Aires|LMT CMT -04 -03 -02|3R.M 4g.M 40 30 20|012323232323232323232323232323232323232323234343434343434343|-331U6.c 125cn pKnH.c Mn0 1iN0 Tb0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 MN0 2jz0 MN0 4lX0 u10 5Lb0 1pB0 Fnz0 u10 uL0 1vd0 SL0 1vd0 SL0 1vd0 17z0 1cN0 1fz0 1cN0 1cL0 1cN0 asn0 Db0 zvd0 Bz0 1tB0 TX0 1wp0 Rb0 1wp0 Rb0 1wp0 TX0 A4p0 uL0 1qN0 WL0|", + "America/Argentina/Catamarca|LMT CMT -04 -03 -02|4n.8 4g.M 40 30 20|012323232323232323232323232323232323232323234343434243432343|-331TA.Q 125bR.E pKnH.c Mn0 1iN0 Tb0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 MN0 2jz0 MN0 4lX0 u10 5Lb0 1pB0 Fnz0 u10 uL0 1vd0 SL0 1vd0 SL0 1vd0 17z0 1cN0 1fz0 1cN0 1cL0 1cN0 asn0 Db0 zvd0 Bz0 1tB0 TX0 1wp0 Rb0 1wq0 Ra0 1wp0 TX0 rlB0 7B0 8zb0 uL0|", + "America/Argentina/Cordoba|LMT CMT -04 -03 -02|4g.M 4g.M 40 30 20|012323232323232323232323232323232323232323234343434243434343|-331TH.c 125c0 pKnH.c Mn0 1iN0 Tb0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 MN0 2jz0 MN0 4lX0 u10 5Lb0 1pB0 Fnz0 u10 uL0 1vd0 SL0 1vd0 SL0 1vd0 17z0 1cN0 1fz0 1cN0 1cL0 1cN0 asn0 Db0 zvd0 Bz0 1tB0 TX0 1wp0 Rb0 1wq0 Ra0 1wp0 TX0 A4p0 uL0 1qN0 WL0|", + "America/Argentina/Jujuy|LMT CMT -04 -03 -02|4l.c 4g.M 40 30 20|0123232323232323232323232323232323232323232343434232434343|-331TC.M 125bT.A pKnH.c Mn0 1iN0 Tb0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 MN0 2jz0 MN0 4lX0 u10 5Lb0 1pB0 Fnz0 u10 uL0 1vd0 SL0 1vd0 SL0 1vd0 17z0 1cN0 1fz0 1cN0 1cL0 1cN0 asn0 Db0 zvd0 Bz0 1tB0 TX0 1ze0 TX0 1ld0 WK0 1wp0 TX0 A4p0 uL0|", + "America/Argentina/La_Rioja|LMT CMT -04 -03 -02|4r.o 4g.M 40 30 20|0123232323232323232323232323232323232323232343434342343432343|-331Tw.A 125bN.o pKnH.c Mn0 1iN0 Tb0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 MN0 2jz0 MN0 4lX0 u10 5Lb0 1pB0 Fnz0 u10 uL0 1vd0 SL0 1vd0 SL0 1vd0 17z0 1cN0 1fz0 1cN0 1cL0 1cN0 asn0 Db0 zvd0 Bz0 1tB0 TX0 1wp0 Qn0 qO0 16n0 Rb0 1wp0 TX0 rlB0 7B0 8zb0 uL0|", + "America/Argentina/Mendoza|LMT CMT -04 -03 -02|4z.g 4g.M 40 30 20|012323232323232323232323232323232323232323234343423232432343|-331To.I 125bF.w pKnH.c Mn0 1iN0 Tb0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 MN0 2jz0 MN0 4lX0 u10 5Lb0 1pB0 Fnz0 u10 uL0 1vd0 SL0 1vd0 SL0 1vd0 17z0 1cN0 1fz0 1cN0 1cL0 1cN0 asn0 Db0 zvd0 Bz0 1tB0 TX0 1u20 SL0 1vd0 Tb0 1wp0 TW0 ri10 Op0 7TX0 uL0|", + "America/Argentina/Rio_Gallegos|LMT CMT -04 -03 -02|4A.Q 4g.M 40 30 20|012323232323232323232323232323232323232323234343434343432343|-331Tn.8 125bD.U pKnH.c Mn0 1iN0 Tb0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 MN0 2jz0 MN0 4lX0 u10 5Lb0 1pB0 Fnz0 u10 uL0 1vd0 SL0 1vd0 SL0 1vd0 17z0 1cN0 1fz0 1cN0 1cL0 1cN0 asn0 Db0 zvd0 Bz0 1tB0 TX0 1wp0 Rb0 1wp0 Rb0 1wp0 TX0 rlB0 7B0 8zb0 uL0|", + "America/Argentina/Salta|LMT CMT -04 -03 -02|4l.E 4g.M 40 30 20|0123232323232323232323232323232323232323232343434342434343|-331TC.k 125bT.8 pKnH.c Mn0 1iN0 Tb0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 MN0 2jz0 MN0 4lX0 u10 5Lb0 1pB0 Fnz0 u10 uL0 1vd0 SL0 1vd0 SL0 1vd0 17z0 1cN0 1fz0 1cN0 1cL0 1cN0 asn0 Db0 zvd0 Bz0 1tB0 TX0 1wp0 Rb0 1wq0 Ra0 1wp0 TX0 A4p0 uL0|", + "America/Argentina/San_Juan|LMT CMT -04 -03 -02|4y.4 4g.M 40 30 20|0123232323232323232323232323232323232323232343434342343432343|-331Tp.U 125bG.I pKnH.c Mn0 1iN0 Tb0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 MN0 2jz0 MN0 4lX0 u10 5Lb0 1pB0 Fnz0 u10 uL0 1vd0 SL0 1vd0 SL0 1vd0 17z0 1cN0 1fz0 1cN0 1cL0 1cN0 asn0 Db0 zvd0 Bz0 1tB0 TX0 1wp0 Qn0 qO0 16n0 Rb0 1wp0 TX0 rld0 m10 8lb0 uL0|", + "America/Argentina/San_Luis|LMT CMT -04 -03 -02|4p.o 4g.M 40 30 20|0123232323232323232323232323232323232323232343434232323432323|-331Ty.A 125bP.o pKnH.c Mn0 1iN0 Tb0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 MN0 2jz0 MN0 4lX0 u10 5Lb0 1pB0 Fnz0 u10 uL0 1vd0 SL0 1vd0 SL0 1vd0 17z0 1cN0 1fz0 1cN0 1cL0 1cN0 asn0 Db0 zvd0 Bz0 1tB0 XX0 1q20 SL0 AN0 vDb0 m10 8lb0 8L0 jd0 1qN0 WL0 1qN0|", + "America/Argentina/Tucuman|LMT CMT -04 -03 -02|4k.Q 4g.M 40 30 20|01232323232323232323232323232323232323232323434343424343234343|-331TD.8 125bT.U pKnH.c Mn0 1iN0 Tb0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 MN0 2jz0 MN0 4lX0 u10 5Lb0 1pB0 Fnz0 u10 uL0 1vd0 SL0 1vd0 SL0 1vd0 17z0 1cN0 1fz0 1cN0 1cL0 1cN0 asn0 Db0 zvd0 Bz0 1tB0 TX0 1wp0 Rb0 1wq0 Ra0 1wp0 TX0 rlB0 4N0 8BX0 uL0 1qN0 WL0|", + "America/Argentina/Ushuaia|LMT CMT -04 -03 -02|4x.c 4g.M 40 30 20|012323232323232323232323232323232323232323234343434343432343|-331Tq.M 125bH.A pKnH.c Mn0 1iN0 Tb0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 1C10 LX0 1C10 LX0 1C10 LX0 1C10 Mn0 MN0 2jz0 MN0 4lX0 u10 5Lb0 1pB0 Fnz0 u10 uL0 1vd0 SL0 1vd0 SL0 1vd0 17z0 1cN0 1fz0 1cN0 1cL0 1cN0 asn0 Db0 zvd0 Bz0 1tB0 TX0 1wp0 Rb0 1wp0 Rb0 1wp0 TX0 rkN0 8p0 8zb0 uL0|", + "America/Asuncion|LMT AMT -04 -03|3O.E 3O.E 40 30|0123232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323|-3eLw9.k 1FGo0 1DKM9.k 3CL0 3Dd0 10L0 1pB0 10n0 1pB0 10n0 1pB0 1cL0 1dd0 1db0 1dd0 1cL0 1dd0 1cL0 1dd0 1cL0 1dd0 1db0 1dd0 1cL0 1dd0 1cL0 1dd0 1cL0 1dd0 1db0 1dd0 1cL0 1lB0 14n0 1dd0 1cL0 1fd0 WL0 1rd0 1aL0 1dB0 Xz0 1qp0 Xb0 1qN0 10L0 1rB0 TX0 1tB0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1qN0 1cL0 WN0 1qL0 11B0 1nX0 1ip0 WL0 1qN0 WL0 1qN0 WL0 1tB0 TX0 1tB0 TX0 1tB0 19X0 1a10 1fz0 1a10 1fz0 1cN0 17b0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 17b0 1ip0|28e5", + "America/Panama|LMT CMT EST|5i.8 5j.A 50|012|-3eLuF.Q Iy01.s|15e5", + "America/Bahia_Banderas|LMT MST CST MDT PST CDT|71 70 60 60 80 50|0121312141313131313131313131313131313152525252525252525252525252|-1UQF0 deL0 8lc0 17c0 10M0 1dd0 otX0 gmN0 P2N0 13Vd0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 1fB0 WL0 1fB0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nW0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0|84e3", + "America/Bahia|LMT -03 -02|2y.4 30 20|01212121212121212121212121212121212121212121212121212121212121|-2glxp.U HdLp.U 1cc0 1e10 1bX0 Ezd0 So0 1vA0 Mn0 1BB0 ML0 1BB0 zX0 qe10 xb0 2ep0 nz0 1C10 zX0 1C10 LX0 1C10 Mn0 H210 Rb0 1tB0 IL0 1Fd0 FX0 1EN0 FX0 1HB0 Lz0 1EN0 Lz0 1C10 IL0 1HB0 Db0 1HB0 On0 1zd0 On0 1zd0 Lz0 1zd0 Rb0 1wN0 Wn0 1tB0 Rb0 1tB0 WL0 1tB0 Rb0 1zd0 On0 1HB0 FX0 l5B0 Rb0|27e5", + "America/Barbados|LMT AST ADT -0330|3W.t 40 30 3u|0121213121212121|-2m4k1.v 1eAN1.v RB0 1Bz0 Op0 1rb0 11d0 1jJc0 IL0 1ip0 17b0 1ip0 17b0 1ld0 13b0|28e4", + "America/Belem|LMT -03 -02|3d.U 30 20|012121212121212121212121212121|-2glwK.4 HdKK.4 1cc0 1e10 1bX0 Ezd0 So0 1vA0 Mn0 1BB0 ML0 1BB0 zX0 qe10 xb0 2ep0 nz0 1C10 zX0 1C10 LX0 1C10 Mn0 H210 Rb0 1tB0 IL0 1Fd0 FX0|20e5", + "America/Belize|LMT CST -0530 CWT CPT CDT|5Q.M 60 5u 50 50 50|012121212121212121212121212121212121212121212121213412121212121212121212121212121212121212121215151|-2kBu7.c fPA7.c Onu 1zcu Rbu 1wou Rbu 1wou Rbu 1zcu Onu 1zcu Onu 1zcu Rbu 1wou Rbu 1wou Rbu 1wou Rbu 1zcu Onu 1zcu Onu 1zcu Rbu 1wou Rbu 1wou Rbu 1zcu Onu 1zcu Onu 1zcu Onu 1zcu Rbu 1wou Rbu 1wou Rbu 1zcu Onu 1zcu Onu 1zcu Rbu Rcu 7Bt0 Ni0 4nd0 Rbu 1wou Rbu 1wou Rbu 1zcu Onu 1zcu Onu 1zcu Rbu 1wou Rbu 1wou Rbu 1wou Rbu 1zcu Onu 1zcu Onu 1zcu Rbu 1wou Rbu 1wou Rbu 1zcu Onu 1zcu Onu 1zcu Onu 1zcu Rbu 1wou Rbu 1wou Rbu 1zcu Onu e9Au qn0 lxB0 mn0|57e3", + "America/Boa_Vista|LMT -04 -03|42.E 40 30|0121212121212121212121212121212121|-2glvV.k HdKV.k 1cc0 1e10 1bX0 Ezd0 So0 1vA0 Mn0 1BB0 ML0 1BB0 zX0 qe10 xb0 2ep0 nz0 1C10 zX0 1C10 LX0 1C10 Mn0 H210 Rb0 1tB0 IL0 1Fd0 FX0 smp0 WL0 1tB0 2L0|62e2", + "America/Bogota|LMT BMT -05 -04|4U.g 4U.g 50 40|01232|-3sTv3.I 1eIo0 38yo3.I 1PX0|90e5", + "America/Boise|LMT PST PDT MST MWT MPT MDT|7I.N 80 70 70 60 60 60|01212134536363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363|-3tFE0 1nEe0 1nX0 11B0 1nX0 8C10 JCL0 8x20 ix0 QwN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 Dd0 1Kn0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|21e4", + "America/Cambridge_Bay|-00 MST MWT MPT MDT CST CDT EST|0 70 60 60 60 60 50 50|012314141414141414141414141414141414141414141414141414141414567541414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141|-21Jc0 RO90 8x20 ix0 14HB0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11A0 1nX0 2K0 WQ0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|15e2", + "America/Campo_Grande|LMT -04 -03|3C.s 40 30|01212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-2glwl.w HdLl.w 1cc0 1e10 1bX0 Ezd0 So0 1vA0 Mn0 1BB0 ML0 1BB0 zX0 qe10 xb0 2ep0 nz0 1C10 zX0 1C10 LX0 1C10 Mn0 H210 Rb0 1tB0 IL0 1Fd0 FX0 1EN0 FX0 1HB0 Lz0 1EN0 Lz0 1C10 IL0 1HB0 Db0 1HB0 On0 1zd0 On0 1zd0 Lz0 1zd0 Rb0 1wN0 Wn0 1tB0 Rb0 1tB0 WL0 1tB0 Rb0 1zd0 On0 1HB0 FX0 1C10 Lz0 1Ip0 HX0 1zd0 On0 1HB0 IL0 1wp0 On0 1C10 Lz0 1C10 On0 1zd0 On0 1zd0 Rb0 1zd0 Lz0 1C10 Lz0 1C10 On0 1zd0 On0 1zd0 On0 1zd0 On0 1HB0 FX0|77e4", + "America/Cancun|LMT CST EST EDT CDT|5L.4 60 50 40 50|0123232341414141414141414141414141414141412|-1UQG0 2q2o0 yLB0 1lb0 14p0 1lb0 14p0 Lz0 xB0 14p0 1nX0 11B0 1nX0 1fB0 WL0 1fB0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 Dd0|63e4", + "America/Caracas|LMT CMT -0430 -04|4r.I 4r.E 4u 40|012323|-3eLvw.g ROnX.U 28KM2.k 1IwOu kqo0|29e5", + "America/Cayenne|LMT -04 -03|3t.k 40 30|012|-2mrwu.E 2gWou.E|58e3", + "America/Chicago|LMT CST CDT EST CWT CPT|5O.A 60 50 50 50 50|012121212121212121212121212121212121213121212121214512121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-3tFG0 1nEe0 1nX0 11B0 1nX0 1wp0 TX0 WN0 1qL0 1cN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 11z0 1o10 11z0 11B0 1Hz0 14p0 11z0 1o10 11z0 1qN0 WL0 1qN0 11z0 1o10 11z0 RB0 8x30 iw0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|92e5", + "America/Chihuahua|LMT MST CST MDT CDT|74.k 70 60 60 50|0121312424231313131313131313131313131313131313131313131313132|-1UQF0 deL0 8lc0 17c0 10M0 1dd0 2zQN0 1lb0 14p0 1lb0 14q0 1lb0 14p0 1nX0 11B0 1nX0 1fB0 WL0 1fB0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0|81e4", + "America/Ciudad_Juarez|LMT MST CST MDT CDT|75.U 70 60 60 50|0121312424231313131313131313131313131313131313131313131313132131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131|-1UQF0 deL0 8lc0 17c0 10M0 1dd0 2zQN0 1lb0 14p0 1lb0 14q0 1lb0 14p0 1nX0 11B0 1nX0 1fB0 WL0 1fB0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 U10 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1wn0 cm0 EP0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0|", + "America/Costa_Rica|LMT SJMT CST CDT|5A.d 5A.d 60 50|01232323232|-3eLun.L 1fyo0 2lu0n.L Db0 1Kp0 Db0 pRB0 15b0 1kp0 mL0|12e5", + "America/Phoenix|LMT MST MDT MWT|7s.i 70 60 60|012121313121|-3tFF0 1nEe0 1nX0 11B0 1nX0 SgN0 4Al1 Ap0 1db0 SWqX 1cL0|42e5", + "America/Cuiaba|LMT -04 -03|3I.k 40 30|012121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-2glwf.E HdLf.E 1cc0 1e10 1bX0 Ezd0 So0 1vA0 Mn0 1BB0 ML0 1BB0 zX0 qe10 xb0 2ep0 nz0 1C10 zX0 1C10 LX0 1C10 Mn0 H210 Rb0 1tB0 IL0 1Fd0 FX0 1EN0 FX0 1HB0 Lz0 1EN0 Lz0 1C10 IL0 1HB0 Db0 1HB0 On0 1zd0 On0 1zd0 Lz0 1zd0 Rb0 1wN0 Wn0 1tB0 Rb0 1tB0 WL0 1tB0 Rb0 1zd0 On0 1HB0 FX0 4a10 HX0 1zd0 On0 1HB0 IL0 1wp0 On0 1C10 Lz0 1C10 On0 1zd0 On0 1zd0 Rb0 1zd0 Lz0 1C10 Lz0 1C10 On0 1zd0 On0 1zd0 On0 1zd0 On0 1HB0 FX0|54e4", + "America/Danmarkshavn|LMT -03 -02 GMT|1e.E 30 20 0|01212121212121212121212121212121213|-2a5WJ.k 2z5fJ.k 19U0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 DC0|8", + "America/Dawson_Creek|LMT PST PDT PWT PPT MST|80.U 80 70 70 70 70|01213412121212121212121212121212121212121212121212121212125|-3tofX.4 1nspX.4 1in0 UGp0 8x10 iy0 3NB0 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 ML0|12e3", + "America/Dawson|LMT YST YDT YWT YPT YDDT PST PDT MST|9h.E 90 80 80 80 70 80 70 70|0121213415167676767676767676767676767676767676767676767676767676767676767676767676767676767678|-2MSeG.k GWpG.k 1in0 1o10 13V0 Ser0 8x00 iz0 LCL0 1fA0 jrA0 fNd0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1z90|13e2", + "America/Denver|LMT MST MDT MWT MPT|6X.U 70 60 60 60|012121212134121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-3tFF0 1nEe0 1nX0 11B0 1nX0 11B0 1qL0 WN0 mn0 Ord0 8x20 ix0 LCN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|26e5", + "America/Detroit|LMT CST EST EWT EPT EDT|5w.b 60 50 40 40 40|0123425252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252|-2Cgir.N peqr.N 156L0 8x40 iv0 6fd0 11z0 JxX1 SMX 1cN0 1cL0 aW10 1cL0 s10 1Vz0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|37e5", + "America/Edmonton|LMT MST MDT MWT MPT|7x.Q 70 60 60 60|0121212121212134121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-2yd4q.8 shdq.8 1in0 17d0 hz0 2dB0 1fz0 1a10 11z0 1qN0 WL0 1qN0 11z0 IGN0 8x20 ix0 3NB0 11z0 XQp0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|10e5", + "America/Eirunepe|LMT -05 -04|4D.s 50 40|0121212121212121212121212121212121|-2glvk.w HdLk.w 1cc0 1e10 1bX0 Ezd0 So0 1vA0 Mn0 1BB0 ML0 1BB0 zX0 qe10 xb0 2ep0 nz0 1C10 zX0 1C10 LX0 1C10 Mn0 H210 Rb0 1tB0 IL0 1Fd0 FX0 dPB0 On0 yTd0 d5X0|31e3", + "America/El_Salvador|LMT CST CDT|5U.M 60 50|012121|-1XiG3.c 2Fvc3.c WL0 1qN0 WL0|11e5", + "America/Tijuana|LMT MST PST PDT PWT PPT|7M.4 70 80 70 70 70|012123245232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232|-1UQF0 4Q00 8mM0 8lc0 SN0 1cL0 pHB0 83r0 zI0 5O10 1Rz0 cOO0 11A0 1o00 11A0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1o00 11A0 BUp0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 U10 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|20e5", + "America/Fort_Nelson|LMT PST PDT PWT PPT MST|8a.L 80 70 70 70 70|012134121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121215|-3tofN.d 1nspN.d 1in0 UGp0 8x10 iy0 3NB0 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0|39e2", + "America/Fort_Wayne|LMT CST CDT CWT CPT EST EDT|5I.C 60 50 50 50 50 40|0121212134121212121212121212151565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565|-3tFG0 1nEe0 1nX0 11B0 1nX0 QI10 Db0 RB0 8x30 iw0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 5Tz0 1o10 qLb0 1cL0 1cN0 1cL0 1qhd0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|", + "America/Fortaleza|LMT -03 -02|2y 30 20|0121212121212121212121212121212121212121|-2glxq HdLq 1cc0 1e10 1bX0 Ezd0 So0 1vA0 Mn0 1BB0 ML0 1BB0 zX0 qe10 xb0 2ep0 nz0 1C10 zX0 1C10 LX0 1C10 Mn0 H210 Rb0 1tB0 IL0 1Fd0 FX0 1EN0 FX0 1HB0 Lz0 nsp0 WL0 1tB0 5z0 2mN0 On0|34e5", + "America/Glace_Bay|LMT AST ADT AWT APT|3X.M 40 30 30 30|012134121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-2IsI0.c CwO0.c 1in0 UGp0 8x50 iu0 iq10 11z0 Jg10 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|19e3", + "America/Godthab|LMT -03 -02 -01|3q.U 30 20 10|012121212121212121212121212121212121212121212121212121212121212121212121212121212121212123232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232|-2a5Ux.4 2z5dx.4 19U0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 2so0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|17e3", + "America/Goose_Bay|LMT NST NDT NST NDT NWT NPT AST ADT ADDT|41.E 3u.Q 2u.Q 3u 2u 2u 2u 40 30 20|0121343434343434356343434343434343434343434343434343434343437878787878787878787878787878787878787878787879787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787|-3tojW.k 1nspt.c 1in0 DXb0 2HbX.8 WL0 1qN0 WL0 1qN0 WL0 1tB0 TX0 1tB0 WL0 1qN0 WL0 1qN0 7UHu itu 1tB0 WL0 1qN0 WL0 1qN0 WL0 1qN0 WL0 1tB0 WL0 1ld0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 S10 g0u 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14n1 1lb0 14p0 1nW0 11C0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zcX Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|76e2", + "America/Grand_Turk|LMT KMT EST EDT AST|4I.w 57.a 50 40 40|01232323232323232323232323232323232323232323232323232323232323232323232323243232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232|-3eLvf.s RK0m.C 2HHBQ.O 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 7jA0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|37e2", + "America/Guatemala|LMT CST CDT|62.4 60 50|0121212121|-24KhV.U 2efXV.U An0 mtd0 Nz0 ifB0 17b0 zDB0 11z0|13e5", + "America/Guayaquil|LMT QMT -05 -04|5j.k 5e 50 40|01232|-3eLuE.E 1DNzS.E 2uILK rz0|27e5", + "America/Guyana|LMT -04 -0345 -03|3Q.D 40 3J 30|01231|-2mf87.l 8Hc7.l 2r7bJ Ey0f|80e4", + "America/Halifax|LMT AST ADT AWT APT|4e.o 40 30 30 30|0121212121212121212121212121212121212121212121212134121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-2IsHJ.A xzzJ.A 1db0 3I30 1in0 3HX0 IL0 1E10 ML0 1yN0 Pb0 1Bd0 Mn0 1Bd0 Rz0 1w10 Xb0 1w10 LX0 1w10 Xb0 1w10 Lz0 1C10 Jz0 1E10 OL0 1yN0 Un0 1qp0 Xb0 1qp0 11X0 1w10 Lz0 1HB0 LX0 1C10 FX0 1w10 Xb0 1qp0 Xb0 1BB0 LX0 1td0 Xb0 1qp0 Xb0 Rf0 8x50 iu0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 3Qp0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 3Qp0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 6i10 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|39e4", + "America/Havana|LMT HMT CST CDT|5t.s 5t.A 50 40|0123232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232|-3eLuu.w 1qx00.8 72zu.o ML0 sld0 An0 1Nd0 Db0 1Nd0 An0 6Ep0 An0 1Nd0 An0 JDd0 Mn0 1Ap0 On0 1fd0 11X0 1qN0 WL0 1wp0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 14n0 1ld0 14L0 1kN0 15b0 1kp0 1cL0 1cN0 1fz0 1a10 1fz0 1fB0 11z0 14p0 1nX0 11B0 1nX0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 14n0 1ld0 14n0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 1a10 1in0 1a10 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1cM0 1cM0 1fA0 17c0 1o00 11A0 1qM0 11A0 1o00 11A0 1o00 14o0 1lc0 14o0 1lc0 11A0 6i00 Rc0 1wo0 U00 1tA0 Rc0 1wo0 U00 1wo0 U00 1zc0 U00 1qM0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0|21e5", + "America/Hermosillo|LMT MST CST MDT PST|7n.Q 70 60 60 80|0121312141313131|-1UQF0 deL0 8lc0 17c0 10M0 1dd0 otX0 gmN0 P2N0 13Vd0 1lb0 14p0 1lb0 14p0 1lb0|64e4", + "America/Indiana/Knox|LMT CST CDT CWT CPT EST|5K.u 60 50 50 50 50|01212134121212121212121212121212121212151212121212121212121212121212121212121212121212121252121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-3tFG0 1nEe0 1nX0 11B0 1nX0 SgN0 8x30 iw0 3NB0 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 1fz0 1cN0 1cL0 1cN0 11z0 1o10 11z0 1o10 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 3Cn0 8wp0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 z8o0 1o00 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|", + "America/Indiana/Marengo|LMT CST CDT CWT CPT EST EDT|5J.n 60 50 50 50 50 40|01212134121212121212121215656565656525656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565|-3tFG0 1nEe0 1nX0 11B0 1nX0 SgN0 8x30 iw0 dyN0 11z0 6fd0 11z0 1o10 11z0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 jrz0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1VA0 LA0 1BX0 1e6p0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|", + "America/Indiana/Petersburg|LMT CST CDT CWT CPT EST EDT|5N.7 60 50 50 50 50 40|01212134121212121212121212121512121212121212121212125212565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565|-3tFG0 1nEe0 1nX0 11B0 1nX0 SgN0 8x30 iw0 njX0 WN0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 3Fb0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 19co0 1o00 Rd0 1zb0 Oo0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|", + "America/Indiana/Tell_City|LMT CST CDT CWT CPT EST EDT|5L.3 60 50 50 50 50 40|012121341212121212121212121512165652121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-3tFG0 1nEe0 1nX0 11B0 1nX0 SgN0 8x30 iw0 njX0 WN0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 8wn0 1cN0 1cL0 1cN0 1cK0 1cN0 1cL0 1qhd0 1o00 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|", + "America/Indiana/Vevay|LMT CST CDT CWT CPT EST EDT|5E.g 60 50 50 50 50 40|0121213415656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565|-3tFG0 1nEe0 1nX0 11B0 1nX0 SgN0 8x30 iw0 kPB0 Awn0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1lnd0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|", + "America/Indiana/Vincennes|LMT CST CDT CWT CPT EST EDT|5O.7 60 50 50 50 50 40|01212134121212121212121212121212156565212565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565|-3tFG0 1nEe0 1nX0 11B0 1nX0 SgN0 8x30 iw0 1o10 11z0 g0p0 11z0 1o10 11z0 1qL0 WN0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 1fz0 1cN0 WL0 1qN0 1cL0 1cN0 1cL0 1cN0 caL0 1cL0 1cN0 1cL0 1qhd0 1o00 Rd0 1zb0 Oo0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|", + "America/Indiana/Winamac|LMT CST CDT CWT CPT EST EDT|5K.p 60 50 50 50 50 40|012121341212121212121212121212121212121565652165656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565|-3tFG0 1nEe0 1nX0 11B0 1nX0 SgN0 8x30 iw0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 1fz0 1cN0 1cL0 1cN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 jrz0 1cL0 1cN0 1cL0 1qhd0 1o00 Rd0 1za0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|", + "America/Inuvik|-00 PST PDT MDT MST|0 80 70 60 70|01212121212121213434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434|-FnA0 L3K0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cK0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|35e2", + "America/Iqaluit|-00 EWT EPT EST EDT CST CDT|0 40 40 50 40 60 50|0123434343434343434343434343434343434343434343434343434343456343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343|-16K00 7nX0 iv0 14HB0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11C0 1nX0 11A0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|67e2", + "America/Jamaica|LMT KMT EST EDT|57.a 57.a 50 40|01232323232323232323232|-3eLuQ.O RK00 2uM1Q.O 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0|94e4", + "America/Juneau|LMT LMT PST PWT PPT PDT YDT YST AKST AKDT|-f2.j 8V.F 80 70 70 70 80 90 90 80|0123425252525252525252525252625252578989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898|-48Pzs.L 1jVwq.s 1EX12.j 8x10 iy0 Vo10 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cM0 1cM0 1cL0 1cN0 1fz0 1a10 1fz0 co0 10q0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|33e3", + "America/Kentucky/Louisville|LMT CST CDT CWT CPT EST EDT|5H.2 60 50 50 50 50 40|01212121213412121212121212121212121212565656565656525656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565|-3tFG0 1nEe0 1nX0 11B0 1nX0 3Fd0 Nb0 LPd0 11z0 RB0 8x30 iw0 1nX1 e0X 9vd0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 xz0 gso0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1VA0 LA0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|", + "America/Kentucky/Monticello|LMT CST CDT CWT CPT EST EDT|5D.o 60 50 50 50 50 40|01212134121212121212121212121212121212121212121212121212121212121212121212565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565|-3tFG0 1nEe0 1nX0 11B0 1nX0 SgN0 8x30 iw0 SWp0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11A0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|", + "America/La_Paz|LMT CMT BST -04|4w.A 4w.A 3w.A 40|0123|-3eLvr.o 1FIo0 13b0|19e5", + "America/Lima|LMT LMT -05 -04|58.c 58.A 50 40|01232323232323232|-3eLuP.M JcM0.o 1bDzP.o zX0 1aN0 1cL0 1cN0 1cL0 1PrB0 zX0 1O10 zX0 6Gp0 zX0 98p0 zX0|11e6", + "America/Los_Angeles|LMT PST PDT PWT PPT|7Q.W 80 70 70 70|0121213412121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-3tFE0 1nEe0 1nX0 11B0 1nX0 SgN0 8x10 iy0 5Wp1 1VaX 3dA0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1a00 1fA0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|15e6", + "America/Maceio|LMT -03 -02|2m.Q 30 20|012121212121212121212121212121212121212121|-2glxB.8 HdLB.8 1cc0 1e10 1bX0 Ezd0 So0 1vA0 Mn0 1BB0 ML0 1BB0 zX0 qe10 xb0 2ep0 nz0 1C10 zX0 1C10 LX0 1C10 Mn0 H210 Rb0 1tB0 IL0 1Fd0 FX0 1EN0 FX0 1HB0 Lz0 dMN0 Lz0 8Q10 WL0 1tB0 5z0 2mN0 On0|93e4", + "America/Managua|LMT MMT CST EST CDT|5J.8 5J.c 60 50 50|01232424232324242|-3eLue.Q 1Mhc0.4 1yAMe.M 4mn0 9Up0 Dz0 1K10 Dz0 s3F0 1KH0 DB0 9In0 k8p0 19X0 1o30 11y0|22e5", + "America/Manaus|LMT -04 -03|40.4 40 30|01212121212121212121212121212121|-2glvX.U HdKX.U 1cc0 1e10 1bX0 Ezd0 So0 1vA0 Mn0 1BB0 ML0 1BB0 zX0 qe10 xb0 2ep0 nz0 1C10 zX0 1C10 LX0 1C10 Mn0 H210 Rb0 1tB0 IL0 1Fd0 FX0 dPB0 On0|19e5", + "America/Martinique|LMT FFMT AST ADT|44.k 44.k 40 30|01232|-3eLvT.E PTA0 2LPbT.E 19X0|39e4", + "America/Matamoros|LMT CST CDT|6u 60 50|0121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-1UQG0 2FjC0 1nX0 i6p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 1fB0 WL0 1fB0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 U10 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|45e4", + "America/Mazatlan|LMT MST CST MDT PST|75.E 70 60 60 80|0121312141313131313131313131313131313131313131313131313131313131|-1UQF0 deL0 8lc0 17c0 10M0 1dd0 otX0 gmN0 P2N0 13Vd0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 1fB0 WL0 1fB0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0|44e4", + "America/Menominee|LMT CST CDT CWT CPT EST|5O.r 60 50 50 50 50|012121341212152121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-3pdG9.x 1jce9.x 1nX0 11B0 1nX0 SgN0 8x30 iw0 1o10 11z0 LCN0 1fz0 6410 9Jb0 1cM0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|85e2", + "America/Merida|LMT CST EST CDT|5W.s 60 50 50|0121313131313131313131313131313131313131313131313131313131|-1UQG0 2q2o0 2hz0 wu30 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 1fB0 WL0 1fB0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0|11e5", + "America/Metlakatla|LMT LMT PST PWT PPT PDT AKST AKDT|-fd.G 8K.i 80 70 70 70 90 80|0123425252525252525252525252525252526767672676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676|-48Pzs.L 1jVwf.5 1EX1d.G 8x10 iy0 Vo10 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1hU10 Rd0 1zb0 Op0 1zb0 Op0 1zb0 uM0 jB0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|14e2", + "America/Mexico_City|LMT MST CST MDT CDT CWT|6A.A 70 60 60 50 50|012131242425242424242424242424242424242424242424242424242424242424242|-1UQF0 deL0 8lc0 17c0 10M0 1dd0 gEn0 TX0 3xd0 Jb0 6zB0 SL0 e5d0 17b0 1Pff0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 1fB0 WL0 1fB0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0|20e6", + "America/Miquelon|LMT AST -03 -02|3I.E 40 30 20|012323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232|-2mKkf.k 2LTAf.k gQ10 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|61e2", + "America/Moncton|LMT EST AST ADT AWT APT|4j.8 50 40 30 30 30|0123232323232323232323245232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232|-3txvE.Q J4ME.Q CwN0 1in0 zAo0 An0 1Nd0 An0 1Nd0 An0 1Nd0 An0 1Nd0 An0 1Nd0 An0 1K10 Lz0 1zB0 NX0 1u10 Wn0 S20 8x50 iu0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 11z0 1o10 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 3Cp0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14n1 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 ReX 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|64e3", + "America/Monterrey|LMT CST CDT|6F.g 60 50|0121212121212121212121212121212121212121212121212121212121|-1UQG0 2FjC0 1nX0 i6p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 1fB0 WL0 1fB0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0|41e5", + "America/Montevideo|LMT MMT -04 -03 -0330 -0230 -02 -0130|3I.P 3I.P 40 30 3u 2u 20 1u|012343434343434343434343435353636353636375363636363636363636363636363636363636363636363|-2tRUf.9 sVc0 8jcf.9 1db0 1dcu 1cLu 1dcu 1cLu ircu 11zu 1o0u 11zu 1o0u 11zu 1o0u 11zu 1qMu WLu 1qMu WLu 1fAu 1cLu 1o0u 11zu NAu 3jXu zXu Dq0u 19Xu pcu jz0 cm10 19X0 6tB0 1fbu 3o0u jX0 4vB0 xz0 3Cp0 mmu 1a10 IMu Db0 4c10 uL0 1Nd0 An0 1SN0 uL0 mp0 28L0 iPB0 un0 1SN0 xz0 1zd0 Lz0 1zd0 Rb0 1zd0 On0 1wp0 Rb0 s8p0 1fB0 1ip0 11z0 1ld0 14n0 1o10 11z0 1o10 11z0 1o10 14n0 1ld0 14n0 1ld0 14n0 1o10 11z0 1o10 11z0 1o10 11z0|17e5", + "America/Toronto|LMT EST EDT EWT EPT|5h.w 50 40 40 40|012121212121212121212121212121212121212121212123412121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-32B6G.s UFdG.s 1in0 11Wu 1nzu 1fD0 WJ0 1wr0 Nb0 1Ap0 On0 1zd0 On0 1wp0 TX0 1tB0 TX0 1tB0 TX0 1tB0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 4kM0 8x40 iv0 1o10 11z0 1nX0 11z0 1o10 11z0 1o10 1qL0 11D0 1nX0 11B0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 11z0 1o10 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|65e5", + "America/New_York|LMT EST EDT EWT EPT|4U.2 50 40 40 40|012121212121212121212121212121212121212121212121213412121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-3tFH0 1nEe0 1nX0 11B0 1nX0 11B0 1qL0 1a10 11z0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 11z0 1o10 11z0 RB0 8x40 iv0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|21e6", + "America/Nome|LMT LMT NST NWT NPT BST BDT YST AKST AKDT|-cW.m b1.C b0 a0 a0 b0 a0 90 90 80|01234256565656565656565656565656565678989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898|-48Pzs.L 1jVyu.p 1EX1W.m 8wW0 iB0 Qlb0 52O0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 cl0 10q0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|38e2", + "America/Noronha|LMT -02 -01|29.E 20 10|0121212121212121212121212121212121212121|-2glxO.k HdKO.k 1cc0 1e10 1bX0 Ezd0 So0 1vA0 Mn0 1BB0 ML0 1BB0 zX0 qe10 xb0 2ep0 nz0 1C10 zX0 1C10 LX0 1C10 Mn0 H210 Rb0 1tB0 IL0 1Fd0 FX0 1EN0 FX0 1HB0 Lz0 nsp0 WL0 1tB0 2L0 2pB0 On0|30e2", + "America/North_Dakota/Beulah|LMT MST MDT MWT MPT CST CDT|6L.7 70 60 60 60 60 50|012121341212121212121212121212121212121212121212121212121212121212121212121212121212121212121212565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565|-3tFF0 1nEe0 1nX0 11B0 1nX0 SgN0 8x20 ix0 QwN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Oo0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0|", + "America/North_Dakota/Center|LMT MST MDT MWT MPT CST CDT|6J.c 70 60 60 60 60 50|0121213412121212121212121212121212121212121212121212121212125656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565|-3tFF0 1nEe0 1nX0 11B0 1nX0 SgN0 8x20 ix0 QwN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14o0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|", + "America/North_Dakota/New_Salem|LMT MST MDT MWT MPT CST CDT|6J.D 70 60 60 60 60 50|0121213412121212121212121212121212121212121212121212121212121212121212121212121212565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565|-3tFF0 1nEe0 1nX0 11B0 1nX0 SgN0 8x20 ix0 QwN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14o0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|", + "America/Ojinaga|LMT MST CST MDT CDT|6V.E 70 60 60 50|0121312424231313131313131313131313131313131313131313131313132424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242|-1UQF0 deL0 8lc0 17c0 10M0 1dd0 2zQN0 1lb0 14p0 1lb0 14q0 1lb0 14p0 1nX0 11B0 1nX0 1fB0 WL0 1fB0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 U10 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1wn0 Rc0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|23e3", + "America/Paramaribo|LMT PMT PMT -0330 -03|3E.E 3E.Q 3E.A 3u 30|01234|-2nDUj.k Wqo0.c qanX.I 1yVXN.o|24e4", + "America/Port-au-Prince|LMT PPMT EST EDT|4N.k 4N 50 40|012323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232|-3eLva.E 15RLX.E 2FnMb 19X0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14q0 1o00 11A0 1o00 11A0 1o00 14o0 1lc0 14o0 1lc0 14o0 1o00 11A0 1o00 11A0 1o00 14o0 1lc0 14o0 1lc0 i6n0 1nX0 11B0 1nX0 d430 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 3iN0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|23e5", + "America/Rio_Branco|LMT -05 -04|4v.c 50 40|01212121212121212121212121212121|-2glvs.M HdLs.M 1cc0 1e10 1bX0 Ezd0 So0 1vA0 Mn0 1BB0 ML0 1BB0 zX0 qe10 xb0 2ep0 nz0 1C10 zX0 1C10 LX0 1C10 Mn0 H210 Rb0 1tB0 IL0 1Fd0 FX0 NBd0 d5X0|31e4", + "America/Porto_Velho|LMT -04 -03|4f.A 40 30|012121212121212121212121212121|-2glvI.o HdKI.o 1cc0 1e10 1bX0 Ezd0 So0 1vA0 Mn0 1BB0 ML0 1BB0 zX0 qe10 xb0 2ep0 nz0 1C10 zX0 1C10 LX0 1C10 Mn0 H210 Rb0 1tB0 IL0 1Fd0 FX0|37e4", + "America/Punta_Arenas|LMT SMT -05 -04 -03|4H.E 4G.J 50 40 30|01213132323232323232343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434|-3eLvg.k MJbX.5 fJAh.f 5knG.J 1Vzh.f jRAG.J 1pbh.f 11d0 1oL0 11d0 1oL0 11d0 1oL0 11d0 1pb0 11d0 nHX0 op0 blz0 ko0 Qeo0 WL0 1zd0 On0 1ip0 11z0 1o10 11z0 1qN0 WL0 1ld0 14n0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 WL0 1qN0 1cL0 1cN0 11z0 1o10 11z0 1qN0 WL0 1fB0 19X0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 17b0 1ip0 11z0 1ip0 1fz0 1fB0 11z0 1qN0 WL0 1qN0 WL0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 17b0 1ip0 11z0 1o10 19X0 1fB0 1nX0 G10 1EL0 Op0 1zb0 Rd0 1wn0 Rd0 46n0 Ap0|", + "America/Winnipeg|LMT CST CDT CWT CPT|6s.A 60 50 50 50|0121212134121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-3kLtv.o 1a3bv.o WL0 3ND0 1in0 Jap0 Rb0 aCN0 8x30 iw0 1tB0 11z0 1ip0 11z0 1o10 11z0 1o10 11z0 1rd0 10L0 1op0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 1cL0 1cN0 11z0 6i10 WL0 6i10 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1a00 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1a00 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 14o0 1lc0 14o0 1o00 11A0 1o00 11A0 1o00 14o0 1lc0 14o0 1lc0 14o0 1o00 11A0 1o00 11A0 1o00 14o0 1lc0 14o0 1lc0 14o0 1lc0 14o0 1o00 11A0 1o00 11A0 1o00 14o0 1lc0 14o0 1lc0 14o0 1o00 11A0 1o00 11A0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|66e4", + "America/Rankin_Inlet|-00 CST CDT EST|0 60 50 50|01212121212121212121212121212121212121212121212121212121212321212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-vDc0 Bjk0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|26e2", + "America/Recife|LMT -03 -02|2j.A 30 20|0121212121212121212121212121212121212121|-2glxE.o HdLE.o 1cc0 1e10 1bX0 Ezd0 So0 1vA0 Mn0 1BB0 ML0 1BB0 zX0 qe10 xb0 2ep0 nz0 1C10 zX0 1C10 LX0 1C10 Mn0 H210 Rb0 1tB0 IL0 1Fd0 FX0 1EN0 FX0 1HB0 Lz0 nsp0 WL0 1tB0 2L0 2pB0 On0|33e5", + "America/Regina|LMT MST MDT MWT MPT CST|6W.A 70 60 60 60 60|012121212121212121212121341212121212121212121212121215|-2AD51.o uHe1.o 1in0 s2L0 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 11z0 66N0 1cL0 1cN0 19X0 1fB0 1cL0 1fB0 1cL0 1cN0 1cL0 M30 8x20 ix0 1ip0 1cL0 1ip0 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 11z0 1o10 11z0 3NB0 1cL0 1cN0|19e4", + "America/Resolute|-00 CST CDT EST|0 60 50 50|01212121212121212121212121212121212121212121212121212121212321212121212321212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-SnA0 103I0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|229", + "America/Santarem|LMT -04 -03|3C.M 40 30|0121212121212121212121212121212|-2glwl.c HdLl.c 1cc0 1e10 1bX0 Ezd0 So0 1vA0 Mn0 1BB0 ML0 1BB0 zX0 qe10 xb0 2ep0 nz0 1C10 zX0 1C10 LX0 1C10 Mn0 H210 Rb0 1tB0 IL0 1Fd0 FX0 NBd0|21e4", + "America/Santiago|LMT SMT -05 -04 -03|4G.J 4G.J 50 40 30|0121313232323232323432343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434|-3eLvh.f MJc0 fJAh.f 5knG.J 1Vzh.f jRAG.J 1pbh.f 11d0 1oL0 11d0 1oL0 11d0 1oL0 11d0 1pb0 11d0 nHX0 op0 9Bz0 hX0 1q10 ko0 Qeo0 WL0 1zd0 On0 1ip0 11z0 1o10 11z0 1qN0 WL0 1ld0 14n0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 WL0 1qN0 1cL0 1cN0 11z0 1o10 11z0 1qN0 WL0 1fB0 19X0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 17b0 1ip0 11z0 1ip0 1fz0 1fB0 11z0 1qN0 WL0 1qN0 WL0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 17b0 1ip0 11z0 1o10 19X0 1fB0 1nX0 G10 1EL0 Op0 1zb0 Rd0 1wn0 Rd0 46n0 Ap0 1Nb0 Ap0 1Nb0 Ap0 1zb0 11B0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 11B0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0|62e5", + "America/Santo_Domingo|LMT SDMT EST EDT -0430 AST|4D.A 4E 50 40 4u 40|012324242424242525|-3eLvk.o 1Jic0.o 1lJMk Mn0 6sp0 Lbu 1Cou yLu 1RAu wLu 1QMu xzu 1Q0u xXu 1PAu 13jB0 e00|29e5", + "America/Sao_Paulo|LMT -03 -02|36.s 30 20|01212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-2glwR.w HdKR.w 1cc0 1e10 1bX0 Ezd0 So0 1vA0 Mn0 1BB0 ML0 1BB0 zX0 pTd0 PX0 2ep0 nz0 1C10 zX0 1C10 LX0 1C10 Mn0 H210 Rb0 1tB0 IL0 1Fd0 FX0 1EN0 FX0 1HB0 Lz0 1EN0 Lz0 1C10 IL0 1HB0 Db0 1HB0 On0 1zd0 On0 1zd0 Lz0 1zd0 Rb0 1wN0 Wn0 1tB0 Rb0 1tB0 WL0 1tB0 Rb0 1zd0 On0 1HB0 FX0 1C10 Lz0 1Ip0 HX0 1zd0 On0 1HB0 IL0 1wp0 On0 1C10 Lz0 1C10 On0 1zd0 On0 1zd0 Rb0 1zd0 Lz0 1C10 Lz0 1C10 On0 1zd0 On0 1zd0 On0 1zd0 On0 1HB0 FX0|20e6", + "America/Scoresbysund|LMT -02 -01 +00|1r.Q 20 10 0|0121323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232|-2a5Ww.8 2z5ew.8 1a00 1cK0 1cL0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|452", + "America/Sitka|LMT LMT PST PWT PPT PDT YST AKST AKDT|-eW.L 91.d 80 70 70 70 90 90 80|0123425252525252525252525252525252567878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787|-48Pzs.L 1jVwu 1EX0W.L 8x10 iy0 Vo10 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 co0 10q0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|90e2", + "America/St_Johns|LMT NST NDT NST NDT NWT NPT NDDT|3u.Q 3u.Q 2u.Q 3u 2u 2u 2u 1u|012121212121212121212121212121212121213434343434343435634343434343434343434343434343434343434343434343434343434343434343434343434343434343437343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343|-3tokt.8 1l020 14L0 1nB0 1in0 1gm0 Dz0 1JB0 1cL0 1cN0 1cL0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1fB0 1cL0 1cN0 1cL0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1fB0 1cL0 1fB0 19X0 1fB0 19X0 10O0 eKX.8 19X0 1iq0 WL0 1qN0 WL0 1qN0 WL0 1tB0 TX0 1tB0 WL0 1qN0 WL0 1qN0 7UHu itu 1tB0 WL0 1qN0 WL0 1qN0 WL0 1qN0 WL0 1tB0 WL0 1ld0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14n1 1lb0 14p0 1nW0 11C0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zcX Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|11e4", + "America/Swift_Current|LMT MST MDT MWT MPT CST|7b.k 70 60 60 60 60|012134121212121212121215|-2AD4M.E uHdM.E 1in0 UGp0 8x20 ix0 1o10 17b0 1ip0 11z0 1o10 11z0 1o10 11z0 isN0 1cL0 3Cp0 1cL0 1cN0 11z0 1qN0 WL0 pMp0|16e3", + "America/Tegucigalpa|LMT CST CDT|5M.Q 60 50|01212121|-1WGGb.8 2ETcb.8 WL0 1qN0 WL0 GRd0 AL0|11e5", + "America/Thule|LMT AST ADT|4z.8 40 30|012121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-2a5To.Q 31NBo.Q 1cL0 1cN0 1cL0 1fB0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|656", + "America/Vancouver|LMT PST PDT PWT PPT|8c.s 80 70 70 70|01213412121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-3tofL.w 1nspL.w 1in0 UGp0 8x10 iy0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|23e5", + "America/Whitehorse|LMT YST YDT YWT YPT YDDT PST PDT MST|90.c 90 80 80 80 70 80 70 70|0121213415167676767676767676767676767676767676767676767676767676767676767676767676767676767678|-2MSeX.M GWpX.M 1in0 1o10 13V0 Ser0 8x00 iz0 LCL0 1fA0 LA0 ytd0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1z90|23e3", + "America/Yakutat|LMT LMT YST YWT YPT YDT AKST AKDT|-eF.5 9i.T 90 80 80 80 90 80|0123425252525252525252525252525252526767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676|-48Pzs.L 1jVwL.G 1EX1F.5 8x00 iz0 Vo10 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 cn0 10q0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|642", + "Antarctica/Casey|-00 +08 +11|0 -80 -b0|0121212121212|-2q00 1DjS0 T90 40P0 KL0 blz0 3m10 1o30 14k0 1kr0 12l0 1o01|10", + "Antarctica/Davis|-00 +07 +05|0 -70 -50|01012121|-vyo0 iXt0 alj0 1D7v0 VB0 3Wn0 KN0|70", + "Pacific/Port_Moresby|LMT PMMT +10|-9M.E -9M.w -a0|012|-3D8VM.E AvA0.8|25e4", + "Antarctica/Macquarie|-00 AEST AEDT|0 -a0 -b0|0121012121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212|-2OPc0 Fb40 1a00 4SK0 1ayy0 Lvs0 1cM0 1o00 Rc0 1wo0 Rc0 1wo0 U00 1wo0 LA0 1C00 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 11A0 1qM0 WM0 1qM0 Oo0 1zc0 Oo0 1zc0 Oo0 1wo0 WM0 1tA0 WM0 1tA0 U00 1tA0 U00 1tA0 11A0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 11A0 1o00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1cM0 1a00 1io0 1cM0 1cM0 1cM0 1cM0 3Co0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0|1", + "Antarctica/Mawson|-00 +06 +05|0 -60 -50|012|-CEo0 2fyk0|60", + "Pacific/Auckland|LMT NZMT NZST NZST NZDT|-bD.4 -bu -cu -c0 -d0|012131313131313131313131313134343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434|-46jLD.4 2nEO9.4 Lz0 1tB0 11zu 1o0u 11zu 1o0u 11zu 1o0u 14nu 1lcu 14nu 1lcu 1lbu 11Au 1nXu 11Au 1nXu 11Au 1nXu 11Au 1nXu 11Au 1qLu WMu 1qLu 11Au 1n1bu IM0 1C00 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1qM0 14o0 1lc0 14o0 1lc0 14o0 1lc0 17c0 1io0 17c0 1io0 17c0 1io0 17c0 1lc0 14o0 1lc0 14o0 1lc0 17c0 1io0 17c0 1io0 17c0 1lc0 14o0 1lc0 14o0 1lc0 17c0 1io0 17c0 1io0 17c0 1io0 17c0 1io0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00|14e5", + "Antarctica/Palmer|-00 -03 -04 -02|0 30 40 20|0121212121213121212121212121212121212121212121212121212121212121212121212121212121|-cao0 nD0 1vd0 SL0 1vd0 17z0 1cN0 1fz0 1cN0 1cL0 1cN0 asn0 Db0 jsN0 14N0 11z0 1o10 11z0 1qN0 WL0 1qN0 WL0 1qN0 1cL0 1cN0 11z0 1o10 11z0 1qN0 WL0 1fB0 19X0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 17b0 1ip0 11z0 1ip0 1fz0 1fB0 11z0 1qN0 WL0 1qN0 WL0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 17b0 1ip0 11z0 1o10 19X0 1fB0 1nX0 G10 1EL0 Op0 1zb0 Rd0 1wn0 Rd0 46n0 Ap0|40", + "Antarctica/Rothera|-00 -03|0 30|01|gOo0|130", + "Asia/Riyadh|LMT +03|-36.Q -30|01|-TvD6.Q|57e5", + "Antarctica/Troll|-00 +00 +02|0 0 -20|01212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|1puo0 hd0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|40", + "Asia/Urumqi|LMT +06|-5O.k -60|01|-1GgtO.k|32e5", + "Europe/Berlin|LMT CET CEST CEMT|-R.s -10 -20 -30|012121212121212321212321212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-36RcR.s UbWR.s 11d0 1iO0 11A0 1o00 11A0 Qrc0 6i00 WM0 1fA0 1cM0 1cM0 1cM0 kL0 Nc0 m10 WM0 1ao0 1cp0 dX0 jz0 Dd0 1io0 17c0 1fA0 1a00 1ehA0 1a00 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|41e5", + "Asia/Almaty|LMT +05 +06 +07|-57.M -50 -60 -70|012323232323232323232321232323232323232323232323232|-1Pc57.M eUo7.M 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 2pB0 IM0 rX0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0|15e5", + "Asia/Amman|LMT EET EEST +03|-2n.I -20 -30 -30|0121212121212121212121212121212121212121212121212121212121212121212121212121212121212123|-1yW2n.I 1HiMn.I KL0 1oN0 11b0 1oN0 11b0 1pd0 1dz0 1cp0 11b0 1op0 11b0 fO10 1db0 1e10 1cL0 1cN0 1cL0 1cN0 1fz0 1pd0 10n0 1ld0 14n0 1hB0 15b0 1ip0 19X0 1cN0 1cL0 1cN0 17b0 1ld0 14o0 1lc0 17c0 1io0 17c0 1io0 17c0 1So0 y00 1fc0 1dc0 1co0 1dc0 1cM0 1cM0 1cM0 1o00 11A0 1lc0 17c0 1cM0 1cM0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 4bX0 Dd0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 LA0 1C00|25e5", + "Asia/Anadyr|LMT +12 +13 +14 +11|-bN.U -c0 -d0 -e0 -b0|01232121212121212121214121212121212121212121212121212121212141|-1PcbN.U eUnN.U 23CL0 1db0 2q10 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 2pB0 IM0 rX0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 2sp0 WM0|13e3", + "Asia/Aqtau|LMT +04 +05 +06|-3l.4 -40 -50 -60|012323232323232323232123232312121212121212121212|-1Pc3l.4 eUnl.4 24PX0 2pX0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 2pB0 IM0 rX0 1cM0 1cM0 1cM0 1cM0 1cM0 1cN0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0|15e4", + "Asia/Aqtobe|LMT +04 +05 +06|-3M.E -40 -50 -60|0123232323232323232321232323232323232323232323232|-1Pc3M.E eUnM.E 23CL0 3Db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 2pB0 IM0 rX0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0|27e4", + "Asia/Ashgabat|LMT +04 +05 +06|-3R.w -40 -50 -60|0123232323232323232323212|-1Pc3R.w eUnR.w 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 2pB0 IM0|41e4", + "Asia/Atyrau|LMT +03 +05 +06 +04|-3r.I -30 -50 -60 -40|01232323232323232323242323232323232324242424242|-1Pc3r.I eUor.I 24PW0 2pX0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 2pB0 IM0 rX0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 2sp0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0|", + "Asia/Baghdad|LMT BMT +03 +04|-2V.E -2V.A -30 -40|0123232323232323232323232323232323232323232323232323232|-3eLCV.E 18ao0.4 2ACnV.A 11b0 1cp0 1dz0 1dd0 1db0 1cN0 1cp0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1de0 1dc0 1dc0 1dc0 1cM0 1dc0 1cM0 1dc0 1cM0 1dc0 1dc0 1dc0 1cM0 1dc0 1cM0 1dc0 1cM0 1dc0 1dc0 1dc0 1cM0 1dc0 1cM0 1dc0 1cM0 1dc0 1dc0 1dc0 1cM0 1dc0 1cM0 1dc0 1cM0 1dc0|66e5", + "Asia/Qatar|LMT +04 +03|-3q.8 -40 -30|012|-21Jfq.8 27BXq.8|96e4", + "Asia/Baku|LMT +03 +04 +05|-3j.o -30 -40 -50|01232323232323232323232123232323232323232323232323232323232323232|-1Pc3j.o 1jUoj.o WCL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 2pB0 1cM0 9Je0 1o00 11z0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|27e5", + "Asia/Bangkok|LMT BMT +07|-6G.4 -6G.4 -70|012|-3D8SG.4 1C000|15e6", + "Asia/Barnaul|LMT +06 +07 +08|-5z -60 -70 -80|0123232323232323232323212323232321212121212121212121212121212121212|-21S5z pCnz 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 2pB0 IM0 rX0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 p90 LE0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 8Hz0 3rd0|", + "Asia/Beirut|LMT EET EEST|-2m -20 -30|0121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-3D8Om 1BWom 1on0 1410 1db0 19B0 1in0 1ip0 WL0 1lQp0 11b0 1oN0 11b0 1oN0 11b0 1pd0 11b0 1oN0 11b0 q6N0 En0 1oN0 11b0 1oN0 11b0 1oN0 11b0 1pd0 11b0 1oN0 11b0 1op0 11b0 dA10 17b0 1iN0 17b0 1iN0 17b0 1iN0 17b0 1vB0 SL0 1mp0 13z0 1iN0 17b0 1iN0 17b0 1jd0 12n0 1a10 1cL0 1cN0 1cL0 1cN0 1cL0 1fB0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0|22e5", + "Asia/Bishkek|LMT +05 +06 +07|-4W.o -50 -60 -70|012323232323232323232321212121212121212121212121212|-1Pc4W.o eUnW.o 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 2e00 1tX0 17b0 1ip0 17b0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1cPu 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0|87e4", + "Asia/Brunei|LMT +0730 +08 +0820 +09|-7l.k -7u -80 -8k -90|0123232323232323242|-1KITl.k gDbP.k 6ynu AnE 1O0k AnE 1NAk AnE 1NAk AnE 1NAk AnE 1O0k AnE 1NAk AnE pAk 8Fz0|42e4", + "Asia/Kolkata|LMT HMT MMT IST +0630|-5R.s -5R.k -5l.a -5u -6u|01234343|-4Fg5R.s BKo0.8 1rDcw.a 1r2LP.a 1un0 HB0 7zX0|15e6", + "Asia/Chita|LMT +08 +09 +10|-7x.Q -80 -90 -a0|012323232323232323232321232323232323232323232323232323232323232312|-21Q7x.Q pAnx.Q 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 2pB0 IM0 rX0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 8Hz0 3re0|33e4", + "Asia/Choibalsan|LMT +07 +08 +10 +09|-7C -70 -80 -a0 -90|0123434343434343434343434343434343434343434343424242|-2APHC 2UkoC cKn0 1da0 1dd0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1fB0 1cL0 1cN0 1cL0 1cN0 1cL0 6hD0 11z0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 3Db0 h1f0 1cJ0 1cP0 1cJ0|38e3", + "Asia/Shanghai|LMT CST CDT|-85.H -80 -90|012121212121212121212121212121|-2M0U5.H Iuo5.H 18n0 OjB0 Rz0 11d0 1wL0 A10 8HX0 1G10 Tz0 1ip0 1jX0 1cN0 11b0 1oN0 aL0 1tU30 Rb0 1o10 11z0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0|23e6", + "Asia/Colombo|LMT MMT +0530 +06 +0630|-5j.o -5j.w -5u -60 -6u|012342432|-3D8Rj.o 13inX.Q 1rFbN.w 1zzu 7Apu 23dz0 11zu n3cu|22e5", + "Asia/Dhaka|LMT HMT +0630 +0530 +06 +07|-61.E -5R.k -6u -5u -60 -70|01232454|-3eLG1.E 26008.k 1unn.k HB0 m6n0 2kxbu 1i00|16e6", + "Asia/Damascus|LMT EET EEST +03|-2p.c -20 -30 -30|01212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212123|-21Jep.c Hep.c 17b0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1xRB0 11X0 1oN0 10L0 1pB0 11b0 1oN0 10L0 1mp0 13X0 1oN0 11b0 1pd0 11b0 1oN0 11b0 1oN0 11b0 1oN0 11b0 1pd0 11b0 1oN0 11b0 1oN0 11b0 1oN0 11b0 1pd0 11b0 1oN0 Nb0 1AN0 Nb0 bcp0 19X0 1gp0 19X0 3ld0 1xX0 Vd0 1Bz0 Sp0 1vX0 10p0 1dz0 1cN0 1cL0 1db0 1db0 1g10 1an0 1ap0 1db0 1fd0 1db0 1cN0 1db0 1dd0 1db0 1cp0 1dz0 1c10 1dX0 1cN0 1db0 1dd0 1db0 1cN0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1db0 1cN0 1db0 1cN0 19z0 1fB0 1qL0 11B0 1on0 Wp0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0|26e5", + "Asia/Dili|LMT +08 +09|-8m.k -80 -90|01212|-2le8m.k 1dnXm.k 1nfA0 Xld0|19e4", + "Asia/Dubai|LMT +04|-3F.c -40|01|-21JfF.c|39e5", + "Asia/Dushanbe|LMT +05 +06 +07|-4z.c -50 -60 -70|012323232323232323232321|-1Pc4z.c eUnz.c 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 2hB0|76e4", + "Asia/Famagusta|LMT EET EEST +03|-2f.M -20 -30 -30|0121212121212121212121212121212121212121212121212121212121212121212121212121212121212312121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-1Vc2f.M 2a3cf.M 1cL0 1qp0 Xz0 19B0 19X0 1fB0 1db0 1cp0 1cL0 1fB0 19X0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1fB0 1cL0 1cN0 1cL0 1cN0 1o30 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 15U0 2Ks0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|", + "Asia/Gaza|LMT EET EEST IST IDT|-2h.Q -20 -30 -20 -30|01212121212121212121212121212121212343434343434343434343434343434312121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-2MBCh.Q 1Azeh.Q MM0 iM0 4JA0 10o0 1pA0 10M0 1pA0 16o0 1jA0 16o0 1jA0 pBa0 Vz0 1oN0 11b0 1oO0 10N0 1pz0 10N0 1pb0 10N0 1pb0 10N0 1pb0 10N0 1pz0 10N0 1pb0 10N0 1pb0 11d0 1oL0 dW0 hfB0 Db0 1fB0 Rb0 bXB0 gM0 8Q00 IM0 1wo0 TX0 1HB0 IL0 1s10 10n0 1o10 WL0 1zd0 On0 1ld0 11z0 1o10 14n0 1o10 14n0 1nd0 12n0 1nd0 Xz0 1q10 12n0 M10 C00 17c0 1io0 17c0 1io0 17c0 1o00 1cL0 1fB0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 17c0 1io0 18N0 1bz0 19z0 1gp0 1610 1iL0 11z0 1o10 14o0 1lA1 SKX 1xd1 MKX 1AN0 1a00 1fA0 1cL0 1cN0 1nX0 1210 1nA0 1210 1qL0 WN0 1qL0 WN0 1qL0 11c0 1on0 11B0 1o00 11A0 1qo0 XA0 1qp0 1cN0 1cL0 17d0 1in0 14p0 1lb0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1lb0 14p0 1in0 17d0 1cL0 1cN0 19X0 e10 2L0 WN0 14n0 gN0 5z0 11B0 WL0 e10 bb0 11B0 TX0 e10 dX0 11B0 On0 gN0 gL0 11B0 Lz0 e10 pb0 WN0 IL0 e10 rX0 WN0 Db0 gN0 uL0 11B0 xz0 e10 An0 11B0 rX0 gN0 Db0 11B0 pb0 e10 Lz0 WN0 mn0 e10 On0 WN0 gL0 gN0 Rb0 11B0 bb0 e10 WL0 11B0 5z0 gN0 11z0 11B0 2L0 gN0 14n0 1fB0 1cL0 1a10 1fz0 14p0 1lb0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1nX0 14p0 1in0 17d0 1fz0 1a10 19X0 1fB0 17b0 e10 28L0 e10 25X0 gN0 25X0 e10 gL0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0|18e5", + "Asia/Hebron|LMT EET EEST IST IDT|-2k.n -20 -30 -20 -30|0121212121212121212121212121212121234343434343434343434343434343431212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-2MBCk.n 1Azek.n MM0 iM0 4JA0 10o0 1pA0 10M0 1pA0 16o0 1jA0 16o0 1jA0 pBa0 Vz0 1oN0 11b0 1oO0 10N0 1pz0 10N0 1pb0 10N0 1pb0 10N0 1pb0 10N0 1pz0 10N0 1pb0 10N0 1pb0 11d0 1oL0 dW0 hfB0 Db0 1fB0 Rb0 bXB0 gM0 8Q00 IM0 1wo0 TX0 1HB0 IL0 1s10 10n0 1o10 WL0 1zd0 On0 1ld0 11z0 1o10 14n0 1o10 14n0 1nd0 12n0 1nd0 Xz0 1q10 12n0 M10 C00 17c0 1io0 17c0 1io0 17c0 1o00 1cL0 1fB0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 17c0 1io0 18N0 1bz0 19z0 1gp0 1610 1iL0 12L0 1mN0 14o0 1lc0 Tb0 1xd1 MKX bB0 cn0 1cN0 1a00 1fA0 1cL0 1cN0 1nX0 1210 1nA0 1210 1qL0 WN0 1qL0 WN0 1qL0 11c0 1on0 11B0 1o00 11A0 1qo0 XA0 1qp0 1cN0 1cL0 17d0 1in0 14p0 1lb0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1lb0 14p0 1in0 17d0 1cL0 1cN0 19X0 e10 2L0 WN0 14n0 gN0 5z0 11B0 WL0 e10 bb0 11B0 TX0 e10 dX0 11B0 On0 gN0 gL0 11B0 Lz0 e10 pb0 WN0 IL0 e10 rX0 WN0 Db0 gN0 uL0 11B0 xz0 e10 An0 11B0 rX0 gN0 Db0 11B0 pb0 e10 Lz0 WN0 mn0 e10 On0 WN0 gL0 gN0 Rb0 11B0 bb0 e10 WL0 11B0 5z0 gN0 11z0 11B0 2L0 gN0 14n0 1fB0 1cL0 1a10 1fz0 14p0 1lb0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1nX0 14p0 1in0 17d0 1fz0 1a10 19X0 1fB0 17b0 e10 28L0 e10 25X0 gN0 25X0 e10 gL0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0|25e4", + "Asia/Ho_Chi_Minh|LMT PLMT +07 +08 +09|-76.u -76.u -70 -80 -90|0123423232|-2yC76.u bK00 1h7b6.u 5lz0 18o0 3Oq0 k5b0 aW00 BAM0|90e5", + "Asia/Hong_Kong|LMT HKT HKST HKWT JST|-7A.G -80 -90 -8u -90|0123412121212121212121212121212121212121212121212121212121212121212121|-2CFH0 1taO0 Hc0 xUu 9tBu 11z0 1tDu Rc0 1wo0 11A0 1cM0 11A0 1o00 11A0 1o00 11A0 1o00 14o0 1o00 11A0 1nX0 U10 1tz0 U10 1wn0 Rd0 1wn0 U10 1tz0 U10 1tz0 U10 1tz0 U10 1wn0 Rd0 1wn0 Rd0 1wn0 U10 1tz0 U10 1tz0 17d0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 s10 1Vz0 1cN0 1cL0 1cN0 1cL0 6fd0 14n0|73e5", + "Asia/Hovd|LMT +06 +07 +08|-66.A -60 -70 -80|012323232323232323232323232323232323232323232323232|-2APG6.A 2Uko6.A cKn0 1db0 1dd0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1fB0 1cL0 1cN0 1cL0 1cN0 1cL0 6hD0 11z0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 kEp0 1cJ0 1cP0 1cJ0|81e3", + "Asia/Irkutsk|LMT IMT +07 +08 +09|-6V.5 -6V.5 -70 -80 -90|012343434343434343434343234343434343434343434343434343434343434343|-3D8SV.5 1Bxc0 pjXV.5 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 2pB0 IM0 rX0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 8Hz0|60e4", + "Europe/Istanbul|LMT IMT EET EEST +03 +04|-1T.Q -1U.U -20 -30 -30 -40|01232323232323232323232323232323232323232323232345423232323232323232323232323232323232323232323232323232323232323234|-3D8NT.Q 1ePXW.U dzzU.U 11b0 8tB0 1on0 1410 1db0 19B0 1in0 3Rd0 Un0 1oN0 11b0 zSN0 CL0 mp0 1Vz0 1gN0 8yn0 1yp0 ML0 1kp0 17b0 1ip0 17b0 1fB0 19X0 1ip0 19X0 1ip0 17b0 qdB0 38L0 1jd0 Tz0 l6O0 11A0 WN0 1qL0 TB0 1tX0 U10 1tz0 11B0 1in0 17d0 z90 cne0 pb0 2Cp0 1800 14o0 1dc0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1a00 1fA0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WO0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 Xc0 1qo0 WM0 1qM0 11A0 1o00 1200 1nA0 11A0 1tA0 U00 15w0|13e6", + "Asia/Jakarta|LMT BMT +0720 +0730 +09 +08 WIB|-77.c -77.c -7k -7u -90 -80 -70|012343536|-49jH7.c 2hiLL.c luM0 mPzO 8vWu 6kpu 4PXu xhcu|31e6", + "Asia/Jayapura|LMT +09 +0930 WIT|-9m.M -90 -9u -90|0123|-1uu9m.M sMMm.M L4nu|26e4", + "Asia/Jerusalem|LMT JMT IST IDT IDDT|-2k.S -2k.E -20 -30 -40|012323232323232432323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232|-3D8Ok.S 1wvA0.e SyOk.E MM0 iM0 4JA0 10o0 1pA0 10M0 1pA0 16o0 1jA0 16o0 1jA0 3LA0 Eo0 oo0 1co0 1dA0 16o0 10M0 1jc0 1tA0 14o0 1cM0 1a00 11A0 1Nc0 Ao0 1Nc0 Ao0 1Ko0 LA0 1o00 WM0 EQK0 Db0 1fB0 Rb0 bXB0 gM0 8Q00 IM0 1wo0 TX0 1HB0 IL0 1s10 10n0 1o10 WL0 1zd0 On0 1ld0 11z0 1o10 14n0 1o10 14n0 1nd0 12n0 1nd0 Xz0 1q10 12n0 1hB0 1dX0 1ep0 1aL0 1eN0 17X0 1nf0 11z0 1tB0 19W0 1e10 17b0 1ep0 1gL0 18N0 1fz0 1eN0 17b0 1gq0 1gn0 19d0 1dz0 1c10 17X0 1hB0 1gn0 19d0 1dz0 1c10 17X0 1kp0 1dz0 1c10 1aL0 1eN0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0|81e4", + "Asia/Kabul|LMT +04 +0430|-4A.M -40 -4u|012|-3eLEA.M 2dTcA.M|46e5", + "Asia/Kamchatka|LMT +11 +12 +13|-ay.A -b0 -c0 -d0|012323232323232323232321232323232323232323232323232323232323212|-1SLKy.A ivXy.A 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 2pB0 IM0 rX0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 2sp0 WM0|18e4", + "Asia/Karachi|LMT +0530 +0630 +05 PKT PKST|-4s.c -5u -6u -50 -50 -60|012134545454|-2xoss.c 1qOKW.c 7zX0 eup0 LqMu 1fy00 1cL0 dK10 11b0 1610 1jX0|24e6", + "Asia/Kathmandu|LMT +0530 +0545|-5F.g -5u -5J|012|-21JhF.g 2EGMb.g|12e5", + "Asia/Khandyga|LMT +08 +09 +10 +11|-92.d -80 -90 -a0 -b0|0123232323232323232323212323232323232323232323232343434343434343432|-21Q92.d pAp2.d 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 2pB0 IM0 rX0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 qK0 yN0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 17V0 7zD0|66e2", + "Asia/Krasnoyarsk|LMT +06 +07 +08|-6b.q -60 -70 -80|01232323232323232323232123232323232323232323232323232323232323232|-21Hib.q prAb.q 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 2pB0 IM0 rX0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 8Hz0|10e5", + "Asia/Kuala_Lumpur|LMT SMT +07 +0720 +0730 +09 +08|-6T.p -6T.p -70 -7k -7u -90 -80|01234546|-2M0ST.p aIM0 17anT.p l5XE 17bO 8Fyu 1so10|71e5", + "Asia/Macau|LMT CST +09 +10 CDT|-7y.a -80 -90 -a0 -90|012323214141414141414141414141414141414141414141414141414141414141414141|-2CFHy.a 1uqKy.a PX0 1kn0 15B0 11b0 4Qq0 1oM0 11c0 1ko0 1u00 11A0 1cM0 11c0 1o00 11A0 1o00 11A0 1oo0 1400 1o00 11A0 1o00 U00 1tA0 U00 1wo0 Rc0 1wru U10 1tz0 U10 1tz0 U10 1tz0 U10 1wn0 Rd0 1wn0 Rd0 1wn0 U10 1tz0 U10 1tz0 17d0 1cK0 1cO0 1cK0 1cO0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 s10 1Vz0 1cN0 1cL0 1cN0 1cL0 6fd0 14n0|57e4", + "Asia/Magadan|LMT +10 +11 +12|-a3.c -a0 -b0 -c0|012323232323232323232321232323232323232323232323232323232323232312|-1Pca3.c eUo3.c 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 2pB0 IM0 rX0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 8Hz0 3Cq0|95e3", + "Asia/Makassar|LMT MMT +08 +09 WITA|-7V.A -7V.A -80 -90 -80|01234|-21JjV.A vfc0 myLV.A 8ML0|15e5", + "Asia/Manila|LMT LMT PST PDT JST|fU -84 -80 -90 -90|01232423232|-54m84 2clc0 1vfc4 AL0 cK10 65X0 mXB0 vX0 VK10 1db0|24e6", + "Asia/Nicosia|LMT EET EEST|-2d.s -20 -30|01212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-1Vc2d.s 2a3cd.s 1cL0 1qp0 Xz0 19B0 19X0 1fB0 1db0 1cp0 1cL0 1fB0 19X0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1fB0 1cL0 1cN0 1cL0 1cN0 1o30 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|32e4", + "Asia/Novokuznetsk|LMT +06 +07 +08|-5M.M -60 -70 -80|012323232323232323232321232323232323232323232323232323232323212|-1PctM.M eULM.M 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 2pB0 IM0 rX0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 2sp0 WM0|55e4", + "Asia/Novosibirsk|LMT +06 +07 +08|-5v.E -60 -70 -80|0123232323232323232323212323212121212121212121212121212121212121212|-21Qnv.E pAFv.E 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 2pB0 IM0 rX0 1cM0 1cM0 ml0 Os0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 8Hz0 4eN0|15e5", + "Asia/Omsk|LMT +05 +06 +07|-4R.u -50 -60 -70|01232323232323232323232123232323232323232323232323232323232323232|-224sR.u pMLR.u 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 2pB0 IM0 rX0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 8Hz0|12e5", + "Asia/Oral|LMT +03 +05 +06 +04|-3p.o -30 -50 -60 -40|01232323232323232424242424242424242424242424242|-1Pc3p.o eUop.o 23CK0 3Db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 2pB0 1cM0 1fA0 1cM0 1cM0 IM0 1EM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0|27e4", + "Asia/Pontianak|LMT PMT +0730 +09 +08 WITA WIB|-7h.k -7h.k -7u -90 -80 -80 -70|012324256|-2ua7h.k XE00 munL.k 8Rau 6kpu 4PXu xhcu Wqnu|23e4", + "Asia/Pyongyang|LMT KST JST KST|-8n -8u -90 -90|012313|-2um8n 97XR 1lTzu 2Onc0 6BA0|29e5", + "Asia/Qostanay|LMT +04 +05 +06|-4e.s -40 -50 -60|012323232323232323232123232323232323232323232323|-1Pc4e.s eUoe.s 23CL0 3Db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 2pB0 IM0 rX0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0|", + "Asia/Qyzylorda|LMT +04 +05 +06|-4l.Q -40 -50 -60|01232323232323232323232323232323232323232323232|-1Pc4l.Q eUol.Q 23CL0 3Db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 3ao0 1EM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 zQl0|73e4", + "Asia/Rangoon|LMT RMT +0630 +09|-6o.L -6o.L -6u -90|01232|-3D8So.L 1BnA0 SmnS.L 7j9u|48e5", + "Asia/Sakhalin|LMT +09 +11 +12 +10|-9u.M -90 -b0 -c0 -a0|01232323232323232323232423232323232424242424242424242424242424242|-2AGVu.M 1BoMu.M 1qFa0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 2pB0 IM0 rX0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 2pB0 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 8Hz0 3rd0|58e4", + "Asia/Samarkand|LMT +04 +05 +06|-4r.R -40 -50 -60|01232323232323232323232|-1Pc4r.R eUor.R 23CL0 3Db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0|36e4", + "Asia/Seoul|LMT KST JST KST KDT KDT|-8r.Q -8u -90 -90 -a0 -9u|012343434343151515151515134343|-2um8r.Q 97XV.Q 1m1zu 6CM0 Fz0 1kN0 14n0 1kN0 14L0 1zd0 On0 69B0 2I0u OL0 1FB0 Rb0 1qN0 TX0 1tB0 TX0 1tB0 TX0 1tB0 TX0 2ap0 12FBu 11A0 1o00 11A0|23e6", + "Asia/Srednekolymsk|LMT +10 +11 +12|-ae.Q -a0 -b0 -c0|01232323232323232323232123232323232323232323232323232323232323232|-1Pcae.Q eUoe.Q 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 2pB0 IM0 rX0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 8Hz0|35e2", + "Asia/Taipei|LMT CST JST CDT|-86 -80 -90 -90|012131313131313131313131313131313131313131|-30bk6 1FDc6 joM0 1yo0 Tz0 1ip0 1jX0 1cN0 11b0 1oN0 11b0 1oN0 11b0 1oN0 11b0 10N0 1BX0 10p0 1pz0 10p0 1pz0 10p0 1db0 1dd0 1db0 1cN0 1db0 1cN0 1db0 1cN0 1db0 1BB0 ML0 1Bd0 ML0 uq10 1db0 1cN0 1db0 97B0 AL0|74e5", + "Asia/Tashkent|LMT +05 +06 +07|-4B.b -50 -60 -70|012323232323232323232321|-1Pc4B.b eUnB.b 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 2pB0|23e5", + "Asia/Tbilisi|LMT TBMT +03 +04 +05|-2X.b -2X.b -30 -40 -50|01234343434343434343434323232343434343434343434323|-3D8OX.b 1LUM0 1jUnX.b WCL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 2pB0 1cK0 1cL0 1cN0 1cL0 1cN0 2pz0 1cL0 1fB0 3Nz0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 An0 Os0 WM0|11e5", + "Asia/Tehran|LMT TMT +0330 +0430 +04 +05|-3p.I -3p.I -3u -4u -40 -50|012345423232323232323232323232323232323232323232323232323232323232323232|-2btDp.I Llc0 1FHaT.I 1pc0 120u Rc0 XA0 Wou JX0 1dB0 1en0 pNB0 UL0 1cN0 1dz0 1cp0 1dz0 1cp0 1dz0 1cp0 1dz0 1cp0 1dz0 1cN0 1dz0 1cp0 1dz0 1cp0 1dz0 1cp0 1dz0 1cN0 1dz0 1cp0 1dz0 1cp0 1dz0 1cp0 1dz0 1cN0 1dz0 64p0 1dz0 1cN0 1dz0 1cp0 1dz0 1cp0 1dz0 1cp0 1dz0 1cN0 1dz0 1cp0 1dz0 1cp0 1dz0 1cp0 1dz0 1cN0 1dz0 1cp0 1dz0 1cp0 1dz0 1cp0 1dz0 1cN0 1dz0 1cp0 1dz0|14e6", + "Asia/Thimphu|LMT +0530 +06|-5W.A -5u -60|012|-Su5W.A 1BGMs.A|79e3", + "Asia/Tokyo|LMT JST JDT|-9i.X -90 -a0|0121212121|-3jE90 2qSo0 Rc0 1lc0 14o0 1zc0 Oo0 1zc0 Oo0|38e6", + "Asia/Tomsk|LMT +06 +07 +08|-5D.P -60 -70 -80|0123232323232323232323212323232323232323232323212121212121212121212|-21NhD.P pxzD.P 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 2pB0 IM0 rX0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 co0 1bB0 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 8Hz0 3Qp0|10e5", + "Asia/Ulaanbaatar|LMT +07 +08 +09|-77.w -70 -80 -90|012323232323232323232323232323232323232323232323232|-2APH7.w 2Uko7.w cKn0 1db0 1dd0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1fB0 1cL0 1cN0 1cL0 1cN0 1cL0 6hD0 11z0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 kEp0 1cJ0 1cP0 1cJ0|12e5", + "Asia/Ust-Nera|LMT +08 +09 +12 +11 +10|-9w.S -80 -90 -c0 -b0 -a0|012343434343434343434345434343434343434343434343434343434343434345|-21Q9w.S pApw.S 23CL0 1d90 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 2pB0 IM0 rX0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 17V0 7zD0|65e2", + "Asia/Vladivostok|LMT +09 +10 +11|-8L.v -90 -a0 -b0|01232323232323232323232123232323232323232323232323232323232323232|-1SJIL.v itXL.v 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 2pB0 IM0 rX0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 8Hz0|60e4", + "Asia/Yakutsk|LMT +08 +09 +10|-8C.W -80 -90 -a0|01232323232323232323232123232323232323232323232323232323232323232|-21Q8C.W pAoC.W 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 2pB0 IM0 rX0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 8Hz0|28e4", + "Asia/Yekaterinburg|LMT PMT +04 +05 +06|-42.x -3J.5 -40 -50 -60|012343434343434343434343234343434343434343434343434343434343434343|-2ag42.x 7mQh.s qBvJ.5 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 2pB0 IM0 rX0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 8Hz0|14e5", + "Asia/Yerevan|LMT +03 +04 +05|-2W -30 -40 -50|0123232323232323232323212121212323232323232323232323232323232|-1Pc2W 1jUnW WCL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 2pB0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 4RX0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0|13e5", + "Atlantic/Azores|LMT HMT -02 -01 +00 WET|1G.E 1S.w 20 10 0 0|01232323232323232323232323232323232323232323234323432343234323232323232323232323232323232323232323232343434343434343434343434343434345434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343|-3tomh.k 18aoh.k aPX0 Sp0 LX0 1vc0 Tc0 1uM0 SM0 1vc0 Tc0 1vc0 SM0 1vc0 6600 1co0 3E00 17c0 1fA0 1a00 1io0 1a00 1io0 17c0 3I00 17c0 1cM0 1cM0 3Fc0 1cM0 1a00 1fA0 1io0 17c0 1cM0 1cM0 1a00 1fA0 1io0 1qM0 Dc0 1tA0 1cM0 1dc0 1400 gL0 IM0 s10 U00 dX0 Rc0 pd0 Rc0 gL0 Oo0 pd0 Rc0 gL0 Oo0 pd0 14o0 1cM0 1cP0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 qIl0 1cM0 1fA0 1cM0 1cM0 1cN0 1cL0 1cN0 1cM0 1cM0 1cM0 1cM0 1cN0 1cL0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cL0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|25e4", + "Atlantic/Bermuda|LMT BMT BST AST ADT|4j.i 4j.i 3j.i 40 30|0121213434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343|-3eLvE.G 16mo0 1bb0 1i10 11X0 ru30 thbE.G 1PX0 11B0 1tz0 Rd0 1zb0 Op0 1zb0 3I10 Lz0 1EN0 FX0 1HB0 FX0 1Kp0 Db0 1Kp0 Db0 1Kp0 FX0 93d0 11z0 GAp0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|65e3", + "Atlantic/Canary|LMT -01 WET WEST|11.A 10 0 -10|01232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232|-1UtaW.o XPAW.o 1lAK0 1a10 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|54e4", + "Atlantic/Cape_Verde|LMT -02 -01|1y.4 20 10|01212|-2ldW0 1eEo0 7zX0 1djf0|50e4", + "Atlantic/Faroe|LMT WET WEST|r.4 0 -10|01212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-2uSnw.U 2Wgow.U 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|49e3", + "Atlantic/Madeira|LMT FMT -01 +00 +01 WET WEST|17.A 17.A 10 0 -10 0 -10|01232323232323232323232323232323232323232323234323432343234323232323232323232323232323232323232323232565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565|-3tomQ.o 18anQ.o aPX0 Sp0 LX0 1vc0 Tc0 1uM0 SM0 1vc0 Tc0 1vc0 SM0 1vc0 6600 1co0 3E00 17c0 1fA0 1a00 1io0 1a00 1io0 17c0 3I00 17c0 1cM0 1cM0 3Fc0 1cM0 1a00 1fA0 1io0 17c0 1cM0 1cM0 1a00 1fA0 1io0 1qM0 Dc0 1tA0 1cM0 1dc0 1400 gL0 IM0 s10 U00 dX0 Rc0 pd0 Rc0 gL0 Oo0 pd0 Rc0 gL0 Oo0 pd0 14o0 1cM0 1cP0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 qIl0 1cM0 1fA0 1cM0 1cM0 1cN0 1cL0 1cN0 1cM0 1cM0 1cM0 1cM0 1cN0 1cL0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|27e4", + "Atlantic/South_Georgia|LMT -02|2q.8 20|01|-3eLxx.Q|30", + "Atlantic/Stanley|LMT SMT -04 -03 -02|3P.o 3P.o 40 30 20|0123232323232323434323232323232323232323232323232323232323232323232323|-3eLw8.A S200 12bA8.A 19X0 1fB0 19X0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 Cn0 1Cc10 WL0 1qL0 U10 1tz0 2mN0 WN0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1tz0 U10 1tz0 WN0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1tz0 WN0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qL0 WN0 1qN0 U10 1wn0 Rd0 1wn0 U10 1tz0 U10 1tz0 U10 1tz0 U10 1tz0 U10 1wn0 U10 1tz0 U10 1tz0 U10|21e2", + "Australia/Sydney|LMT AEST AEDT|-a4.Q -a0 -b0|01212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212|-32oW4.Q RlC4.Q xc0 10jc0 yM0 1cM0 1cM0 1fA0 1a00 17c00 LA0 1C00 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 14o0 1o00 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 U00 1qM0 WM0 1tA0 WM0 1tA0 U00 1tA0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 11A0 1o00 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 11A0 1o00 WM0 1qM0 14o0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0|40e5", + "Australia/Adelaide|LMT ACST ACST ACDT|-9e.k -90 -9u -au|012323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323|-32oVe.k ak0e.k H1Bu xc0 10jc0 yM0 1cM0 1cM0 1fA0 1a00 17c00 LA0 1C00 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 U00 1qM0 WM0 1tA0 WM0 1tA0 U00 1tA0 U00 1tA0 Oo0 1zc0 WM0 1qM0 Rc0 1zc0 U00 1tA0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 11A0 1o00 WM0 1qM0 14o0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0|11e5", + "Australia/Brisbane|LMT AEST AEDT|-ac.8 -a0 -b0|012121212121212121|-32Bmc.8 Ry2c.8 xc0 10jc0 yM0 1cM0 1cM0 1fA0 1a00 17c00 LA0 H1A0 Oo0 1zc0 Oo0 1zc0 Oo0|20e5", + "Australia/Broken_Hill|LMT AEST ACST ACST ACDT|-9p.M -a0 -90 -9u -au|0123434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434|-32oVp.M 3Lzp.M 6wp0 H1Bu xc0 10jc0 yM0 1cM0 1cM0 1fA0 1a00 17c00 LA0 1C00 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 14o0 1o00 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 U00 1qM0 WM0 1tA0 WM0 1tA0 U00 1tA0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 11A0 1o00 WM0 1qM0 14o0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0|18e3", + "Australia/Hobart|LMT AEST AEDT|-9N.g -a0 -b0|01212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212|-3109N.g Pk1N.g 1a00 1qM0 Oo0 1zc0 Oo0 TAo0 yM0 1cM0 1cM0 1fA0 1a00 VfA0 1cM0 1o00 Rc0 1wo0 Rc0 1wo0 U00 1wo0 LA0 1C00 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 11A0 1qM0 WM0 1qM0 Oo0 1zc0 Oo0 1zc0 Oo0 1wo0 WM0 1tA0 WM0 1tA0 U00 1tA0 U00 1tA0 11A0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 11A0 1o00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1cM0 1a00 1io0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0|21e4", + "Australia/Darwin|LMT ACST ACST ACDT|-8H.k -90 -9u -au|01232323232|-32oUH.k ajXH.k H1Bu xc0 10jc0 yM0 1cM0 1cM0 1fA0 1a00|12e4", + "Australia/Eucla|LMT +0845 +0945|-8z.s -8J -9J|01212121212121212121|-30nIz.s PkpO.s xc0 10jc0 yM0 1cM0 1cM0 1gSo0 Oo0 l5A0 Oo0 iJA0 G00 zU00 IM0 1qM0 11A0 1o00 11A0|368", + "Australia/Lord_Howe|LMT AEST +1030 +1130 +11|-aA.k -a0 -au -bu -b0|01232323232424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424|-32oWA.k 3tzAA.k 1zdu Rb0 1zd0 On0 1zd0 On0 1zd0 On0 1zd0 TXu 1qMu WLu 1tAu WLu 1tAu TXu 1tAu Onu 1zcu Onu 1zcu Onu 1zcu Rbu 1zcu Onu 1zcu Onu 1zcu 11zu 1o0u 11zu 1o0u 11zu 1o0u 11zu 1qMu WLu 11Au 1nXu 1qMu 11zu 1o0u 11zu 1o0u 11zu 1qMu WLu 1qMu 11zu 1o0u WLu 1qMu 14nu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1fzu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1fzu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1fzu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1fzu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1fzu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1fzu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1fzu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1fzu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1fzu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1fzu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1fzu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1fzu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1fzu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1fzu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1fzu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1fzu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu|347", + "Australia/Lindeman|LMT AEST AEDT|-9T.U -a0 -b0|0121212121212121212121|-32BlT.U Ry1T.U xc0 10jc0 yM0 1cM0 1cM0 1fA0 1a00 17c00 LA0 H1A0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0|10", + "Australia/Melbourne|LMT AEST AEDT|-9D.Q -a0 -b0|01212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212|-32oVD.Q RlBD.Q xc0 10jc0 yM0 1cM0 1cM0 1fA0 1a00 17c00 LA0 1C00 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 U00 1qM0 WM0 1qM0 11A0 1tA0 U00 1tA0 U00 1tA0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 11A0 1o00 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 11A0 1o00 WM0 1qM0 14o0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0|39e5", + "Australia/Perth|LMT AWST AWDT|-7H.o -80 -90|01212121212121212121|-30nHH.o PkpH.o xc0 10jc0 yM0 1cM0 1cM0 1gSo0 Oo0 l5A0 Oo0 iJA0 G00 zU00 IM0 1qM0 11A0 1o00 11A0|18e5", + "CET|CET CEST|-10 -20|01010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-2aFe0 11d0 1iO0 11A0 1o00 11A0 Qrc0 6i00 WM0 1fA0 1cM0 1cM0 1cM0 16M0 1gMM0 1a00 1fA0 1cM0 1cM0 1cM0 1fA0 1a00 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|", + "Pacific/Easter|LMT EMT -07 -06 -05|7h.s 7h.s 70 60 50|0123232323232323232323232323234343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434|-3eLsG.w 1HRc0 1s4IG.w WL0 1zd0 On0 1ip0 11z0 1o10 11z0 1qN0 WL0 1ld0 14n0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 WL0 1qN0 11z0 1o10 2pA0 11z0 1o10 11z0 1qN0 WL0 1qN0 WL0 1qN0 1cL0 1cN0 11z0 1o10 11z0 1qN0 WL0 1fB0 19X0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 17b0 1ip0 11z0 1ip0 1fz0 1fB0 11z0 1qN0 WL0 1qN0 WL0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 17b0 1ip0 11z0 1o10 19X0 1fB0 1nX0 G10 1EL0 Op0 1zb0 Rd0 1wn0 Rd0 46n0 Ap0 1Nb0 Ap0 1Nb0 Ap0 1zb0 11B0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 11B0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0|30e2", + "CST6CDT|CST CDT CWT CPT|60 50 50 50|010102301010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-261s0 1nX0 11B0 1nX0 SgN0 8x30 iw0 QwN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|", + "EET|EET EEST|-20 -30|010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|hDB0 1a00 1fA0 1cM0 1cM0 1cM0 1fA0 1a00 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|", + "Europe/Dublin|LMT DMT IST GMT BST IST|p.l p.l -y.D 0 -10 -10|012343434343435353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353|-3BHby.D 1ra20 Rc0 1fzy.D 14M0 1fc0 1g00 1co0 1dc0 1co0 1oo0 1400 1dc0 19A0 1io0 1io0 WM0 1o00 14o0 1o00 17c0 1io0 17c0 1fA0 1a00 1lc0 17c0 1io0 17c0 1fA0 1a00 1io0 17c0 1io0 17c0 1fA0 1cM0 1io0 17c0 1fA0 1a00 1io0 17c0 1io0 17c0 1fA0 1a00 1io0 1qM0 Dc0 g600 14o0 1wo0 17c0 1io0 11A0 1o00 17c0 1fA0 1a00 1fA0 1cM0 1fA0 1a00 17c0 1fA0 1a00 1io0 17c0 1lc0 17c0 1fA0 1a00 1io0 17c0 1io0 17c0 1fA0 1a00 1a00 1qM0 WM0 1qM0 11A0 1o00 WM0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1tA0 IM0 90o0 U00 1tA0 U00 1tA0 U00 1tA0 U00 1tA0 WM0 1qM0 WM0 1qM0 WM0 1tA0 U00 1tA0 U00 1tA0 11z0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1o00 14o0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|12e5", + "EST|EST|50|0||", + "EST5EDT|EST EDT EWT EPT|50 40 40 40|010102301010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-261t0 1nX0 11B0 1nX0 SgN0 8x40 iv0 QwN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|", + "Etc/GMT-0|GMT|0|0||", + "Etc/GMT-1|+01|-10|0||", + "Etc/GMT-10|+10|-a0|0||", + "Etc/GMT-11|+11|-b0|0||", + "Etc/GMT-12|+12|-c0|0||", + "Etc/GMT-13|+13|-d0|0||", + "Etc/GMT-14|+14|-e0|0||", + "Etc/GMT-2|+02|-20|0||", + "Etc/GMT-3|+03|-30|0||", + "Etc/GMT-4|+04|-40|0||", + "Etc/GMT-5|+05|-50|0||", + "Etc/GMT-6|+06|-60|0||", + "Etc/GMT-7|+07|-70|0||", + "Etc/GMT-8|+08|-80|0||", + "Etc/GMT-9|+09|-90|0||", + "Etc/GMT+1|-01|10|0||", + "Etc/GMT+10|-10|a0|0||", + "Etc/GMT+11|-11|b0|0||", + "Etc/GMT+12|-12|c0|0||", + "Etc/GMT+2|-02|20|0||", + "Etc/GMT+3|-03|30|0||", + "Etc/GMT+4|-04|40|0||", + "Etc/GMT+5|-05|50|0||", + "Etc/GMT+6|-06|60|0||", + "Etc/GMT+7|-07|70|0||", + "Etc/GMT+8|-08|80|0||", + "Etc/GMT+9|-09|90|0||", + "Etc/UTC|UTC|0|0||", + "Europe/Brussels|LMT BMT WET CET CEST WEST|-h.u -h.u 0 -10 -20 -10|012343434325252525252525252525252525252525252525252525434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343|-3D8Mh.u u1Ah.u SO00 3zX0 11c0 1iO0 11A0 1o00 11A0 my0 Ic0 1qM0 Rc0 1EM0 UM0 1u00 10o0 1io0 1io0 17c0 1a00 1fA0 1cM0 1cM0 1io0 17c0 1fA0 1a00 1io0 1a30 1io0 17c0 1fA0 1a00 1io0 17c0 1cM0 1cM0 1a00 1io0 1cM0 1cM0 1a00 1fA0 1io0 17c0 1cM0 1cM0 1a00 1fA0 1io0 1qM0 Dc0 y00 5Wn0 WM0 1fA0 1cM0 16M0 1iM0 16M0 1C00 Uo0 1eeo0 1a00 1fA0 1cM0 1cM0 1cM0 1fA0 1a00 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|21e5", + "Europe/Andorra|LMT WET CET CEST|-6.4 0 -10 -20|0123232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232|-2M0M6.4 1Pnc6.4 1xIN0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|79e3", + "Europe/Astrakhan|LMT +03 +04 +05|-3c.c -30 -40 -50|012323232323232323212121212121212121212121212121212121212121212|-1Pcrc.c eUMc.c 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 2pB0 1cM0 1fA0 1cM0 3Co0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 8Hz0 3rd0|10e5", + "Europe/Athens|LMT AMT EET EEST CEST CET|-1y.Q -1y.Q -20 -30 -20 -10|0123234545232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232|-30SNy.Q OMM1 CNbx.Q mn0 kU10 9b0 3Es0 Xa0 1fb0 1dd0 k3X0 Nz0 SCp0 1vc0 SO0 1cM0 1a00 1ao0 1fc0 1a10 1fG0 1cg0 1dX0 1bX0 1cQ0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|35e5", + "Europe/London|LMT GMT BST BDST|1.f 0 -10 -20|01212121212121212121212121212121212121212121212121232323232321212321212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-4VgnW.J 2KHdW.J Rc0 1fA0 14M0 1fc0 1g00 1co0 1dc0 1co0 1oo0 1400 1dc0 19A0 1io0 1io0 WM0 1o00 14o0 1o00 17c0 1io0 17c0 1fA0 1a00 1lc0 17c0 1io0 17c0 1fA0 1a00 1io0 17c0 1io0 17c0 1fA0 1cM0 1io0 17c0 1fA0 1a00 1io0 17c0 1io0 17c0 1fA0 1a00 1io0 1qM0 Dc0 2Rz0 Dc0 1zc0 Oo0 1zc0 Rc0 1wo0 17c0 1iM0 FA0 xB0 1fA0 1a00 14o0 bb0 LA0 xB0 Rc0 1wo0 11A0 1o00 17c0 1fA0 1a00 1fA0 1cM0 1fA0 1a00 17c0 1fA0 1a00 1io0 17c0 1lc0 17c0 1fA0 1a00 1io0 17c0 1io0 17c0 1fA0 1a00 1a00 1qM0 WM0 1qM0 11A0 1o00 WM0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1tA0 IM0 90o0 U00 1tA0 U00 1tA0 U00 1tA0 U00 1tA0 WM0 1qM0 WM0 1qM0 WM0 1tA0 U00 1tA0 U00 1tA0 11z0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1o00 14o0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|10e6", + "Europe/Belgrade|LMT CET CEST|-1m -10 -20|012121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-3topm 2juLm 3IP0 WM0 1fA0 1cM0 1cM0 1rc0 Qo0 1vmo0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|12e5", + "Europe/Prague|LMT PMT CET CEST GMT|-V.I -V.I -10 -20 0|0123232323232323232423232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232|-4QbAV.I 1FDc0 XPaV.I 11d0 1iO0 11A0 1o00 11A0 Qrc0 6i00 WM0 1fA0 1cM0 1cM0 1cM0 1cM0 1qM0 11c0 mp0 xA0 mn0 17c0 1io0 17c0 1fc0 1ao0 1bNc0 1cM0 1fA0 1a00 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|13e5", + "Europe/Bucharest|LMT BMT EET EEST|-1I.o -1I.o -20 -30|01232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232|-3awpI.o 1AU00 20LI.o RA0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1Axc0 On0 1fA0 1a10 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cK0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cL0 1cN0 1cL0 1fB0 1nX0 11E0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|19e5", + "Europe/Budapest|LMT CET CEST|-1g.k -10 -20|01212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-3cK1g.k 124Lg.k 11d0 1iO0 11A0 1o00 11A0 1oo0 11c0 1lc0 17c0 O1V0 3Nf0 WM0 1fA0 1cM0 1cM0 1oJ0 1dd0 1020 1fX0 1cp0 1cM0 1cM0 1cM0 1fA0 1a00 bhy0 Rb0 1wr0 Rc0 1C00 LA0 1C00 LA0 SNW0 1a00 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cO0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|17e5", + "Europe/Zurich|LMT BMT CET CEST|-y.8 -t.K -10 -20|0123232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232|-4HyMy.8 1Dw04.m 1SfAt.K 11A0 1o00 11A0 1xG10 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|38e4", + "Europe/Chisinau|LMT CMT BMT EET EEST CEST CET MSK MSD|-1T.k -1T -1I.o -20 -30 -20 -10 -30 -40|0123434343434343434345656578787878787878787878434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343|-3D8NT.k 1wNA0.k wGMa.A 20LI.o RA0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 27A0 2en0 39g0 WM0 1fA0 1cM0 V90 1t7z0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 gL0 WO0 1cM0 1cM0 1cK0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1fB0 1nX0 11D0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|67e4", + "Europe/Gibraltar|LMT GMT BST BDST CET CEST|l.o 0 -10 -20 -10 -20|0121212121212121212121212121212121212121212121212123232323232121232121212121212121212145454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454|-3BHbC.A 1ra1C.A Rc0 1fA0 14M0 1fc0 1g00 1co0 1dc0 1co0 1oo0 1400 1dc0 19A0 1io0 1io0 WM0 1o00 14o0 1o00 17c0 1io0 17c0 1fA0 1a00 1lc0 17c0 1io0 17c0 1fA0 1a00 1io0 17c0 1io0 17c0 1fA0 1cM0 1io0 17c0 1fA0 1a00 1io0 17c0 1io0 17c0 1fA0 1a00 1io0 1qM0 Dc0 2Rz0 Dc0 1zc0 Oo0 1zc0 Rc0 1wo0 17c0 1iM0 FA0 xB0 1fA0 1a00 14o0 bb0 LA0 xB0 Rc0 1wo0 11A0 1o00 17c0 1fA0 1a00 1fA0 1cM0 1fA0 1a00 17c0 1fA0 1a00 1io0 17c0 1lc0 17c0 1fA0 10Jz0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|30e3", + "Europe/Helsinki|LMT HMT EET EEST|-1D.N -1D.N -20 -30|01232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232|-3H0ND.N 1Iu00 OULD.N 1dA0 1xGq0 1cM0 1cM0 1cM0 1cN0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|12e5", + "Europe/Kaliningrad|LMT CET CEST EET EEST MSK MSD +03|-1m -10 -20 -20 -30 -30 -40 -30|012121212121212343565656565656565654343434343434343434343434343434343434343434373|-36Rdm UbXm 11d0 1iO0 11A0 1o00 11A0 Qrc0 6i00 WM0 1fA0 1cM0 1cM0 1cM0 390 7A0 1en0 12N0 1pbb0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cN0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 8Hz0|44e4", + "Europe/Kiev|LMT KMT EET MSK CEST CET MSD EEST|-22.4 -22.4 -20 -30 -20 -10 -40 -30|01234545363636363636363636367272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272|-3D8O2.4 1LUM0 eUo2.4 rnz0 2Hg0 WM0 1fA0 da0 1v4m0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 Db0 3220 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o10 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|34e5", + "Europe/Kirov|LMT +03 +04 +05 MSD MSK MSK|-3i.M -30 -40 -50 -40 -30 -40|0123232323232323232454524545454545454545454545454545454545454565|-22WM0 qH90 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cN0 1cM0 1fA0 1cM0 2pz0 1cN0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 8Hz0|48e4", + "Europe/Lisbon|LMT WET WEST WEMT CET CEST|A.J 0 -10 -20 -10 -20|01212121212121212121212121212121212121212121232123212321232121212121212121212121212121212121212121214121212121212121212121212121212124545454212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-2le00 aPX0 Sp0 LX0 1vc0 Tc0 1uM0 SM0 1vc0 Tc0 1vc0 SM0 1vc0 6600 1co0 3E00 17c0 1fA0 1a00 1io0 1a00 1io0 17c0 3I00 17c0 1cM0 1cM0 3Fc0 1cM0 1a00 1fA0 1io0 17c0 1cM0 1cM0 1a00 1fA0 1io0 1qM0 Dc0 1tA0 1cM0 1dc0 1400 gL0 IM0 s10 U00 dX0 Rc0 pd0 Rc0 gL0 Oo0 pd0 Rc0 gL0 Oo0 pd0 14o0 1cM0 1cP0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 pvy0 1cM0 1cM0 1fA0 1cM0 1cM0 1cN0 1cL0 1cN0 1cM0 1cM0 1cM0 1cM0 1cN0 1cL0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|27e5", + "Europe/Madrid|LMT WET WEST WEMT CET CEST|e.I 0 -10 -20 -10 -20|0121212121212121212321454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454|-2M0M0 G5z0 19B0 1cL0 1dd0 b1z0 18p0 3HX0 17d0 1fz0 1a10 1io0 1a00 1in0 17d0 iIn0 Hd0 1cL0 bb0 1200 2s20 14n0 5aL0 Mp0 1vz0 17d0 1in0 17d0 1in0 17d0 1in0 17d0 6hX0 11B0 XHX0 1a10 1fz0 1a10 19X0 1cN0 1fz0 1a10 1fC0 1cM0 1cM0 1cM0 1fA0 1a00 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|62e5", + "Europe/Malta|LMT CET CEST|-W.4 -10 -20|01212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-35rcW.4 SXzW.4 Lz0 1cN0 1db0 1410 1on0 Wp0 1qL0 17d0 1cL0 M3B0 5M20 WM0 1fA0 1co0 17c0 1iM0 16m0 1de0 1lc0 14m0 1lc0 WO0 1qM0 GTW0 On0 1C10 LA0 1C00 LA0 1EM0 LA0 1C00 LA0 1zc0 Oo0 1C00 Oo0 1co0 1cM0 1lA0 Xc0 1qq0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1iN0 19z0 1fB0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|42e4", + "Europe/Minsk|LMT MMT EET MSK CEST CET MSD EEST +03|-1O.g -1O -20 -30 -20 -10 -40 -30 -30|012345454363636363636363636372727272727272727272727272727272727272728|-3D8NO.g 1LUM0.g eUnO qNX0 3gQ0 WM0 1fA0 1cM0 Al0 1tsn0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 3Fc0 1cN0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0|19e5", + "Europe/Paris|LMT PMT WET WEST CEST CET WEMT|-9.l -9.l 0 -10 -20 -10 -20|01232323232323232323232323232323232323232323232323234545463654545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545|-3bQ09.l MDA0 cNb9.l HA0 19A0 1iM0 11c0 1oo0 Wo0 1rc0 QM0 1EM0 UM0 1u00 10o0 1io0 1wo0 Rc0 1a00 1fA0 1cM0 1cM0 1io0 17c0 1fA0 1a00 1io0 1a00 1io0 17c0 1fA0 1a00 1io0 17c0 1cM0 1cM0 1a00 1io0 1cM0 1cM0 1a00 1fA0 1io0 17c0 1cM0 1cM0 1a00 1fA0 1io0 1qM0 Df0 Ik0 5M30 WM0 1fA0 1cM0 Vx0 hB0 1aq0 16M0 1ekn0 1cL0 1fC0 1a00 1fA0 1cM0 1cM0 1cM0 1fA0 1a00 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|11e6", + "Europe/Moscow|LMT MMT MMT MST MDST MSD MSK +05 EET EEST MSK|-2u.h -2u.h -2v.j -3v.j -4v.j -40 -30 -50 -20 -30 -40|01232434565756865656565656565656565698656565656565656565656565656565656565656a6|-3D8Ou.h 1sQM0 2pyW.W 1bA0 11X0 GN0 1Hb0 c4v.j ik0 3DA0 dz0 15A0 c10 2q10 iM10 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cN0 IM0 rX0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 8Hz0|16e6", + "Europe/Riga|LMT RMT LST EET MSK CEST CET MSD EEST|-1A.y -1A.y -2A.y -20 -30 -20 -10 -40 -30|0121213456565647474747474747474838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383|-3D8NA.y 1xde0 11A0 1iM0 ko0 gWm0 yDXA.y 2bX0 3fE0 WM0 1fA0 1cM0 1cM0 4m0 1sLy0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cN0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cN0 1o00 11A0 1o00 11A0 1qM0 3oo0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|64e4", + "Europe/Rome|LMT RMT CET CEST|-N.U -N.U -10 -20|012323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232|-4aU0N.U 15snN.U T000 Lz0 1cN0 1db0 1410 1on0 Wp0 1qL0 17d0 1cL0 M3B0 5M20 WM0 1fA0 1cM0 16M0 1iM0 16m0 1de0 1lc0 14m0 1lc0 WO0 1qM0 GTW0 On0 1C10 LA0 1C00 LA0 1EM0 LA0 1C00 LA0 1zc0 Oo0 1C00 Oo0 1C00 LA0 1zc0 Oo0 1C00 LA0 1C00 LA0 1zc0 Oo0 1C00 Oo0 1zc0 Oo0 1fC0 1a00 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|39e5", + "Europe/Samara|LMT +03 +04 +05|-3k.k -30 -40 -50|0123232323232323232121232323232323232323232323232323232323212|-22WM0 qH90 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 2pB0 1cM0 1fA0 2y10 14m0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 2sp0 WM0|12e5", + "Europe/Saratov|LMT +03 +04 +05|-34.i -30 -40 -50|012323232323232321212121212121212121212121212121212121212121212|-22WM0 qH90 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 2pB0 1cM0 1cM0 1cM0 1fA0 1cM0 3Co0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 8Hz0 5810|", + "Europe/Simferopol|LMT SMT EET MSK CEST CET MSD EEST MSK|-2g.o -2g -20 -30 -20 -10 -40 -30 -40|0123454543636363636363636363272727636363727272727272727272727272727272727283|-3D8Og.o 1LUM0.o eUog rEn0 2qs0 WM0 1fA0 1cM0 3V0 1u0L0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1Q00 4eN0 1cM0 1cM0 1cM0 1cM0 dV0 WO0 1cM0 1cM0 1fy0 1o30 11B0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11z0 1nW0|33e4", + "Europe/Sofia|LMT IMT EET CET CEST EEST|-1x.g -1U.U -20 -10 -20 -30|0123434325252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252|-3D8Nx.g AiLA.k 1UFeU.U WM0 1fA0 1cM0 1cM0 1cN0 1mKH0 1dd0 1fb0 1ap0 1fb0 1a20 1fy0 1a30 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cK0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1fB0 1nX0 11E0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|12e5", + "Europe/Tallinn|LMT TMT CET CEST EET MSK MSD EEST|-1D -1D -10 -20 -20 -30 -40 -30|0123214532323565656565656565657474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474747474|-3D8ND 1wI00 teD 11A0 1Ta0 4rXl KSLD 2FX0 2Jg0 WM0 1fA0 1cM0 18J0 1sTX0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cN0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o10 11A0 1qM0 5QM0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|41e4", + "Europe/Tirane|LMT CET CEST|-1j.k -10 -20|01212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-2glBj.k 14pcj.k 5LC0 WM0 4M0 1fCK0 10n0 1op0 11z0 1pd0 11z0 1qN0 WL0 1qp0 Xb0 1qp0 Xb0 1qp0 11z0 1lB0 11z0 1qN0 11z0 1iN0 16n0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|42e4", + "Europe/Ulyanovsk|LMT +03 +04 +05 +02|-3d.A -30 -40 -50 -20|01232323232323232321214121212121212121212121212121212121212121212|-22WM0 qH90 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 2pB0 1cM0 1fA0 2pB0 IM0 rX0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 8Hz0 3rd0|13e5", + "Europe/Vienna|LMT CET CEST|-15.l -10 -20|01212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121|-36Rd5.l UbX5.l 11d0 1iO0 11A0 1o00 11A0 3KM0 14o0 LA00 6i00 WM0 1fA0 1cM0 1cM0 1cM0 400 2qM0 1ao0 1co0 1cM0 1io0 17c0 1gHa0 19X0 1cP0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|18e5", + "Europe/Vilnius|LMT WMT KMT CET EET MSK CEST MSD EEST|-1F.g -1o -1z.A -10 -20 -30 -20 -40 -30|0123435636365757575757575757584848484848484848463648484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484|-3D8NF.g 1u5Ah.g 6ILM.o 1Ooz.A zz0 Mfd0 29W0 3is0 WM0 1fA0 1cM0 LV0 1tgL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cN0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11B0 1o00 11A0 1qM0 8io0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|54e4", + "Europe/Volgograd|LMT +03 +04 +05 MSD MSK MSK|-2V.E -30 -40 -50 -40 -30 -40|012323232323232324545452454545454545454545454545454545454545456525|-21IqV.E psLV.E 23CL0 1db0 1cN0 1db0 1cN0 1db0 1dd0 1cO0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cN0 1cM0 1cM0 1cM0 1fA0 1cM0 2pz0 1cN0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 8Hz0 9Jd0 5gn0|10e5", + "Europe/Warsaw|LMT WMT CET CEST EET EEST|-1o -1o -10 -20 -20 -30|0123232345423232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232|-3D8No 1qDA0 1LXo 11d0 1iO0 11A0 1o00 11A0 1on0 11A0 6zy0 HWP0 5IM0 WM0 1fA0 1cM0 1dz0 1mL0 1en0 15B0 1aq0 1nA0 11A0 1io0 17c0 1fA0 1a00 iDX0 LA0 1cM0 1cM0 1C00 Oo0 1cM0 1cM0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1C00 LA0 uso0 1a00 1fA0 1cM0 1cM0 1cM0 1fA0 1a00 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cN0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|17e5", + "HST|HST|a0|0||", + "Indian/Chagos|LMT +05 +06|-4N.E -50 -60|012|-2xosN.E 3AGLN.E|30e2", + "Indian/Maldives|LMT MMT +05|-4S -4S -50|012|-3D8QS 3eLA0|35e4", + "Indian/Mauritius|LMT +04 +05|-3O -40 -50|012121|-2xorO 34unO 14L0 12kr0 11z0|15e4", + "Pacific/Kwajalein|LMT +11 +10 +09 -12 +12|-b9.k -b0 -a0 -90 c0 -c0|0123145|-2M0X9.k 1rDA9.k akp0 6Up0 12ry0 Wan0|14e3", + "MET|MET MEST|-10 -20|01010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-2aFe0 11d0 1iO0 11A0 1o00 11A0 Qrc0 6i00 WM0 1fA0 1cM0 1cM0 1cM0 16M0 1gMM0 1a00 1fA0 1cM0 1cM0 1cM0 1fA0 1a00 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|", + "MST|MST|70|0||", + "MST7MDT|MST MDT MWT MPT|70 60 60 60|010102301010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-261r0 1nX0 11B0 1nX0 SgN0 8x20 ix0 QwN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|", + "Pacific/Chatham|LMT +1215 +1245 +1345|-cd.M -cf -cJ -dJ|0123232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323|-46jMd.M 37RbW.M 1adef IM0 1C00 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1qM0 14o0 1lc0 14o0 1lc0 14o0 1lc0 17c0 1io0 17c0 1io0 17c0 1io0 17c0 1lc0 14o0 1lc0 14o0 1lc0 17c0 1io0 17c0 1io0 17c0 1lc0 14o0 1lc0 14o0 1lc0 17c0 1io0 17c0 1io0 17c0 1io0 17c0 1io0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00|600", + "Pacific/Apia|LMT LMT -1130 -11 -10 +14 +13|-cx.4 bq.U bu b0 a0 -e0 -d0|012343456565656565656565656|-38Fox.4 J1A0 1yW03.4 2rRbu 1ff0 1a00 CI0 AQ0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0|37e3", + "Pacific/Bougainville|LMT PMMT +10 +09 +11|-am.g -9M.w -a0 -90 -b0|012324|-3D8Wm.g AvAx.I 1TCLM.w 7CN0 2MQp0|18e4", + "Pacific/Efate|LMT +11 +12|-bd.g -b0 -c0|012121212121212121212121|-2l9nd.g 2uNXd.g Dc0 n610 1cL0 1cN0 1cL0 1fB0 19X0 1fB0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1fB0 Lz0 1Nd0 An0|66e3", + "Pacific/Enderbury|-00 -12 -11 +13|0 c0 b0 -d0|0123|-1iIo0 1GsA0 B7X0|1", + "Pacific/Fakaofo|LMT -11 +13|bo.U b0 -d0|012|-2M0Az.4 4ufXz.4|483", + "Pacific/Fiji|LMT +12 +13|-bT.I -c0 -d0|012121212121212121212121212121|-2bUzT.I 3m8NT.I LA0 1EM0 IM0 nJc0 LA0 1o00 Rc0 1wo0 Ao0 1Nc0 Ao0 1Q00 xz0 1SN0 uM0 1SM0 uM0 1VA0 s00 1VA0 s00 1VA0 s00 20o0 pc0 2hc0 bc0|88e4", + "Pacific/Tarawa|LMT +12|-bw.4 -c0|01|-2M0Xw.4|29e3", + "Pacific/Galapagos|LMT -05 -06|5W.o 50 60|01212|-1yVS1.A 2dTz1.A gNd0 rz0|25e3", + "Pacific/Gambier|LMT -09|8X.M 90|01|-2jof0.c|125", + "Pacific/Guadalcanal|LMT +11|-aD.M -b0|01|-2joyD.M|11e4", + "Pacific/Guam|LMT LMT GST +09 GDT ChST|el -9D -a0 -90 -b0 -a0|0123242424242424242425|-54m9D 2glc0 1DFbD 6pB0 AhB0 3QL0 g2p0 3p91 WOX rX0 1zd0 Rb0 1wp0 Rb0 5xd0 rX0 5sN0 zb1 1C0X On0 ULb0|17e4", + "Pacific/Honolulu|LMT HST HDT HWT HPT HST|av.q au 9u 9u 9u a0|01213415|-3061s.y 1uMdW.y 8x0 lef0 8wWu iAu 46p0|37e4", + "Pacific/Kiritimati|LMT -1040 -10 +14|at.k aE a0 -e0|0123|-2M0Bu.E 3bIMa.E B7Xk|51e2", + "Pacific/Kosrae|LMT LMT +11 +09 +10 +12|d8.4 -aP.U -b0 -90 -a0 -c0|0123243252|-54maP.U 2glc0 xsnP.U axC0 HBy0 akp0 axd0 WOK0 1bdz0|66e2", + "Pacific/Marquesas|LMT -0930|9i 9u|01|-2joeG|86e2", + "Pacific/Pago_Pago|LMT LMT SST|-cB.c bm.M b0|012|-38FoB.c J1A0|37e2", + "Pacific/Nauru|LMT +1130 +09 +12|-b7.E -bu -90 -c0|01213|-1Xdn7.E QCnB.E 7mqu 1lnbu|10e3", + "Pacific/Niue|LMT -1120 -11|bj.E bk b0|012|-FScE.k suo0.k|12e2", + "Pacific/Norfolk|LMT +1112 +1130 +1230 +11 +12|-bb.Q -bc -bu -cu -b0 -c0|0123245454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545|-2M0Xb.Q 21ILX.Q W01G Oo0 1COo0 9Jcu 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0|25e4", + "Pacific/Noumea|LMT +11 +12|-b5.M -b0 -c0|01212121|-2l9n5.M 2EqM5.M xX0 1PB0 yn0 HeP0 Ao0|98e3", + "Pacific/Palau|LMT LMT +09|f2.4 -8V.U -90|012|-54m8V.U 2glc0|21e3", + "Pacific/Pitcairn|LMT -0830 -08|8E.k 8u 80|012|-2M0Dj.E 3UVXN.E|56", + "Pacific/Rarotonga|LMT LMT -1030 -0930 -10|-dk.U aD.4 au 9u a0|01234343434343434343434343434|-2Otpk.U 28zc0 13tbO.U IL0 1zcu Onu 1zcu Onu 1zcu Rbu 1zcu Onu 1zcu Onu 1zcu Onu 1zcu Onu 1zcu Onu 1zcu Rbu 1zcu Onu 1zcu Onu 1zcu Onu|13e3", + "Pacific/Tahiti|LMT -10|9W.g a0|01|-2joe1.I|18e4", + "Pacific/Tongatapu|LMT +1220 +13 +14|-cj.c -ck -d0 -e0|01232323232|-XbMj.c BgLX.c 1yndk 15A0 1wo0 xz0 1Q10 xz0 zWN0 s00|75e3", + "PST8PDT|PST PDT PWT PPT|80 70 70 70|010102301010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-261q0 1nX0 11B0 1nX0 SgN0 8x10 iy0 QwN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|", + "WET|WET WEST|0 -10|010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|hDB0 1a00 1fA0 1cM0 1cM0 1cM0 1fA0 1a00 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|" + ], + "links": [ + "Africa/Abidjan|Africa/Accra", + "Africa/Abidjan|Africa/Bamako", + "Africa/Abidjan|Africa/Banjul", + "Africa/Abidjan|Africa/Conakry", + "Africa/Abidjan|Africa/Dakar", + "Africa/Abidjan|Africa/Freetown", + "Africa/Abidjan|Africa/Lome", + "Africa/Abidjan|Africa/Nouakchott", + "Africa/Abidjan|Africa/Ouagadougou", + "Africa/Abidjan|Africa/Timbuktu", + "Africa/Abidjan|Atlantic/Reykjavik", + "Africa/Abidjan|Atlantic/St_Helena", + "Africa/Abidjan|Iceland", + "Africa/Cairo|Egypt", + "Africa/Johannesburg|Africa/Maseru", + "Africa/Johannesburg|Africa/Mbabane", + "Africa/Lagos|Africa/Bangui", + "Africa/Lagos|Africa/Brazzaville", + "Africa/Lagos|Africa/Douala", + "Africa/Lagos|Africa/Kinshasa", + "Africa/Lagos|Africa/Libreville", + "Africa/Lagos|Africa/Luanda", + "Africa/Lagos|Africa/Malabo", + "Africa/Lagos|Africa/Niamey", + "Africa/Lagos|Africa/Porto-Novo", + "Africa/Maputo|Africa/Blantyre", + "Africa/Maputo|Africa/Bujumbura", + "Africa/Maputo|Africa/Gaborone", + "Africa/Maputo|Africa/Harare", + "Africa/Maputo|Africa/Kigali", + "Africa/Maputo|Africa/Lubumbashi", + "Africa/Maputo|Africa/Lusaka", + "Africa/Nairobi|Africa/Addis_Ababa", + "Africa/Nairobi|Africa/Asmara", + "Africa/Nairobi|Africa/Asmera", + "Africa/Nairobi|Africa/Dar_es_Salaam", + "Africa/Nairobi|Africa/Djibouti", + "Africa/Nairobi|Africa/Kampala", + "Africa/Nairobi|Africa/Mogadishu", + "Africa/Nairobi|Indian/Antananarivo", + "Africa/Nairobi|Indian/Comoro", + "Africa/Nairobi|Indian/Mayotte", + "Africa/Tripoli|Libya", + "America/Adak|America/Atka", + "America/Adak|US/Aleutian", + "America/Anchorage|US/Alaska", + "America/Argentina/Buenos_Aires|America/Buenos_Aires", + "America/Argentina/Catamarca|America/Argentina/ComodRivadavia", + "America/Argentina/Catamarca|America/Catamarca", + "America/Argentina/Cordoba|America/Cordoba", + "America/Argentina/Cordoba|America/Rosario", + "America/Argentina/Jujuy|America/Jujuy", + "America/Argentina/Mendoza|America/Mendoza", + "America/Chicago|US/Central", + "America/Denver|America/Shiprock", + "America/Denver|Navajo", + "America/Denver|US/Mountain", + "America/Detroit|US/Michigan", + "America/Edmonton|America/Yellowknife", + "America/Edmonton|Canada/Mountain", + "America/Fort_Wayne|America/Indiana/Indianapolis", + "America/Fort_Wayne|America/Indianapolis", + "America/Fort_Wayne|US/East-Indiana", + "America/Godthab|America/Nuuk", + "America/Halifax|Canada/Atlantic", + "America/Havana|Cuba", + "America/Indiana/Knox|America/Knox_IN", + "America/Indiana/Knox|US/Indiana-Starke", + "America/Iqaluit|America/Pangnirtung", + "America/Jamaica|Jamaica", + "America/Kentucky/Louisville|America/Louisville", + "America/Los_Angeles|US/Pacific", + "America/Manaus|Brazil/West", + "America/Mazatlan|Mexico/BajaSur", + "America/Mexico_City|Mexico/General", + "America/New_York|US/Eastern", + "America/Noronha|Brazil/DeNoronha", + "America/Panama|America/Atikokan", + "America/Panama|America/Cayman", + "America/Panama|America/Coral_Harbour", + "America/Phoenix|America/Creston", + "America/Phoenix|US/Arizona", + "America/Puerto_Rico|America/Anguilla", + "America/Puerto_Rico|America/Antigua", + "America/Puerto_Rico|America/Aruba", + "America/Puerto_Rico|America/Blanc-Sablon", + "America/Puerto_Rico|America/Curacao", + "America/Puerto_Rico|America/Dominica", + "America/Puerto_Rico|America/Grenada", + "America/Puerto_Rico|America/Guadeloupe", + "America/Puerto_Rico|America/Kralendijk", + "America/Puerto_Rico|America/Lower_Princes", + "America/Puerto_Rico|America/Marigot", + "America/Puerto_Rico|America/Montserrat", + "America/Puerto_Rico|America/Port_of_Spain", + "America/Puerto_Rico|America/St_Barthelemy", + "America/Puerto_Rico|America/St_Kitts", + "America/Puerto_Rico|America/St_Lucia", + "America/Puerto_Rico|America/St_Thomas", + "America/Puerto_Rico|America/St_Vincent", + "America/Puerto_Rico|America/Tortola", + "America/Puerto_Rico|America/Virgin", + "America/Regina|Canada/Saskatchewan", + "America/Rio_Branco|America/Porto_Acre", + "America/Rio_Branco|Brazil/Acre", + "America/Santiago|Chile/Continental", + "America/Sao_Paulo|Brazil/East", + "America/St_Johns|Canada/Newfoundland", + "America/Tijuana|America/Ensenada", + "America/Tijuana|America/Santa_Isabel", + "America/Tijuana|Mexico/BajaNorte", + "America/Toronto|America/Montreal", + "America/Toronto|America/Nassau", + "America/Toronto|America/Nipigon", + "America/Toronto|America/Thunder_Bay", + "America/Toronto|Canada/Eastern", + "America/Vancouver|Canada/Pacific", + "America/Whitehorse|Canada/Yukon", + "America/Winnipeg|America/Rainy_River", + "America/Winnipeg|Canada/Central", + "Asia/Ashgabat|Asia/Ashkhabad", + "Asia/Bangkok|Asia/Phnom_Penh", + "Asia/Bangkok|Asia/Vientiane", + "Asia/Bangkok|Indian/Christmas", + "Asia/Brunei|Asia/Kuching", + "Asia/Dhaka|Asia/Dacca", + "Asia/Dubai|Asia/Muscat", + "Asia/Dubai|Indian/Mahe", + "Asia/Dubai|Indian/Reunion", + "Asia/Ho_Chi_Minh|Asia/Saigon", + "Asia/Hong_Kong|Hongkong", + "Asia/Jerusalem|Asia/Tel_Aviv", + "Asia/Jerusalem|Israel", + "Asia/Kathmandu|Asia/Katmandu", + "Asia/Kolkata|Asia/Calcutta", + "Asia/Kuala_Lumpur|Asia/Singapore", + "Asia/Kuala_Lumpur|Singapore", + "Asia/Macau|Asia/Macao", + "Asia/Makassar|Asia/Ujung_Pandang", + "Asia/Nicosia|Europe/Nicosia", + "Asia/Qatar|Asia/Bahrain", + "Asia/Rangoon|Asia/Yangon", + "Asia/Rangoon|Indian/Cocos", + "Asia/Riyadh|Antarctica/Syowa", + "Asia/Riyadh|Asia/Aden", + "Asia/Riyadh|Asia/Kuwait", + "Asia/Seoul|ROK", + "Asia/Shanghai|Asia/Chongqing", + "Asia/Shanghai|Asia/Chungking", + "Asia/Shanghai|Asia/Harbin", + "Asia/Shanghai|PRC", + "Asia/Taipei|ROC", + "Asia/Tehran|Iran", + "Asia/Thimphu|Asia/Thimbu", + "Asia/Tokyo|Japan", + "Asia/Ulaanbaatar|Asia/Ulan_Bator", + "Asia/Urumqi|Antarctica/Vostok", + "Asia/Urumqi|Asia/Kashgar", + "Atlantic/Faroe|Atlantic/Faeroe", + "Australia/Adelaide|Australia/South", + "Australia/Brisbane|Australia/Queensland", + "Australia/Broken_Hill|Australia/Yancowinna", + "Australia/Darwin|Australia/North", + "Australia/Hobart|Australia/Currie", + "Australia/Hobart|Australia/Tasmania", + "Australia/Lord_Howe|Australia/LHI", + "Australia/Melbourne|Australia/Victoria", + "Australia/Perth|Australia/West", + "Australia/Sydney|Australia/ACT", + "Australia/Sydney|Australia/Canberra", + "Australia/Sydney|Australia/NSW", + "Etc/GMT-0|Etc/GMT", + "Etc/GMT-0|Etc/GMT+0", + "Etc/GMT-0|Etc/GMT0", + "Etc/GMT-0|Etc/Greenwich", + "Etc/GMT-0|GMT", + "Etc/GMT-0|GMT+0", + "Etc/GMT-0|GMT-0", + "Etc/GMT-0|GMT0", + "Etc/GMT-0|Greenwich", + "Etc/UTC|Etc/UCT", + "Etc/UTC|Etc/Universal", + "Etc/UTC|Etc/Zulu", + "Etc/UTC|UCT", + "Etc/UTC|UTC", + "Etc/UTC|Universal", + "Etc/UTC|Zulu", + "Europe/Belgrade|Europe/Ljubljana", + "Europe/Belgrade|Europe/Podgorica", + "Europe/Belgrade|Europe/Sarajevo", + "Europe/Belgrade|Europe/Skopje", + "Europe/Belgrade|Europe/Zagreb", + "Europe/Berlin|Arctic/Longyearbyen", + "Europe/Berlin|Atlantic/Jan_Mayen", + "Europe/Berlin|Europe/Copenhagen", + "Europe/Berlin|Europe/Oslo", + "Europe/Berlin|Europe/Stockholm", + "Europe/Brussels|Europe/Amsterdam", + "Europe/Brussels|Europe/Luxembourg", + "Europe/Chisinau|Europe/Tiraspol", + "Europe/Dublin|Eire", + "Europe/Helsinki|Europe/Mariehamn", + "Europe/Istanbul|Asia/Istanbul", + "Europe/Istanbul|Turkey", + "Europe/Kiev|Europe/Kyiv", + "Europe/Kiev|Europe/Uzhgorod", + "Europe/Kiev|Europe/Zaporozhye", + "Europe/Lisbon|Portugal", + "Europe/London|Europe/Belfast", + "Europe/London|Europe/Guernsey", + "Europe/London|Europe/Isle_of_Man", + "Europe/London|Europe/Jersey", + "Europe/London|GB", + "Europe/London|GB-Eire", + "Europe/Moscow|W-SU", + "Europe/Paris|Europe/Monaco", + "Europe/Prague|Europe/Bratislava", + "Europe/Rome|Europe/San_Marino", + "Europe/Rome|Europe/Vatican", + "Europe/Warsaw|Poland", + "Europe/Zurich|Europe/Busingen", + "Europe/Zurich|Europe/Vaduz", + "Indian/Maldives|Indian/Kerguelen", + "Pacific/Auckland|Antarctica/McMurdo", + "Pacific/Auckland|Antarctica/South_Pole", + "Pacific/Auckland|NZ", + "Pacific/Chatham|NZ-CHAT", + "Pacific/Easter|Chile/EasterIsland", + "Pacific/Enderbury|Pacific/Kanton", + "Pacific/Guadalcanal|Pacific/Pohnpei", + "Pacific/Guadalcanal|Pacific/Ponape", + "Pacific/Guam|Pacific/Saipan", + "Pacific/Honolulu|Pacific/Johnston", + "Pacific/Honolulu|US/Hawaii", + "Pacific/Kwajalein|Kwajalein", + "Pacific/Pago_Pago|Pacific/Midway", + "Pacific/Pago_Pago|Pacific/Samoa", + "Pacific/Pago_Pago|US/Samoa", + "Pacific/Port_Moresby|Antarctica/DumontDUrville", + "Pacific/Port_Moresby|Pacific/Chuuk", + "Pacific/Port_Moresby|Pacific/Truk", + "Pacific/Port_Moresby|Pacific/Yap", + "Pacific/Tarawa|Pacific/Funafuti", + "Pacific/Tarawa|Pacific/Majuro", + "Pacific/Tarawa|Pacific/Wake", + "Pacific/Tarawa|Pacific/Wallis" + ], + "countries": [ + "AD|Europe/Andorra", + "AE|Asia/Dubai", + "AF|Asia/Kabul", + "AG|America/Puerto_Rico America/Antigua", + "AI|America/Puerto_Rico America/Anguilla", + "AL|Europe/Tirane", + "AM|Asia/Yerevan", + "AO|Africa/Lagos Africa/Luanda", + "AQ|Antarctica/Casey Antarctica/Davis Antarctica/Mawson Antarctica/Palmer Antarctica/Rothera Antarctica/Troll Asia/Urumqi Pacific/Auckland Pacific/Port_Moresby Asia/Riyadh Antarctica/McMurdo Antarctica/DumontDUrville Antarctica/Syowa Antarctica/Vostok", + "AR|America/Argentina/Buenos_Aires America/Argentina/Cordoba America/Argentina/Salta America/Argentina/Jujuy America/Argentina/Tucuman America/Argentina/Catamarca America/Argentina/La_Rioja America/Argentina/San_Juan America/Argentina/Mendoza America/Argentina/San_Luis America/Argentina/Rio_Gallegos America/Argentina/Ushuaia", + "AS|Pacific/Pago_Pago", + "AT|Europe/Vienna", + "AU|Australia/Lord_Howe Antarctica/Macquarie Australia/Hobart Australia/Melbourne Australia/Sydney Australia/Broken_Hill Australia/Brisbane Australia/Lindeman Australia/Adelaide Australia/Darwin Australia/Perth Australia/Eucla", + "AW|America/Puerto_Rico America/Aruba", + "AX|Europe/Helsinki Europe/Mariehamn", + "AZ|Asia/Baku", + "BA|Europe/Belgrade Europe/Sarajevo", + "BB|America/Barbados", + "BD|Asia/Dhaka", + "BE|Europe/Brussels", + "BF|Africa/Abidjan Africa/Ouagadougou", + "BG|Europe/Sofia", + "BH|Asia/Qatar Asia/Bahrain", + "BI|Africa/Maputo Africa/Bujumbura", + "BJ|Africa/Lagos Africa/Porto-Novo", + "BL|America/Puerto_Rico America/St_Barthelemy", + "BM|Atlantic/Bermuda", + "BN|Asia/Kuching Asia/Brunei", + "BO|America/La_Paz", + "BQ|America/Puerto_Rico America/Kralendijk", + "BR|America/Noronha America/Belem America/Fortaleza America/Recife America/Araguaina America/Maceio America/Bahia America/Sao_Paulo America/Campo_Grande America/Cuiaba America/Santarem America/Porto_Velho America/Boa_Vista America/Manaus America/Eirunepe America/Rio_Branco", + "BS|America/Toronto America/Nassau", + "BT|Asia/Thimphu", + "BW|Africa/Maputo Africa/Gaborone", + "BY|Europe/Minsk", + "BZ|America/Belize", + "CA|America/St_Johns America/Halifax America/Glace_Bay America/Moncton America/Goose_Bay America/Toronto America/Iqaluit America/Winnipeg America/Resolute America/Rankin_Inlet America/Regina America/Swift_Current America/Edmonton America/Cambridge_Bay America/Inuvik America/Dawson_Creek America/Fort_Nelson America/Whitehorse America/Dawson America/Vancouver America/Panama America/Puerto_Rico America/Phoenix America/Blanc-Sablon America/Atikokan America/Creston", + "CC|Asia/Yangon Indian/Cocos", + "CD|Africa/Maputo Africa/Lagos Africa/Kinshasa Africa/Lubumbashi", + "CF|Africa/Lagos Africa/Bangui", + "CG|Africa/Lagos Africa/Brazzaville", + "CH|Europe/Zurich", + "CI|Africa/Abidjan", + "CK|Pacific/Rarotonga", + "CL|America/Santiago America/Punta_Arenas Pacific/Easter", + "CM|Africa/Lagos Africa/Douala", + "CN|Asia/Shanghai Asia/Urumqi", + "CO|America/Bogota", + "CR|America/Costa_Rica", + "CU|America/Havana", + "CV|Atlantic/Cape_Verde", + "CW|America/Puerto_Rico America/Curacao", + "CX|Asia/Bangkok Indian/Christmas", + "CY|Asia/Nicosia Asia/Famagusta", + "CZ|Europe/Prague", + "DE|Europe/Zurich Europe/Berlin Europe/Busingen", + "DJ|Africa/Nairobi Africa/Djibouti", + "DK|Europe/Berlin Europe/Copenhagen", + "DM|America/Puerto_Rico America/Dominica", + "DO|America/Santo_Domingo", + "DZ|Africa/Algiers", + "EC|America/Guayaquil Pacific/Galapagos", + "EE|Europe/Tallinn", + "EG|Africa/Cairo", + "EH|Africa/El_Aaiun", + "ER|Africa/Nairobi Africa/Asmara", + "ES|Europe/Madrid Africa/Ceuta Atlantic/Canary", + "ET|Africa/Nairobi Africa/Addis_Ababa", + "FI|Europe/Helsinki", + "FJ|Pacific/Fiji", + "FK|Atlantic/Stanley", + "FM|Pacific/Kosrae Pacific/Port_Moresby Pacific/Guadalcanal Pacific/Chuuk Pacific/Pohnpei", + "FO|Atlantic/Faroe", + "FR|Europe/Paris", + "GA|Africa/Lagos Africa/Libreville", + "GB|Europe/London", + "GD|America/Puerto_Rico America/Grenada", + "GE|Asia/Tbilisi", + "GF|America/Cayenne", + "GG|Europe/London Europe/Guernsey", + "GH|Africa/Abidjan Africa/Accra", + "GI|Europe/Gibraltar", + "GL|America/Nuuk America/Danmarkshavn America/Scoresbysund America/Thule", + "GM|Africa/Abidjan Africa/Banjul", + "GN|Africa/Abidjan Africa/Conakry", + "GP|America/Puerto_Rico America/Guadeloupe", + "GQ|Africa/Lagos Africa/Malabo", + "GR|Europe/Athens", + "GS|Atlantic/South_Georgia", + "GT|America/Guatemala", + "GU|Pacific/Guam", + "GW|Africa/Bissau", + "GY|America/Guyana", + "HK|Asia/Hong_Kong", + "HN|America/Tegucigalpa", + "HR|Europe/Belgrade Europe/Zagreb", + "HT|America/Port-au-Prince", + "HU|Europe/Budapest", + "ID|Asia/Jakarta Asia/Pontianak Asia/Makassar Asia/Jayapura", + "IE|Europe/Dublin", + "IL|Asia/Jerusalem", + "IM|Europe/London Europe/Isle_of_Man", + "IN|Asia/Kolkata", + "IO|Indian/Chagos", + "IQ|Asia/Baghdad", + "IR|Asia/Tehran", + "IS|Africa/Abidjan Atlantic/Reykjavik", + "IT|Europe/Rome", + "JE|Europe/London Europe/Jersey", + "JM|America/Jamaica", + "JO|Asia/Amman", + "JP|Asia/Tokyo", + "KE|Africa/Nairobi", + "KG|Asia/Bishkek", + "KH|Asia/Bangkok Asia/Phnom_Penh", + "KI|Pacific/Tarawa Pacific/Kanton Pacific/Kiritimati", + "KM|Africa/Nairobi Indian/Comoro", + "KN|America/Puerto_Rico America/St_Kitts", + "KP|Asia/Pyongyang", + "KR|Asia/Seoul", + "KW|Asia/Riyadh Asia/Kuwait", + "KY|America/Panama America/Cayman", + "KZ|Asia/Almaty Asia/Qyzylorda Asia/Qostanay Asia/Aqtobe Asia/Aqtau Asia/Atyrau Asia/Oral", + "LA|Asia/Bangkok Asia/Vientiane", + "LB|Asia/Beirut", + "LC|America/Puerto_Rico America/St_Lucia", + "LI|Europe/Zurich Europe/Vaduz", + "LK|Asia/Colombo", + "LR|Africa/Monrovia", + "LS|Africa/Johannesburg Africa/Maseru", + "LT|Europe/Vilnius", + "LU|Europe/Brussels Europe/Luxembourg", + "LV|Europe/Riga", + "LY|Africa/Tripoli", + "MA|Africa/Casablanca", + "MC|Europe/Paris Europe/Monaco", + "MD|Europe/Chisinau", + "ME|Europe/Belgrade Europe/Podgorica", + "MF|America/Puerto_Rico America/Marigot", + "MG|Africa/Nairobi Indian/Antananarivo", + "MH|Pacific/Tarawa Pacific/Kwajalein Pacific/Majuro", + "MK|Europe/Belgrade Europe/Skopje", + "ML|Africa/Abidjan Africa/Bamako", + "MM|Asia/Yangon", + "MN|Asia/Ulaanbaatar Asia/Hovd Asia/Choibalsan", + "MO|Asia/Macau", + "MP|Pacific/Guam Pacific/Saipan", + "MQ|America/Martinique", + "MR|Africa/Abidjan Africa/Nouakchott", + "MS|America/Puerto_Rico America/Montserrat", + "MT|Europe/Malta", + "MU|Indian/Mauritius", + "MV|Indian/Maldives", + "MW|Africa/Maputo Africa/Blantyre", + "MX|America/Mexico_City America/Cancun America/Merida America/Monterrey America/Matamoros America/Chihuahua America/Ciudad_Juarez America/Ojinaga America/Mazatlan America/Bahia_Banderas America/Hermosillo America/Tijuana", + "MY|Asia/Kuching Asia/Singapore Asia/Kuala_Lumpur", + "MZ|Africa/Maputo", + "NA|Africa/Windhoek", + "NC|Pacific/Noumea", + "NE|Africa/Lagos Africa/Niamey", + "NF|Pacific/Norfolk", + "NG|Africa/Lagos", + "NI|America/Managua", + "NL|Europe/Brussels Europe/Amsterdam", + "NO|Europe/Berlin Europe/Oslo", + "NP|Asia/Kathmandu", + "NR|Pacific/Nauru", + "NU|Pacific/Niue", + "NZ|Pacific/Auckland Pacific/Chatham", + "OM|Asia/Dubai Asia/Muscat", + "PA|America/Panama", + "PE|America/Lima", + "PF|Pacific/Tahiti Pacific/Marquesas Pacific/Gambier", + "PG|Pacific/Port_Moresby Pacific/Bougainville", + "PH|Asia/Manila", + "PK|Asia/Karachi", + "PL|Europe/Warsaw", + "PM|America/Miquelon", + "PN|Pacific/Pitcairn", + "PR|America/Puerto_Rico", + "PS|Asia/Gaza Asia/Hebron", + "PT|Europe/Lisbon Atlantic/Madeira Atlantic/Azores", + "PW|Pacific/Palau", + "PY|America/Asuncion", + "QA|Asia/Qatar", + "RE|Asia/Dubai Indian/Reunion", + "RO|Europe/Bucharest", + "RS|Europe/Belgrade", + "RU|Europe/Kaliningrad Europe/Moscow Europe/Simferopol Europe/Kirov Europe/Volgograd Europe/Astrakhan Europe/Saratov Europe/Ulyanovsk Europe/Samara Asia/Yekaterinburg Asia/Omsk Asia/Novosibirsk Asia/Barnaul Asia/Tomsk Asia/Novokuznetsk Asia/Krasnoyarsk Asia/Irkutsk Asia/Chita Asia/Yakutsk Asia/Khandyga Asia/Vladivostok Asia/Ust-Nera Asia/Magadan Asia/Sakhalin Asia/Srednekolymsk Asia/Kamchatka Asia/Anadyr", + "RW|Africa/Maputo Africa/Kigali", + "SA|Asia/Riyadh", + "SB|Pacific/Guadalcanal", + "SC|Asia/Dubai Indian/Mahe", + "SD|Africa/Khartoum", + "SE|Europe/Berlin Europe/Stockholm", + "SG|Asia/Singapore", + "SH|Africa/Abidjan Atlantic/St_Helena", + "SI|Europe/Belgrade Europe/Ljubljana", + "SJ|Europe/Berlin Arctic/Longyearbyen", + "SK|Europe/Prague Europe/Bratislava", + "SL|Africa/Abidjan Africa/Freetown", + "SM|Europe/Rome Europe/San_Marino", + "SN|Africa/Abidjan Africa/Dakar", + "SO|Africa/Nairobi Africa/Mogadishu", + "SR|America/Paramaribo", + "SS|Africa/Juba", + "ST|Africa/Sao_Tome", + "SV|America/El_Salvador", + "SX|America/Puerto_Rico America/Lower_Princes", + "SY|Asia/Damascus", + "SZ|Africa/Johannesburg Africa/Mbabane", + "TC|America/Grand_Turk", + "TD|Africa/Ndjamena", + "TF|Asia/Dubai Indian/Maldives Indian/Kerguelen", + "TG|Africa/Abidjan Africa/Lome", + "TH|Asia/Bangkok", + "TJ|Asia/Dushanbe", + "TK|Pacific/Fakaofo", + "TL|Asia/Dili", + "TM|Asia/Ashgabat", + "TN|Africa/Tunis", + "TO|Pacific/Tongatapu", + "TR|Europe/Istanbul", + "TT|America/Puerto_Rico America/Port_of_Spain", + "TV|Pacific/Tarawa Pacific/Funafuti", + "TW|Asia/Taipei", + "TZ|Africa/Nairobi Africa/Dar_es_Salaam", + "UA|Europe/Simferopol Europe/Kyiv", + "UG|Africa/Nairobi Africa/Kampala", + "UM|Pacific/Pago_Pago Pacific/Tarawa Pacific/Midway Pacific/Wake", + "US|America/New_York America/Detroit America/Kentucky/Louisville America/Kentucky/Monticello America/Indiana/Indianapolis America/Indiana/Vincennes America/Indiana/Winamac America/Indiana/Marengo America/Indiana/Petersburg America/Indiana/Vevay America/Chicago America/Indiana/Tell_City America/Indiana/Knox America/Menominee America/North_Dakota/Center America/North_Dakota/New_Salem America/North_Dakota/Beulah America/Denver America/Boise America/Phoenix America/Los_Angeles America/Anchorage America/Juneau America/Sitka America/Metlakatla America/Yakutat America/Nome America/Adak Pacific/Honolulu", + "UY|America/Montevideo", + "UZ|Asia/Samarkand Asia/Tashkent", + "VA|Europe/Rome Europe/Vatican", + "VC|America/Puerto_Rico America/St_Vincent", + "VE|America/Caracas", + "VG|America/Puerto_Rico America/Tortola", + "VI|America/Puerto_Rico America/St_Thomas", + "VN|Asia/Bangkok Asia/Ho_Chi_Minh", + "VU|Pacific/Efate", + "WF|Pacific/Tarawa Pacific/Wallis", + "WS|Pacific/Apia", + "YE|Asia/Riyadh Asia/Aden", + "YT|Africa/Nairobi Indian/Mayotte", + "ZA|Africa/Johannesburg", + "ZM|Africa/Maputo Africa/Lusaka", + "ZW|Africa/Maputo Africa/Harare" + ] +} diff --git a/resources/timezones.json b/resources/timezones.json index 72e8bd9a48..fd19fe4df9 100644 --- a/resources/timezones.json +++ b/resources/timezones.json @@ -1,1506 +1,1506 @@ [ - { - "name": "Africa/Abidjan", - "label": "Africa/Abidjan" - }, - { - "name": "Africa/Accra", - "label": "Africa/Accra" - }, - { - "name": "Africa/Nairobi", - "label": "Africa/Nairobi" - }, - { - "name": "Africa/Algiers", - "label": "Africa/Algiers" - }, - { - "name": "Africa/Lagos", - "label": "Africa/Lagos" - }, - { - "name": "Africa/Bissau", - "label": "Africa/Bissau" - }, - { - "name": "Africa/Maputo", - "label": "Africa/Maputo" - }, - { - "name": "Africa/Cairo", - "label": "Africa/Cairo" - }, - { - "name": "Africa/Casablanca", - "label": "Africa/Casablanca" - }, - { - "name": "Africa/Ceuta", - "label": "Africa/Ceuta" - }, - { - "name": "Africa/El_Aaiun", - "label": "Africa/El Aaiun" - }, - { - "name": "Africa/Johannesburg", - "label": "Africa/Johannesburg" - }, - { - "name": "Africa/Juba", - "label": "Africa/Juba" - }, - { - "name": "Africa/Khartoum", - "label": "Africa/Khartoum" - }, - { - "name": "Africa/Monrovia", - "label": "Africa/Monrovia" - }, - { - "name": "Africa/Ndjamena", - "label": "Africa/Ndjamena" - }, - { - "name": "Africa/Sao_Tome", - "label": "Africa/Sao Tome" - }, - { - "name": "Africa/Tripoli", - "label": "Africa/Tripoli" - }, - { - "name": "Africa/Tunis", - "label": "Africa/Tunis" - }, - { - "name": "Africa/Windhoek", - "label": "Africa/Windhoek" - }, - { - "name": "America/Adak", - "label": "America/Adak" - }, - { - "name": "America/Anchorage", - "label": "America/Anchorage" - }, - { - "name": "America/Port_of_Spain", - "label": "America/Port of Spain" - }, - { - "name": "America/Araguaina", - "label": "America/Araguaina" - }, - { - "name": "America/Argentina/Buenos_Aires", - "label": "America/Argentina/Buenos Aires" - }, - { - "name": "America/Argentina/Catamarca", - "label": "America/Argentina/Catamarca" - }, - { - "name": "America/Argentina/Cordoba", - "label": "America/Argentina/Cordoba" - }, - { - "name": "America/Argentina/Jujuy", - "label": "America/Argentina/Jujuy" - }, - { - "name": "America/Argentina/La_Rioja", - "label": "America/Argentina/La Rioja" - }, - { - "name": "America/Argentina/Mendoza", - "label": "America/Argentina/Mendoza" - }, - { - "name": "America/Argentina/Rio_Gallegos", - "label": "America/Argentina/Rio Gallegos" - }, - { - "name": "America/Argentina/Salta", - "label": "America/Argentina/Salta" - }, - { - "name": "America/Argentina/San_Juan", - "label": "America/Argentina/San Juan" - }, - { - "name": "America/Argentina/San_Luis", - "label": "America/Argentina/San Luis" - }, - { - "name": "America/Argentina/Tucuman", - "label": "America/Argentina/Tucuman" - }, - { - "name": "America/Argentina/Ushuaia", - "label": "America/Argentina/Ushuaia" - }, - { - "name": "America/Curacao", - "label": "America/Curacao" - }, - { - "name": "America/Asuncion", - "label": "America/Asuncion" - }, - { - "name": "America/Atikokan", - "label": "America/Atikokan" - }, - { - "name": "America/Bahia_Banderas", - "label": "America/Bahia Banderas" - }, - { - "name": "America/Bahia", - "label": "America/Bahia" - }, - { - "name": "America/Barbados", - "label": "America/Barbados" - }, - { - "name": "America/Belem", - "label": "America/Belem" - }, - { - "name": "America/Belize", - "label": "America/Belize" - }, - { - "name": "America/Blanc-Sablon", - "label": "America/Blanc-Sablon" - }, - { - "name": "America/Boa_Vista", - "label": "America/Boa Vista" - }, - { - "name": "America/Bogota", - "label": "America/Bogota" - }, - { - "name": "America/Boise", - "label": "America/Boise" - }, - { - "name": "America/Cambridge_Bay", - "label": "America/Cambridge Bay" - }, - { - "name": "America/Campo_Grande", - "label": "America/Campo Grande" - }, - { - "name": "America/Cancun", - "label": "America/Cancun" - }, - { - "name": "America/Caracas", - "label": "America/Caracas" - }, - { - "name": "America/Cayenne", - "label": "America/Cayenne" - }, - { - "name": "America/Panama", - "label": "America/Panama" - }, - { - "name": "America/Chicago", - "label": "America/Chicago" - }, - { - "name": "America/Chihuahua", - "label": "America/Chihuahua" - }, - { - "name": "America/Costa_Rica", - "label": "America/Costa Rica" - }, - { - "name": "America/Creston", - "label": "America/Creston" - }, - { - "name": "America/Cuiaba", - "label": "America/Cuiaba" - }, - { - "name": "America/Danmarkshavn", - "label": "America/Danmarkshavn" - }, - { - "name": "America/Dawson_Creek", - "label": "America/Dawson Creek" - }, - { - "name": "America/Dawson", - "label": "America/Dawson" - }, - { - "name": "America/Denver", - "label": "America/Denver" - }, - { - "name": "America/Detroit", - "label": "America/Detroit" - }, - { - "name": "America/Edmonton", - "label": "America/Edmonton" - }, - { - "name": "America/Eirunepe", - "label": "America/Eirunepe" - }, - { - "name": "America/El_Salvador", - "label": "America/El Salvador" - }, - { - "name": "America/Tijuana", - "label": "America/Tijuana" - }, - { - "name": "America/Fort_Nelson", - "label": "America/Fort Nelson" - }, - { - "name": "America/Fort_Wayne", - "label": "America/Fort Wayne" - }, - { - "name": "America/Fortaleza", - "label": "America/Fortaleza" - }, - { - "name": "America/Glace_Bay", - "label": "America/Glace Bay" - }, - { - "name": "America/Godthab", - "label": "America/Godthab" - }, - { - "name": "America/Goose_Bay", - "label": "America/Goose Bay" - }, - { - "name": "America/Grand_Turk", - "label": "America/Grand Turk" - }, - { - "name": "America/Guatemala", - "label": "America/Guatemala" - }, - { - "name": "America/Guayaquil", - "label": "America/Guayaquil" - }, - { - "name": "America/Guyana", - "label": "America/Guyana" - }, - { - "name": "America/Halifax", - "label": "America/Halifax" - }, - { - "name": "America/Havana", - "label": "America/Havana" - }, - { - "name": "America/Hermosillo", - "label": "America/Hermosillo" - }, - { - "name": "America/Indiana/Knox", - "label": "America/Indiana/Knox" - }, - { - "name": "America/Indiana/Marengo", - "label": "America/Indiana/Marengo" - }, - { - "name": "America/Indiana/Petersburg", - "label": "America/Indiana/Petersburg" - }, - { - "name": "America/Indiana/Tell_City", - "label": "America/Indiana/Tell City" - }, - { - "name": "America/Indiana/Vevay", - "label": "America/Indiana/Vevay" - }, - { - "name": "America/Indiana/Vincennes", - "label": "America/Indiana/Vincennes" - }, - { - "name": "America/Indiana/Winamac", - "label": "America/Indiana/Winamac" - }, - { - "name": "America/Inuvik", - "label": "America/Inuvik" - }, - { - "name": "America/Iqaluit", - "label": "America/Iqaluit" - }, - { - "name": "America/Jamaica", - "label": "America/Jamaica" - }, - { - "name": "America/Juneau", - "label": "America/Juneau" - }, - { - "name": "America/Kentucky/Louisville", - "label": "America/Kentucky/Louisville" - }, - { - "name": "America/Kentucky/Monticello", - "label": "America/Kentucky/Monticello" - }, - { - "name": "America/La_Paz", - "label": "America/La Paz" - }, - { - "name": "America/Lima", - "label": "America/Lima" - }, - { - "name": "America/Los_Angeles", - "label": "America/Los Angeles" - }, - { - "name": "America/Maceio", - "label": "America/Maceio" - }, - { - "name": "America/Managua", - "label": "America/Managua" - }, - { - "name": "America/Manaus", - "label": "America/Manaus" - }, - { - "name": "America/Martinique", - "label": "America/Martinique" - }, - { - "name": "America/Matamoros", - "label": "America/Matamoros" - }, - { - "name": "America/Mazatlan", - "label": "America/Mazatlan" - }, - { - "name": "America/Menominee", - "label": "America/Menominee" - }, - { - "name": "America/Merida", - "label": "America/Merida" - }, - { - "name": "America/Metlakatla", - "label": "America/Metlakatla" - }, - { - "name": "America/Mexico_City", - "label": "America/Mexico City" - }, - { - "name": "America/Miquelon", - "label": "America/Miquelon" - }, - { - "name": "America/Moncton", - "label": "America/Moncton" - }, - { - "name": "America/Monterrey", - "label": "America/Monterrey" - }, - { - "name": "America/Montevideo", - "label": "America/Montevideo" - }, - { - "name": "America/Toronto", - "label": "America/Toronto" - }, - { - "name": "America/Nassau", - "label": "America/Nassau" - }, - { - "name": "America/New_York", - "label": "America/New York" - }, - { - "name": "America/Nipigon", - "label": "America/Nipigon" - }, - { - "name": "America/Nome", - "label": "America/Nome" - }, - { - "name": "America/Noronha", - "label": "America/Noronha" - }, - { - "name": "America/North_Dakota/Beulah", - "label": "America/North Dakota/Beulah" - }, - { - "name": "America/North_Dakota/Center", - "label": "America/North Dakota/Center" - }, - { - "name": "America/North_Dakota/New_Salem", - "label": "America/North Dakota/New Salem" - }, - { - "name": "America/Ojinaga", - "label": "America/Ojinaga" - }, - { - "name": "America/Pangnirtung", - "label": "America/Pangnirtung" - }, - { - "name": "America/Paramaribo", - "label": "America/Paramaribo" - }, - { - "name": "America/Phoenix", - "label": "America/Phoenix" - }, - { - "name": "America/Port-au-Prince", - "label": "America/Port-au-Prince" - }, - { - "name": "America/Rio_Branco", - "label": "America/Rio Branco" - }, - { - "name": "America/Porto_Velho", - "label": "America/Porto Velho" - }, - { - "name": "America/Puerto_Rico", - "label": "America/Puerto Rico" - }, - { - "name": "America/Punta_Arenas", - "label": "America/Punta Arenas" - }, - { - "name": "America/Rainy_River", - "label": "America/Rainy River" - }, - { - "name": "America/Rankin_Inlet", - "label": "America/Rankin Inlet" - }, - { - "name": "America/Recife", - "label": "America/Recife" - }, - { - "name": "America/Regina", - "label": "America/Regina" - }, - { - "name": "America/Resolute", - "label": "America/Resolute" - }, - { - "name": "America/Santarem", - "label": "America/Santarem" - }, - { - "name": "America/Santiago", - "label": "America/Santiago" - }, - { - "name": "America/Santo_Domingo", - "label": "America/Santo Domingo" - }, - { - "name": "America/Sao_Paulo", - "label": "America/Sao Paulo" - }, - { - "name": "America/Scoresbysund", - "label": "America/Scoresbysund" - }, - { - "name": "America/Sitka", - "label": "America/Sitka" - }, - { - "name": "America/St_Johns", - "label": "America/St Johns" - }, - { - "name": "America/Swift_Current", - "label": "America/Swift Current" - }, - { - "name": "America/Tegucigalpa", - "label": "America/Tegucigalpa" - }, - { - "name": "America/Thule", - "label": "America/Thule" - }, - { - "name": "America/Thunder_Bay", - "label": "America/Thunder Bay" - }, - { - "name": "America/Vancouver", - "label": "America/Vancouver" - }, - { - "name": "America/Whitehorse", - "label": "America/Whitehorse" - }, - { - "name": "America/Winnipeg", - "label": "America/Winnipeg" - }, - { - "name": "America/Yakutat", - "label": "America/Yakutat" - }, - { - "name": "America/Yellowknife", - "label": "America/Yellowknife" - }, - { - "name": "Antarctica/Casey", - "label": "Antarctica/Casey" - }, - { - "name": "Antarctica/Davis", - "label": "Antarctica/Davis" - }, - { - "name": "Antarctica/DumontDUrville", - "label": "Antarctica/DumontDUrville" - }, - { - "name": "Antarctica/Macquarie", - "label": "Antarctica/Macquarie" - }, - { - "name": "Antarctica/Mawson", - "label": "Antarctica/Mawson" - }, - { - "name": "Pacific/Auckland", - "label": "Pacific/Auckland" - }, - { - "name": "Antarctica/Palmer", - "label": "Antarctica/Palmer" - }, - { - "name": "Antarctica/Rothera", - "label": "Antarctica/Rothera" - }, - { - "name": "Antarctica/Syowa", - "label": "Antarctica/Syowa" - }, - { - "name": "Antarctica/Troll", - "label": "Antarctica/Troll" - }, - { - "name": "Antarctica/Vostok", - "label": "Antarctica/Vostok" - }, - { - "name": "Europe/Oslo", - "label": "Europe/Oslo" - }, - { - "name": "Asia/Riyadh", - "label": "Asia/Riyadh" - }, - { - "name": "Asia/Almaty", - "label": "Asia/Almaty" - }, - { - "name": "Asia/Amman", - "label": "Asia/Amman" - }, - { - "name": "Asia/Anadyr", - "label": "Asia/Anadyr" - }, - { - "name": "Asia/Aqtau", - "label": "Asia/Aqtau" - }, - { - "name": "Asia/Aqtobe", - "label": "Asia/Aqtobe" - }, - { - "name": "Asia/Ashgabat", - "label": "Asia/Ashgabat" - }, - { - "name": "Asia/Atyrau", - "label": "Asia/Atyrau" - }, - { - "name": "Asia/Baghdad", - "label": "Asia/Baghdad" - }, - { - "name": "Asia/Qatar", - "label": "Asia/Qatar" - }, - { - "name": "Asia/Baku", - "label": "Asia/Baku" - }, - { - "name": "Asia/Bangkok", - "label": "Asia/Bangkok" - }, - { - "name": "Asia/Barnaul", - "label": "Asia/Barnaul" - }, - { - "name": "Asia/Beirut", - "label": "Asia/Beirut" - }, - { - "name": "Asia/Bishkek", - "label": "Asia/Bishkek" - }, - { - "name": "Asia/Brunei", - "label": "Asia/Brunei" - }, - { - "name": "Asia/Kolkata", - "label": "Asia/Kolkata" - }, - { - "name": "Asia/Chita", - "label": "Asia/Chita" - }, - { - "name": "Asia/Choibalsan", - "label": "Asia/Choibalsan" - }, - { - "name": "Asia/Shanghai", - "label": "Asia/Shanghai" - }, - { - "name": "Asia/Colombo", - "label": "Asia/Colombo" - }, - { - "name": "Asia/Dhaka", - "label": "Asia/Dhaka" - }, - { - "name": "Asia/Damascus", - "label": "Asia/Damascus" - }, - { - "name": "Asia/Dili", - "label": "Asia/Dili" - }, - { - "name": "Asia/Dubai", - "label": "Asia/Dubai" - }, - { - "name": "Asia/Dushanbe", - "label": "Asia/Dushanbe" - }, - { - "name": "Asia/Famagusta", - "label": "Asia/Famagusta" - }, - { - "name": "Asia/Gaza", - "label": "Asia/Gaza" - }, - { - "name": "Asia/Hebron", - "label": "Asia/Hebron" - }, - { - "name": "Asia/Ho_Chi_Minh", - "label": "Asia/Ho Chi Minh" - }, - { - "name": "Asia/Hong_Kong", - "label": "Asia/Hong Kong" - }, - { - "name": "Asia/Hovd", - "label": "Asia/Hovd" - }, - { - "name": "Asia/Irkutsk", - "label": "Asia/Irkutsk" - }, - { - "name": "Europe/Istanbul", - "label": "Europe/Istanbul" - }, - { - "name": "Asia/Jakarta", - "label": "Asia/Jakarta" - }, - { - "name": "Asia/Jayapura", - "label": "Asia/Jayapura" - }, - { - "name": "Asia/Jerusalem", - "label": "Asia/Jerusalem" - }, - { - "name": "Asia/Kabul", - "label": "Asia/Kabul" - }, - { - "name": "Asia/Kamchatka", - "label": "Asia/Kamchatka" - }, - { - "name": "Asia/Karachi", - "label": "Asia/Karachi" - }, - { - "name": "Asia/Urumqi", - "label": "Asia/Urumqi" - }, - { - "name": "Asia/Kathmandu", - "label": "Asia/Kathmandu" - }, - { - "name": "Asia/Khandyga", - "label": "Asia/Khandyga" - }, - { - "name": "Asia/Krasnoyarsk", - "label": "Asia/Krasnoyarsk" - }, - { - "name": "Asia/Kuala_Lumpur", - "label": "Asia/Kuala Lumpur" - }, - { - "name": "Asia/Kuching", - "label": "Asia/Kuching" - }, - { - "name": "Asia/Macau", - "label": "Asia/Macau" - }, - { - "name": "Asia/Magadan", - "label": "Asia/Magadan" - }, - { - "name": "Asia/Makassar", - "label": "Asia/Makassar" - }, - { - "name": "Asia/Manila", - "label": "Asia/Manila" - }, - { - "name": "Asia/Nicosia", - "label": "Asia/Nicosia" - }, - { - "name": "Asia/Novokuznetsk", - "label": "Asia/Novokuznetsk" - }, - { - "name": "Asia/Novosibirsk", - "label": "Asia/Novosibirsk" - }, - { - "name": "Asia/Omsk", - "label": "Asia/Omsk" - }, - { - "name": "Asia/Oral", - "label": "Asia/Oral" - }, - { - "name": "Asia/Pontianak", - "label": "Asia/Pontianak" - }, - { - "name": "Asia/Pyongyang", - "label": "Asia/Pyongyang" - }, - { - "name": "Asia/Qyzylorda", - "label": "Asia/Qyzylorda" - }, - { - "name": "Asia/Rangoon", - "label": "Asia/Rangoon" - }, - { - "name": "Asia/Sakhalin", - "label": "Asia/Sakhalin" - }, - { - "name": "Asia/Samarkand", - "label": "Asia/Samarkand" - }, - { - "name": "Asia/Seoul", - "label": "Asia/Seoul" - }, - { - "name": "Asia/Srednekolymsk", - "label": "Asia/Srednekolymsk" - }, - { - "name": "Asia/Taipei", - "label": "Asia/Taipei" - }, - { - "name": "Asia/Tashkent", - "label": "Asia/Tashkent" - }, - { - "name": "Asia/Tbilisi", - "label": "Asia/Tbilisi" - }, - { - "name": "Asia/Tehran", - "label": "Asia/Tehran" - }, - { - "name": "Asia/Thimphu", - "label": "Asia/Thimphu" - }, - { - "name": "Asia/Tokyo", - "label": "Asia/Tokyo" - }, - { - "name": "Asia/Tomsk", - "label": "Asia/Tomsk" - }, - { - "name": "Asia/Ulaanbaatar", - "label": "Asia/Ulaanbaatar" - }, - { - "name": "Asia/Ust-Nera", - "label": "Asia/Ust-Nera" - }, - { - "name": "Asia/Vladivostok", - "label": "Asia/Vladivostok" - }, - { - "name": "Asia/Yakutsk", - "label": "Asia/Yakutsk" - }, - { - "name": "Asia/Yekaterinburg", - "label": "Asia/Yekaterinburg" - }, - { - "name": "Asia/Yerevan", - "label": "Asia/Yerevan" - }, - { - "name": "Atlantic/Azores", - "label": "Atlantic/Azores" - }, - { - "name": "Atlantic/Bermuda", - "label": "Atlantic/Bermuda" - }, - { - "name": "Atlantic/Canary", - "label": "Atlantic/Canary" - }, - { - "name": "Atlantic/Cape_Verde", - "label": "Atlantic/Cape Verde" - }, - { - "name": "Atlantic/Faroe", - "label": "Atlantic/Faroe" - }, - { - "name": "Atlantic/Madeira", - "label": "Atlantic/Madeira" - }, - { - "name": "Atlantic/Reykjavik", - "label": "Atlantic/Reykjavik" - }, - { - "name": "Atlantic/South_Georgia", - "label": "Atlantic/South Georgia" - }, - { - "name": "Atlantic/Stanley", - "label": "Atlantic/Stanley" - }, - { - "name": "Australia/Sydney", - "label": "Australia/Sydney" - }, - { - "name": "Australia/Adelaide", - "label": "Australia/Adelaide" - }, - { - "name": "Australia/Brisbane", - "label": "Australia/Brisbane" - }, - { - "name": "Australia/Broken_Hill", - "label": "Australia/Broken Hill" - }, - { - "name": "Australia/Currie", - "label": "Australia/Currie" - }, - { - "name": "Australia/Darwin", - "label": "Australia/Darwin" - }, - { - "name": "Australia/Eucla", - "label": "Australia/Eucla" - }, - { - "name": "Australia/Hobart", - "label": "Australia/Hobart" - }, - { - "name": "Australia/Lord_Howe", - "label": "Australia/Lord Howe" - }, - { - "name": "Australia/Lindeman", - "label": "Australia/Lindeman" - }, - { - "name": "Australia/Melbourne", - "label": "Australia/Melbourne" - }, - { - "name": "Australia/Perth", - "label": "Australia/Perth" - }, - { - "name": "CET", - "label": "CET" - }, - { - "name": "Pacific/Easter", - "label": "Pacific/Easter" - }, - { - "name": "CST6CDT", - "label": "CST6CDT" - }, - { - "name": "EET", - "label": "EET" - }, - { - "name": "Europe/Dublin", - "label": "Europe/Dublin" - }, - { - "name": "EST", - "label": "EST" - }, - { - "name": "EST5EDT", - "label": "EST5EDT" - }, - { - "name": "Etc/GMT-0", - "label": "Etc/GMT-0" - }, - { - "name": "Etc/GMT-1", - "label": "Etc/GMT-1" - }, - { - "name": "Pacific/Port_Moresby", - "label": "Pacific/Port Moresby" - }, - { - "name": "Pacific/Pohnpei", - "label": "Pacific/Pohnpei" - }, - { - "name": "Pacific/Tarawa", - "label": "Pacific/Tarawa" - }, - { - "name": "Etc/GMT-13", - "label": "Etc/GMT-13" - }, - { - "name": "Etc/GMT-14", - "label": "Etc/GMT-14" - }, - { - "name": "Etc/GMT-2", - "label": "Etc/GMT-2" - }, - { - "name": "Etc/GMT-3", - "label": "Etc/GMT-3" - }, - { - "name": "Etc/GMT-4", - "label": "Etc/GMT-4" - }, - { - "name": "Etc/GMT-5", - "label": "Etc/GMT-5" - }, - { - "name": "Etc/GMT-6", - "label": "Etc/GMT-6" - }, - { - "name": "Indian/Christmas", - "label": "Indian/Christmas" - }, - { - "name": "Etc/GMT-8", - "label": "Etc/GMT-8" - }, - { - "name": "Pacific/Palau", - "label": "Pacific/Palau" - }, - { - "name": "Etc/GMT+1", - "label": "Etc/GMT+1" - }, - { - "name": "Etc/GMT+10", - "label": "Etc/GMT+10" - }, - { - "name": "Etc/GMT+11", - "label": "Etc/GMT+11" - }, - { - "name": "Etc/GMT+12", - "label": "Etc/GMT+12" - }, - { - "name": "Etc/GMT+3", - "label": "Etc/GMT+3" - }, - { - "name": "Etc/GMT+4", - "label": "Etc/GMT+4" - }, - { - "name": "Etc/GMT+5", - "label": "Etc/GMT+5" - }, - { - "name": "Etc/GMT+6", - "label": "Etc/GMT+6" - }, - { - "name": "Etc/GMT+7", - "label": "Etc/GMT+7" - }, - { - "name": "Etc/GMT+8", - "label": "Etc/GMT+8" - }, - { - "name": "Etc/GMT+9", - "label": "Etc/GMT+9" - }, - { - "name": "Etc/UCT", - "label": "Etc/UCT" - }, - { - "name": "Etc/UTC", - "label": "Etc/UTC" - }, - { - "name": "Europe/Amsterdam", - "label": "Europe/Amsterdam" - }, - { - "name": "Europe/Andorra", - "label": "Europe/Andorra" - }, - { - "name": "Europe/Astrakhan", - "label": "Europe/Astrakhan" - }, - { - "name": "Europe/Athens", - "label": "Europe/Athens" - }, - { - "name": "Europe/London", - "label": "Europe/London" - }, - { - "name": "Europe/Belgrade", - "label": "Europe/Belgrade" - }, - { - "name": "Europe/Berlin", - "label": "Europe/Berlin" - }, - { - "name": "Europe/Prague", - "label": "Europe/Prague" - }, - { - "name": "Europe/Brussels", - "label": "Europe/Brussels" - }, - { - "name": "Europe/Bucharest", - "label": "Europe/Bucharest" - }, - { - "name": "Europe/Budapest", - "label": "Europe/Budapest" - }, - { - "name": "Europe/Zurich", - "label": "Europe/Zurich" - }, - { - "name": "Europe/Chisinau", - "label": "Europe/Chisinau" - }, - { - "name": "Europe/Copenhagen", - "label": "Europe/Copenhagen" - }, - { - "name": "Europe/Gibraltar", - "label": "Europe/Gibraltar" - }, - { - "name": "Europe/Helsinki", - "label": "Europe/Helsinki" - }, - { - "name": "Europe/Kaliningrad", - "label": "Europe/Kaliningrad" - }, - { - "name": "Europe/Kiev", - "label": "Europe/Kiev" - }, - { - "name": "Europe/Kirov", - "label": "Europe/Kirov" - }, - { - "name": "Europe/Lisbon", - "label": "Europe/Lisbon" - }, - { - "name": "Europe/Luxembourg", - "label": "Europe/Luxembourg" - }, - { - "name": "Europe/Madrid", - "label": "Europe/Madrid" - }, - { - "name": "Europe/Malta", - "label": "Europe/Malta" - }, - { - "name": "Europe/Minsk", - "label": "Europe/Minsk" - }, - { - "name": "Europe/Monaco", - "label": "Europe/Monaco" - }, - { - "name": "Europe/Moscow", - "label": "Europe/Moscow" - }, - { - "name": "Europe/Paris", - "label": "Europe/Paris" - }, - { - "name": "Europe/Riga", - "label": "Europe/Riga" - }, - { - "name": "Europe/Rome", - "label": "Europe/Rome" - }, - { - "name": "Europe/Samara", - "label": "Europe/Samara" - }, - { - "name": "Europe/Saratov", - "label": "Europe/Saratov" - }, - { - "name": "Europe/Simferopol", - "label": "Europe/Simferopol" - }, - { - "name": "Europe/Sofia", - "label": "Europe/Sofia" - }, - { - "name": "Europe/Stockholm", - "label": "Europe/Stockholm" - }, - { - "name": "Europe/Tallinn", - "label": "Europe/Tallinn" - }, - { - "name": "Europe/Tirane", - "label": "Europe/Tirane" - }, - { - "name": "Europe/Ulyanovsk", - "label": "Europe/Ulyanovsk" - }, - { - "name": "Europe/Uzhgorod", - "label": "Europe/Uzhgorod" - }, - { - "name": "Europe/Vienna", - "label": "Europe/Vienna" - }, - { - "name": "Europe/Vilnius", - "label": "Europe/Vilnius" - }, - { - "name": "Europe/Volgograd", - "label": "Europe/Volgograd" - }, - { - "name": "Europe/Warsaw", - "label": "Europe/Warsaw" - }, - { - "name": "Europe/Zaporozhye", - "label": "Europe/Zaporozhye" - }, - { - "name": "HST", - "label": "HST" - }, - { - "name": "Indian/Chagos", - "label": "Indian/Chagos" - }, - { - "name": "Indian/Cocos", - "label": "Indian/Cocos" - }, - { - "name": "Indian/Kerguelen", - "label": "Indian/Kerguelen" - }, - { - "name": "Indian/Mahe", - "label": "Indian/Mahe" - }, - { - "name": "Indian/Maldives", - "label": "Indian/Maldives" - }, - { - "name": "Indian/Mauritius", - "label": "Indian/Mauritius" - }, - { - "name": "Indian/Reunion", - "label": "Indian/Reunion" - }, - { - "name": "Pacific/Kwajalein", - "label": "Pacific/Kwajalein" - }, - { - "name": "MET", - "label": "MET" - }, - { - "name": "MST", - "label": "MST" - }, - { - "name": "MST7MDT", - "label": "MST7MDT" - }, - { - "name": "Pacific/Chatham", - "label": "Pacific/Chatham" - }, - { - "name": "Pacific/Apia", - "label": "Pacific/Apia" - }, - { - "name": "Pacific/Bougainville", - "label": "Pacific/Bougainville" - }, - { - "name": "Pacific/Efate", - "label": "Pacific/Efate" - }, - { - "name": "Pacific/Enderbury", - "label": "Pacific/Enderbury" - }, - { - "name": "Pacific/Fakaofo", - "label": "Pacific/Fakaofo" - }, - { - "name": "Pacific/Fiji", - "label": "Pacific/Fiji" - }, - { - "name": "Pacific/Galapagos", - "label": "Pacific/Galapagos" - }, - { - "name": "Pacific/Gambier", - "label": "Pacific/Gambier" - }, - { - "name": "Pacific/Guadalcanal", - "label": "Pacific/Guadalcanal" - }, - { - "name": "Pacific/Guam", - "label": "Pacific/Guam" - }, - { - "name": "Pacific/Honolulu", - "label": "Pacific/Honolulu" - }, - { - "name": "Pacific/Kiritimati", - "label": "Pacific/Kiritimati" - }, - { - "name": "Pacific/Kosrae", - "label": "Pacific/Kosrae" - }, - { - "name": "Pacific/Majuro", - "label": "Pacific/Majuro" - }, - { - "name": "Pacific/Marquesas", - "label": "Pacific/Marquesas" - }, - { - "name": "Pacific/Pago_Pago", - "label": "Pacific/Pago Pago" - }, - { - "name": "Pacific/Nauru", - "label": "Pacific/Nauru" - }, - { - "name": "Pacific/Niue", - "label": "Pacific/Niue" - }, - { - "name": "Pacific/Norfolk", - "label": "Pacific/Norfolk" - }, - { - "name": "Pacific/Noumea", - "label": "Pacific/Noumea" - }, - { - "name": "Pacific/Pitcairn", - "label": "Pacific/Pitcairn" - }, - { - "name": "Pacific/Rarotonga", - "label": "Pacific/Rarotonga" - }, - { - "name": "Pacific/Tahiti", - "label": "Pacific/Tahiti" - }, - { - "name": "Pacific/Tongatapu", - "label": "Pacific/Tongatapu" - }, - { - "name": "PST8PDT", - "label": "PST8PDT" - }, - { - "name": "WET", - "label": "WET" - } + { + "name": "Africa/Abidjan", + "label": "Africa/Abidjan" + }, + { + "name": "Africa/Accra", + "label": "Africa/Accra" + }, + { + "name": "Africa/Nairobi", + "label": "Africa/Nairobi" + }, + { + "name": "Africa/Algiers", + "label": "Africa/Algiers" + }, + { + "name": "Africa/Lagos", + "label": "Africa/Lagos" + }, + { + "name": "Africa/Bissau", + "label": "Africa/Bissau" + }, + { + "name": "Africa/Maputo", + "label": "Africa/Maputo" + }, + { + "name": "Africa/Cairo", + "label": "Africa/Cairo" + }, + { + "name": "Africa/Casablanca", + "label": "Africa/Casablanca" + }, + { + "name": "Africa/Ceuta", + "label": "Africa/Ceuta" + }, + { + "name": "Africa/El_Aaiun", + "label": "Africa/El Aaiun" + }, + { + "name": "Africa/Johannesburg", + "label": "Africa/Johannesburg" + }, + { + "name": "Africa/Juba", + "label": "Africa/Juba" + }, + { + "name": "Africa/Khartoum", + "label": "Africa/Khartoum" + }, + { + "name": "Africa/Monrovia", + "label": "Africa/Monrovia" + }, + { + "name": "Africa/Ndjamena", + "label": "Africa/Ndjamena" + }, + { + "name": "Africa/Sao_Tome", + "label": "Africa/Sao Tome" + }, + { + "name": "Africa/Tripoli", + "label": "Africa/Tripoli" + }, + { + "name": "Africa/Tunis", + "label": "Africa/Tunis" + }, + { + "name": "Africa/Windhoek", + "label": "Africa/Windhoek" + }, + { + "name": "America/Adak", + "label": "America/Adak" + }, + { + "name": "America/Anchorage", + "label": "America/Anchorage" + }, + { + "name": "America/Port_of_Spain", + "label": "America/Port of Spain" + }, + { + "name": "America/Araguaina", + "label": "America/Araguaina" + }, + { + "name": "America/Argentina/Buenos_Aires", + "label": "America/Argentina/Buenos Aires" + }, + { + "name": "America/Argentina/Catamarca", + "label": "America/Argentina/Catamarca" + }, + { + "name": "America/Argentina/Cordoba", + "label": "America/Argentina/Cordoba" + }, + { + "name": "America/Argentina/Jujuy", + "label": "America/Argentina/Jujuy" + }, + { + "name": "America/Argentina/La_Rioja", + "label": "America/Argentina/La Rioja" + }, + { + "name": "America/Argentina/Mendoza", + "label": "America/Argentina/Mendoza" + }, + { + "name": "America/Argentina/Rio_Gallegos", + "label": "America/Argentina/Rio Gallegos" + }, + { + "name": "America/Argentina/Salta", + "label": "America/Argentina/Salta" + }, + { + "name": "America/Argentina/San_Juan", + "label": "America/Argentina/San Juan" + }, + { + "name": "America/Argentina/San_Luis", + "label": "America/Argentina/San Luis" + }, + { + "name": "America/Argentina/Tucuman", + "label": "America/Argentina/Tucuman" + }, + { + "name": "America/Argentina/Ushuaia", + "label": "America/Argentina/Ushuaia" + }, + { + "name": "America/Curacao", + "label": "America/Curacao" + }, + { + "name": "America/Asuncion", + "label": "America/Asuncion" + }, + { + "name": "America/Atikokan", + "label": "America/Atikokan" + }, + { + "name": "America/Bahia_Banderas", + "label": "America/Bahia Banderas" + }, + { + "name": "America/Bahia", + "label": "America/Bahia" + }, + { + "name": "America/Barbados", + "label": "America/Barbados" + }, + { + "name": "America/Belem", + "label": "America/Belem" + }, + { + "name": "America/Belize", + "label": "America/Belize" + }, + { + "name": "America/Blanc-Sablon", + "label": "America/Blanc-Sablon" + }, + { + "name": "America/Boa_Vista", + "label": "America/Boa Vista" + }, + { + "name": "America/Bogota", + "label": "America/Bogota" + }, + { + "name": "America/Boise", + "label": "America/Boise" + }, + { + "name": "America/Cambridge_Bay", + "label": "America/Cambridge Bay" + }, + { + "name": "America/Campo_Grande", + "label": "America/Campo Grande" + }, + { + "name": "America/Cancun", + "label": "America/Cancun" + }, + { + "name": "America/Caracas", + "label": "America/Caracas" + }, + { + "name": "America/Cayenne", + "label": "America/Cayenne" + }, + { + "name": "America/Panama", + "label": "America/Panama" + }, + { + "name": "America/Chicago", + "label": "America/Chicago" + }, + { + "name": "America/Chihuahua", + "label": "America/Chihuahua" + }, + { + "name": "America/Costa_Rica", + "label": "America/Costa Rica" + }, + { + "name": "America/Creston", + "label": "America/Creston" + }, + { + "name": "America/Cuiaba", + "label": "America/Cuiaba" + }, + { + "name": "America/Danmarkshavn", + "label": "America/Danmarkshavn" + }, + { + "name": "America/Dawson_Creek", + "label": "America/Dawson Creek" + }, + { + "name": "America/Dawson", + "label": "America/Dawson" + }, + { + "name": "America/Denver", + "label": "America/Denver" + }, + { + "name": "America/Detroit", + "label": "America/Detroit" + }, + { + "name": "America/Edmonton", + "label": "America/Edmonton" + }, + { + "name": "America/Eirunepe", + "label": "America/Eirunepe" + }, + { + "name": "America/El_Salvador", + "label": "America/El Salvador" + }, + { + "name": "America/Tijuana", + "label": "America/Tijuana" + }, + { + "name": "America/Fort_Nelson", + "label": "America/Fort Nelson" + }, + { + "name": "America/Fort_Wayne", + "label": "America/Fort Wayne" + }, + { + "name": "America/Fortaleza", + "label": "America/Fortaleza" + }, + { + "name": "America/Glace_Bay", + "label": "America/Glace Bay" + }, + { + "name": "America/Godthab", + "label": "America/Godthab" + }, + { + "name": "America/Goose_Bay", + "label": "America/Goose Bay" + }, + { + "name": "America/Grand_Turk", + "label": "America/Grand Turk" + }, + { + "name": "America/Guatemala", + "label": "America/Guatemala" + }, + { + "name": "America/Guayaquil", + "label": "America/Guayaquil" + }, + { + "name": "America/Guyana", + "label": "America/Guyana" + }, + { + "name": "America/Halifax", + "label": "America/Halifax" + }, + { + "name": "America/Havana", + "label": "America/Havana" + }, + { + "name": "America/Hermosillo", + "label": "America/Hermosillo" + }, + { + "name": "America/Indiana/Knox", + "label": "America/Indiana/Knox" + }, + { + "name": "America/Indiana/Marengo", + "label": "America/Indiana/Marengo" + }, + { + "name": "America/Indiana/Petersburg", + "label": "America/Indiana/Petersburg" + }, + { + "name": "America/Indiana/Tell_City", + "label": "America/Indiana/Tell City" + }, + { + "name": "America/Indiana/Vevay", + "label": "America/Indiana/Vevay" + }, + { + "name": "America/Indiana/Vincennes", + "label": "America/Indiana/Vincennes" + }, + { + "name": "America/Indiana/Winamac", + "label": "America/Indiana/Winamac" + }, + { + "name": "America/Inuvik", + "label": "America/Inuvik" + }, + { + "name": "America/Iqaluit", + "label": "America/Iqaluit" + }, + { + "name": "America/Jamaica", + "label": "America/Jamaica" + }, + { + "name": "America/Juneau", + "label": "America/Juneau" + }, + { + "name": "America/Kentucky/Louisville", + "label": "America/Kentucky/Louisville" + }, + { + "name": "America/Kentucky/Monticello", + "label": "America/Kentucky/Monticello" + }, + { + "name": "America/La_Paz", + "label": "America/La Paz" + }, + { + "name": "America/Lima", + "label": "America/Lima" + }, + { + "name": "America/Los_Angeles", + "label": "America/Los Angeles" + }, + { + "name": "America/Maceio", + "label": "America/Maceio" + }, + { + "name": "America/Managua", + "label": "America/Managua" + }, + { + "name": "America/Manaus", + "label": "America/Manaus" + }, + { + "name": "America/Martinique", + "label": "America/Martinique" + }, + { + "name": "America/Matamoros", + "label": "America/Matamoros" + }, + { + "name": "America/Mazatlan", + "label": "America/Mazatlan" + }, + { + "name": "America/Menominee", + "label": "America/Menominee" + }, + { + "name": "America/Merida", + "label": "America/Merida" + }, + { + "name": "America/Metlakatla", + "label": "America/Metlakatla" + }, + { + "name": "America/Mexico_City", + "label": "America/Mexico City" + }, + { + "name": "America/Miquelon", + "label": "America/Miquelon" + }, + { + "name": "America/Moncton", + "label": "America/Moncton" + }, + { + "name": "America/Monterrey", + "label": "America/Monterrey" + }, + { + "name": "America/Montevideo", + "label": "America/Montevideo" + }, + { + "name": "America/Toronto", + "label": "America/Toronto" + }, + { + "name": "America/Nassau", + "label": "America/Nassau" + }, + { + "name": "America/New_York", + "label": "America/New York" + }, + { + "name": "America/Nipigon", + "label": "America/Nipigon" + }, + { + "name": "America/Nome", + "label": "America/Nome" + }, + { + "name": "America/Noronha", + "label": "America/Noronha" + }, + { + "name": "America/North_Dakota/Beulah", + "label": "America/North Dakota/Beulah" + }, + { + "name": "America/North_Dakota/Center", + "label": "America/North Dakota/Center" + }, + { + "name": "America/North_Dakota/New_Salem", + "label": "America/North Dakota/New Salem" + }, + { + "name": "America/Ojinaga", + "label": "America/Ojinaga" + }, + { + "name": "America/Pangnirtung", + "label": "America/Pangnirtung" + }, + { + "name": "America/Paramaribo", + "label": "America/Paramaribo" + }, + { + "name": "America/Phoenix", + "label": "America/Phoenix" + }, + { + "name": "America/Port-au-Prince", + "label": "America/Port-au-Prince" + }, + { + "name": "America/Rio_Branco", + "label": "America/Rio Branco" + }, + { + "name": "America/Porto_Velho", + "label": "America/Porto Velho" + }, + { + "name": "America/Puerto_Rico", + "label": "America/Puerto Rico" + }, + { + "name": "America/Punta_Arenas", + "label": "America/Punta Arenas" + }, + { + "name": "America/Rainy_River", + "label": "America/Rainy River" + }, + { + "name": "America/Rankin_Inlet", + "label": "America/Rankin Inlet" + }, + { + "name": "America/Recife", + "label": "America/Recife" + }, + { + "name": "America/Regina", + "label": "America/Regina" + }, + { + "name": "America/Resolute", + "label": "America/Resolute" + }, + { + "name": "America/Santarem", + "label": "America/Santarem" + }, + { + "name": "America/Santiago", + "label": "America/Santiago" + }, + { + "name": "America/Santo_Domingo", + "label": "America/Santo Domingo" + }, + { + "name": "America/Sao_Paulo", + "label": "America/Sao Paulo" + }, + { + "name": "America/Scoresbysund", + "label": "America/Scoresbysund" + }, + { + "name": "America/Sitka", + "label": "America/Sitka" + }, + { + "name": "America/St_Johns", + "label": "America/St Johns" + }, + { + "name": "America/Swift_Current", + "label": "America/Swift Current" + }, + { + "name": "America/Tegucigalpa", + "label": "America/Tegucigalpa" + }, + { + "name": "America/Thule", + "label": "America/Thule" + }, + { + "name": "America/Thunder_Bay", + "label": "America/Thunder Bay" + }, + { + "name": "America/Vancouver", + "label": "America/Vancouver" + }, + { + "name": "America/Whitehorse", + "label": "America/Whitehorse" + }, + { + "name": "America/Winnipeg", + "label": "America/Winnipeg" + }, + { + "name": "America/Yakutat", + "label": "America/Yakutat" + }, + { + "name": "America/Yellowknife", + "label": "America/Yellowknife" + }, + { + "name": "Antarctica/Casey", + "label": "Antarctica/Casey" + }, + { + "name": "Antarctica/Davis", + "label": "Antarctica/Davis" + }, + { + "name": "Antarctica/DumontDUrville", + "label": "Antarctica/DumontDUrville" + }, + { + "name": "Antarctica/Macquarie", + "label": "Antarctica/Macquarie" + }, + { + "name": "Antarctica/Mawson", + "label": "Antarctica/Mawson" + }, + { + "name": "Pacific/Auckland", + "label": "Pacific/Auckland" + }, + { + "name": "Antarctica/Palmer", + "label": "Antarctica/Palmer" + }, + { + "name": "Antarctica/Rothera", + "label": "Antarctica/Rothera" + }, + { + "name": "Antarctica/Syowa", + "label": "Antarctica/Syowa" + }, + { + "name": "Antarctica/Troll", + "label": "Antarctica/Troll" + }, + { + "name": "Antarctica/Vostok", + "label": "Antarctica/Vostok" + }, + { + "name": "Europe/Oslo", + "label": "Europe/Oslo" + }, + { + "name": "Asia/Riyadh", + "label": "Asia/Riyadh" + }, + { + "name": "Asia/Almaty", + "label": "Asia/Almaty" + }, + { + "name": "Asia/Amman", + "label": "Asia/Amman" + }, + { + "name": "Asia/Anadyr", + "label": "Asia/Anadyr" + }, + { + "name": "Asia/Aqtau", + "label": "Asia/Aqtau" + }, + { + "name": "Asia/Aqtobe", + "label": "Asia/Aqtobe" + }, + { + "name": "Asia/Ashgabat", + "label": "Asia/Ashgabat" + }, + { + "name": "Asia/Atyrau", + "label": "Asia/Atyrau" + }, + { + "name": "Asia/Baghdad", + "label": "Asia/Baghdad" + }, + { + "name": "Asia/Qatar", + "label": "Asia/Qatar" + }, + { + "name": "Asia/Baku", + "label": "Asia/Baku" + }, + { + "name": "Asia/Bangkok", + "label": "Asia/Bangkok" + }, + { + "name": "Asia/Barnaul", + "label": "Asia/Barnaul" + }, + { + "name": "Asia/Beirut", + "label": "Asia/Beirut" + }, + { + "name": "Asia/Bishkek", + "label": "Asia/Bishkek" + }, + { + "name": "Asia/Brunei", + "label": "Asia/Brunei" + }, + { + "name": "Asia/Kolkata", + "label": "Asia/Kolkata" + }, + { + "name": "Asia/Chita", + "label": "Asia/Chita" + }, + { + "name": "Asia/Choibalsan", + "label": "Asia/Choibalsan" + }, + { + "name": "Asia/Shanghai", + "label": "Asia/Shanghai" + }, + { + "name": "Asia/Colombo", + "label": "Asia/Colombo" + }, + { + "name": "Asia/Dhaka", + "label": "Asia/Dhaka" + }, + { + "name": "Asia/Damascus", + "label": "Asia/Damascus" + }, + { + "name": "Asia/Dili", + "label": "Asia/Dili" + }, + { + "name": "Asia/Dubai", + "label": "Asia/Dubai" + }, + { + "name": "Asia/Dushanbe", + "label": "Asia/Dushanbe" + }, + { + "name": "Asia/Famagusta", + "label": "Asia/Famagusta" + }, + { + "name": "Asia/Gaza", + "label": "Asia/Gaza" + }, + { + "name": "Asia/Hebron", + "label": "Asia/Hebron" + }, + { + "name": "Asia/Ho_Chi_Minh", + "label": "Asia/Ho Chi Minh" + }, + { + "name": "Asia/Hong_Kong", + "label": "Asia/Hong Kong" + }, + { + "name": "Asia/Hovd", + "label": "Asia/Hovd" + }, + { + "name": "Asia/Irkutsk", + "label": "Asia/Irkutsk" + }, + { + "name": "Europe/Istanbul", + "label": "Europe/Istanbul" + }, + { + "name": "Asia/Jakarta", + "label": "Asia/Jakarta" + }, + { + "name": "Asia/Jayapura", + "label": "Asia/Jayapura" + }, + { + "name": "Asia/Jerusalem", + "label": "Asia/Jerusalem" + }, + { + "name": "Asia/Kabul", + "label": "Asia/Kabul" + }, + { + "name": "Asia/Kamchatka", + "label": "Asia/Kamchatka" + }, + { + "name": "Asia/Karachi", + "label": "Asia/Karachi" + }, + { + "name": "Asia/Urumqi", + "label": "Asia/Urumqi" + }, + { + "name": "Asia/Kathmandu", + "label": "Asia/Kathmandu" + }, + { + "name": "Asia/Khandyga", + "label": "Asia/Khandyga" + }, + { + "name": "Asia/Krasnoyarsk", + "label": "Asia/Krasnoyarsk" + }, + { + "name": "Asia/Kuala_Lumpur", + "label": "Asia/Kuala Lumpur" + }, + { + "name": "Asia/Kuching", + "label": "Asia/Kuching" + }, + { + "name": "Asia/Macau", + "label": "Asia/Macau" + }, + { + "name": "Asia/Magadan", + "label": "Asia/Magadan" + }, + { + "name": "Asia/Makassar", + "label": "Asia/Makassar" + }, + { + "name": "Asia/Manila", + "label": "Asia/Manila" + }, + { + "name": "Asia/Nicosia", + "label": "Asia/Nicosia" + }, + { + "name": "Asia/Novokuznetsk", + "label": "Asia/Novokuznetsk" + }, + { + "name": "Asia/Novosibirsk", + "label": "Asia/Novosibirsk" + }, + { + "name": "Asia/Omsk", + "label": "Asia/Omsk" + }, + { + "name": "Asia/Oral", + "label": "Asia/Oral" + }, + { + "name": "Asia/Pontianak", + "label": "Asia/Pontianak" + }, + { + "name": "Asia/Pyongyang", + "label": "Asia/Pyongyang" + }, + { + "name": "Asia/Qyzylorda", + "label": "Asia/Qyzylorda" + }, + { + "name": "Asia/Rangoon", + "label": "Asia/Rangoon" + }, + { + "name": "Asia/Sakhalin", + "label": "Asia/Sakhalin" + }, + { + "name": "Asia/Samarkand", + "label": "Asia/Samarkand" + }, + { + "name": "Asia/Seoul", + "label": "Asia/Seoul" + }, + { + "name": "Asia/Srednekolymsk", + "label": "Asia/Srednekolymsk" + }, + { + "name": "Asia/Taipei", + "label": "Asia/Taipei" + }, + { + "name": "Asia/Tashkent", + "label": "Asia/Tashkent" + }, + { + "name": "Asia/Tbilisi", + "label": "Asia/Tbilisi" + }, + { + "name": "Asia/Tehran", + "label": "Asia/Tehran" + }, + { + "name": "Asia/Thimphu", + "label": "Asia/Thimphu" + }, + { + "name": "Asia/Tokyo", + "label": "Asia/Tokyo" + }, + { + "name": "Asia/Tomsk", + "label": "Asia/Tomsk" + }, + { + "name": "Asia/Ulaanbaatar", + "label": "Asia/Ulaanbaatar" + }, + { + "name": "Asia/Ust-Nera", + "label": "Asia/Ust-Nera" + }, + { + "name": "Asia/Vladivostok", + "label": "Asia/Vladivostok" + }, + { + "name": "Asia/Yakutsk", + "label": "Asia/Yakutsk" + }, + { + "name": "Asia/Yekaterinburg", + "label": "Asia/Yekaterinburg" + }, + { + "name": "Asia/Yerevan", + "label": "Asia/Yerevan" + }, + { + "name": "Atlantic/Azores", + "label": "Atlantic/Azores" + }, + { + "name": "Atlantic/Bermuda", + "label": "Atlantic/Bermuda" + }, + { + "name": "Atlantic/Canary", + "label": "Atlantic/Canary" + }, + { + "name": "Atlantic/Cape_Verde", + "label": "Atlantic/Cape Verde" + }, + { + "name": "Atlantic/Faroe", + "label": "Atlantic/Faroe" + }, + { + "name": "Atlantic/Madeira", + "label": "Atlantic/Madeira" + }, + { + "name": "Atlantic/Reykjavik", + "label": "Atlantic/Reykjavik" + }, + { + "name": "Atlantic/South_Georgia", + "label": "Atlantic/South Georgia" + }, + { + "name": "Atlantic/Stanley", + "label": "Atlantic/Stanley" + }, + { + "name": "Australia/Sydney", + "label": "Australia/Sydney" + }, + { + "name": "Australia/Adelaide", + "label": "Australia/Adelaide" + }, + { + "name": "Australia/Brisbane", + "label": "Australia/Brisbane" + }, + { + "name": "Australia/Broken_Hill", + "label": "Australia/Broken Hill" + }, + { + "name": "Australia/Currie", + "label": "Australia/Currie" + }, + { + "name": "Australia/Darwin", + "label": "Australia/Darwin" + }, + { + "name": "Australia/Eucla", + "label": "Australia/Eucla" + }, + { + "name": "Australia/Hobart", + "label": "Australia/Hobart" + }, + { + "name": "Australia/Lord_Howe", + "label": "Australia/Lord Howe" + }, + { + "name": "Australia/Lindeman", + "label": "Australia/Lindeman" + }, + { + "name": "Australia/Melbourne", + "label": "Australia/Melbourne" + }, + { + "name": "Australia/Perth", + "label": "Australia/Perth" + }, + { + "name": "CET", + "label": "CET" + }, + { + "name": "Pacific/Easter", + "label": "Pacific/Easter" + }, + { + "name": "CST6CDT", + "label": "CST6CDT" + }, + { + "name": "EET", + "label": "EET" + }, + { + "name": "Europe/Dublin", + "label": "Europe/Dublin" + }, + { + "name": "EST", + "label": "EST" + }, + { + "name": "EST5EDT", + "label": "EST5EDT" + }, + { + "name": "Etc/GMT-0", + "label": "Etc/GMT-0" + }, + { + "name": "Etc/GMT-1", + "label": "Etc/GMT-1" + }, + { + "name": "Pacific/Port_Moresby", + "label": "Pacific/Port Moresby" + }, + { + "name": "Pacific/Pohnpei", + "label": "Pacific/Pohnpei" + }, + { + "name": "Pacific/Tarawa", + "label": "Pacific/Tarawa" + }, + { + "name": "Etc/GMT-13", + "label": "Etc/GMT-13" + }, + { + "name": "Etc/GMT-14", + "label": "Etc/GMT-14" + }, + { + "name": "Etc/GMT-2", + "label": "Etc/GMT-2" + }, + { + "name": "Etc/GMT-3", + "label": "Etc/GMT-3" + }, + { + "name": "Etc/GMT-4", + "label": "Etc/GMT-4" + }, + { + "name": "Etc/GMT-5", + "label": "Etc/GMT-5" + }, + { + "name": "Etc/GMT-6", + "label": "Etc/GMT-6" + }, + { + "name": "Indian/Christmas", + "label": "Indian/Christmas" + }, + { + "name": "Etc/GMT-8", + "label": "Etc/GMT-8" + }, + { + "name": "Pacific/Palau", + "label": "Pacific/Palau" + }, + { + "name": "Etc/GMT+1", + "label": "Etc/GMT+1" + }, + { + "name": "Etc/GMT+10", + "label": "Etc/GMT+10" + }, + { + "name": "Etc/GMT+11", + "label": "Etc/GMT+11" + }, + { + "name": "Etc/GMT+12", + "label": "Etc/GMT+12" + }, + { + "name": "Etc/GMT+3", + "label": "Etc/GMT+3" + }, + { + "name": "Etc/GMT+4", + "label": "Etc/GMT+4" + }, + { + "name": "Etc/GMT+5", + "label": "Etc/GMT+5" + }, + { + "name": "Etc/GMT+6", + "label": "Etc/GMT+6" + }, + { + "name": "Etc/GMT+7", + "label": "Etc/GMT+7" + }, + { + "name": "Etc/GMT+8", + "label": "Etc/GMT+8" + }, + { + "name": "Etc/GMT+9", + "label": "Etc/GMT+9" + }, + { + "name": "Etc/UCT", + "label": "Etc/UCT" + }, + { + "name": "Etc/UTC", + "label": "Etc/UTC" + }, + { + "name": "Europe/Amsterdam", + "label": "Europe/Amsterdam" + }, + { + "name": "Europe/Andorra", + "label": "Europe/Andorra" + }, + { + "name": "Europe/Astrakhan", + "label": "Europe/Astrakhan" + }, + { + "name": "Europe/Athens", + "label": "Europe/Athens" + }, + { + "name": "Europe/London", + "label": "Europe/London" + }, + { + "name": "Europe/Belgrade", + "label": "Europe/Belgrade" + }, + { + "name": "Europe/Berlin", + "label": "Europe/Berlin" + }, + { + "name": "Europe/Prague", + "label": "Europe/Prague" + }, + { + "name": "Europe/Brussels", + "label": "Europe/Brussels" + }, + { + "name": "Europe/Bucharest", + "label": "Europe/Bucharest" + }, + { + "name": "Europe/Budapest", + "label": "Europe/Budapest" + }, + { + "name": "Europe/Zurich", + "label": "Europe/Zurich" + }, + { + "name": "Europe/Chisinau", + "label": "Europe/Chisinau" + }, + { + "name": "Europe/Copenhagen", + "label": "Europe/Copenhagen" + }, + { + "name": "Europe/Gibraltar", + "label": "Europe/Gibraltar" + }, + { + "name": "Europe/Helsinki", + "label": "Europe/Helsinki" + }, + { + "name": "Europe/Kaliningrad", + "label": "Europe/Kaliningrad" + }, + { + "name": "Europe/Kiev", + "label": "Europe/Kiev" + }, + { + "name": "Europe/Kirov", + "label": "Europe/Kirov" + }, + { + "name": "Europe/Lisbon", + "label": "Europe/Lisbon" + }, + { + "name": "Europe/Luxembourg", + "label": "Europe/Luxembourg" + }, + { + "name": "Europe/Madrid", + "label": "Europe/Madrid" + }, + { + "name": "Europe/Malta", + "label": "Europe/Malta" + }, + { + "name": "Europe/Minsk", + "label": "Europe/Minsk" + }, + { + "name": "Europe/Monaco", + "label": "Europe/Monaco" + }, + { + "name": "Europe/Moscow", + "label": "Europe/Moscow" + }, + { + "name": "Europe/Paris", + "label": "Europe/Paris" + }, + { + "name": "Europe/Riga", + "label": "Europe/Riga" + }, + { + "name": "Europe/Rome", + "label": "Europe/Rome" + }, + { + "name": "Europe/Samara", + "label": "Europe/Samara" + }, + { + "name": "Europe/Saratov", + "label": "Europe/Saratov" + }, + { + "name": "Europe/Simferopol", + "label": "Europe/Simferopol" + }, + { + "name": "Europe/Sofia", + "label": "Europe/Sofia" + }, + { + "name": "Europe/Stockholm", + "label": "Europe/Stockholm" + }, + { + "name": "Europe/Tallinn", + "label": "Europe/Tallinn" + }, + { + "name": "Europe/Tirane", + "label": "Europe/Tirane" + }, + { + "name": "Europe/Ulyanovsk", + "label": "Europe/Ulyanovsk" + }, + { + "name": "Europe/Uzhgorod", + "label": "Europe/Uzhgorod" + }, + { + "name": "Europe/Vienna", + "label": "Europe/Vienna" + }, + { + "name": "Europe/Vilnius", + "label": "Europe/Vilnius" + }, + { + "name": "Europe/Volgograd", + "label": "Europe/Volgograd" + }, + { + "name": "Europe/Warsaw", + "label": "Europe/Warsaw" + }, + { + "name": "Europe/Zaporozhye", + "label": "Europe/Zaporozhye" + }, + { + "name": "HST", + "label": "HST" + }, + { + "name": "Indian/Chagos", + "label": "Indian/Chagos" + }, + { + "name": "Indian/Cocos", + "label": "Indian/Cocos" + }, + { + "name": "Indian/Kerguelen", + "label": "Indian/Kerguelen" + }, + { + "name": "Indian/Mahe", + "label": "Indian/Mahe" + }, + { + "name": "Indian/Maldives", + "label": "Indian/Maldives" + }, + { + "name": "Indian/Mauritius", + "label": "Indian/Mauritius" + }, + { + "name": "Indian/Reunion", + "label": "Indian/Reunion" + }, + { + "name": "Pacific/Kwajalein", + "label": "Pacific/Kwajalein" + }, + { + "name": "MET", + "label": "MET" + }, + { + "name": "MST", + "label": "MST" + }, + { + "name": "MST7MDT", + "label": "MST7MDT" + }, + { + "name": "Pacific/Chatham", + "label": "Pacific/Chatham" + }, + { + "name": "Pacific/Apia", + "label": "Pacific/Apia" + }, + { + "name": "Pacific/Bougainville", + "label": "Pacific/Bougainville" + }, + { + "name": "Pacific/Efate", + "label": "Pacific/Efate" + }, + { + "name": "Pacific/Enderbury", + "label": "Pacific/Enderbury" + }, + { + "name": "Pacific/Fakaofo", + "label": "Pacific/Fakaofo" + }, + { + "name": "Pacific/Fiji", + "label": "Pacific/Fiji" + }, + { + "name": "Pacific/Galapagos", + "label": "Pacific/Galapagos" + }, + { + "name": "Pacific/Gambier", + "label": "Pacific/Gambier" + }, + { + "name": "Pacific/Guadalcanal", + "label": "Pacific/Guadalcanal" + }, + { + "name": "Pacific/Guam", + "label": "Pacific/Guam" + }, + { + "name": "Pacific/Honolulu", + "label": "Pacific/Honolulu" + }, + { + "name": "Pacific/Kiritimati", + "label": "Pacific/Kiritimati" + }, + { + "name": "Pacific/Kosrae", + "label": "Pacific/Kosrae" + }, + { + "name": "Pacific/Majuro", + "label": "Pacific/Majuro" + }, + { + "name": "Pacific/Marquesas", + "label": "Pacific/Marquesas" + }, + { + "name": "Pacific/Pago_Pago", + "label": "Pacific/Pago Pago" + }, + { + "name": "Pacific/Nauru", + "label": "Pacific/Nauru" + }, + { + "name": "Pacific/Niue", + "label": "Pacific/Niue" + }, + { + "name": "Pacific/Norfolk", + "label": "Pacific/Norfolk" + }, + { + "name": "Pacific/Noumea", + "label": "Pacific/Noumea" + }, + { + "name": "Pacific/Pitcairn", + "label": "Pacific/Pitcairn" + }, + { + "name": "Pacific/Rarotonga", + "label": "Pacific/Rarotonga" + }, + { + "name": "Pacific/Tahiti", + "label": "Pacific/Tahiti" + }, + { + "name": "Pacific/Tongatapu", + "label": "Pacific/Tongatapu" + }, + { + "name": "PST8PDT", + "label": "PST8PDT" + }, + { + "name": "WET", + "label": "WET" + } ] diff --git a/src/CDN.js b/src/CDN.js index 873d20950d..93a66e8d6d 100644 --- a/src/CDN.js +++ b/src/CDN.js @@ -3,82 +3,84 @@ // like Formio.cdn.ace === 'http://cdn.form.io/ace/1.4.12'. // For latest version use empty string class CDN { - constructor(baseUrl) { - this.baseUrl = baseUrl || 'https://cdn.form.io'; - this.overrides = {}; - this.libs = { - 'js': '', - 'ace': '1.4.12', - 'bootstrap': '4.6.2', - 'ckeditor': '19.0.0', - 'flatpickr': '4.6.8', - 'flatpickr-formio': '4.6.13-formio.3', - 'font-awesome': '4.7.0', - 'grid': 'latest', - 'moment-timezone': 'latest', - 'quill': '2.0.0-dev.3', - 'shortcut-buttons-flatpickr': '0.4.0', - 'uswds': '2.4.8', - 'core': '' - }; - this.updateUrls(); - } - - getVersion(lib) { - return this.libs[lib]; - } + constructor(baseUrl) { + this.baseUrl = baseUrl || 'https://cdn.form.io'; + this.overrides = {}; + this.libs = { + js: '', + ace: '1.4.12', + bootstrap: '4.6.2', + ckeditor: '19.0.0', + flatpickr: '4.6.8', + 'flatpickr-formio': '4.6.13-formio.3', + 'font-awesome': '4.7.0', + grid: 'latest', + 'moment-timezone': 'latest', + quill: '2.0.0-dev.3', + 'shortcut-buttons-flatpickr': '0.4.0', + uswds: '2.4.8', + core: '', + }; + this.updateUrls(); + } - // Sets a specific library version - setVersion(lib, version) { - this.libs[lib] = version; - this.updateUrls(); - } + getVersion(lib) { + return this.libs[lib]; + } - // Sets base CDN url for all libraries - setBaseUrl(url) { - this.baseUrl = url; - this.updateUrls(); - } + // Sets a specific library version + setVersion(lib, version) { + this.libs[lib] = version; + this.updateUrls(); + } - // Allows to override CDN url for a specific library - setOverrideUrl(lib, url) { - this.overrides[lib] = url; - this.updateUrls(); - } + // Sets base CDN url for all libraries + setBaseUrl(url) { + this.baseUrl = url; + this.updateUrls(); + } - // Removes override for a specific library - removeOverride(lib) { - delete this.overrides[lib]; - this.updateUrls(); - } + // Allows to override CDN url for a specific library + setOverrideUrl(lib, url) { + this.overrides[lib] = url; + this.updateUrls(); + } - // Removes all overrides - removeOverrides() { - this.overrides = {}; - this.updateUrls(); - } + // Removes override for a specific library + removeOverride(lib) { + delete this.overrides[lib]; + this.updateUrls(); + } - buildUrl(cdnUrl, lib, version) { - let url; - if (version === 'latest' || version === '') { - url = `${cdnUrl}/${lib}`; + // Removes all overrides + removeOverrides() { + this.overrides = {}; + this.updateUrls(); } - else { - url = `${cdnUrl}/${lib}/${version}`; + + buildUrl(cdnUrl, lib, version) { + let url; + if (version === 'latest' || version === '') { + url = `${cdnUrl}/${lib}`; + } else { + url = `${cdnUrl}/${lib}/${version}`; + } + return url; } - return url; - } - updateUrls() { - for (const lib in this.libs) { - if (lib in this.overrides) { - this[lib] = this.buildUrl(this.overrides[lib], lib, this.libs[lib]); - } - else { - this[lib] = this.buildUrl(this.baseUrl, lib, this.libs[lib]); - } + updateUrls() { + for (const lib in this.libs) { + if (lib in this.overrides) { + this[lib] = this.buildUrl( + this.overrides[lib], + lib, + this.libs[lib], + ); + } else { + this[lib] = this.buildUrl(this.baseUrl, lib, this.libs[lib]); + } + } } - } } export default CDN; diff --git a/src/CDN.unit.js b/src/CDN.unit.js index 0f3231805f..a622c7f94e 100644 --- a/src/CDN.unit.js +++ b/src/CDN.unit.js @@ -1,44 +1,44 @@ import CDN from './CDN'; import assert from 'power-assert'; -describe('Formio.js CDN class Tests', function() { - let cdn; +describe('Formio.js CDN class Tests', function () { + let cdn; - before(function() { - cdn = new CDN('https://cdn.form.io'); - }); + before(function () { + cdn = new CDN('https://cdn.form.io'); + }); - it('Should give correct CDN URLs', function() { - for (const lib in cdn.libs) { - let expectedUrl = `${cdn.baseUrl}/${lib}/${cdn.libs[lib]}`; - if (cdn.libs[lib] === '' || cdn.libs[lib] === 'latest') { - expectedUrl = `${cdn.baseUrl}/${lib}`; - } - assert.equal(cdn[lib], expectedUrl); - } - }); + it('Should give correct CDN URLs', function () { + for (const lib in cdn.libs) { + let expectedUrl = `${cdn.baseUrl}/${lib}/${cdn.libs[lib]}`; + if (cdn.libs[lib] === '' || cdn.libs[lib] === 'latest') { + expectedUrl = `${cdn.baseUrl}/${lib}`; + } + assert.equal(cdn[lib], expectedUrl); + } + }); - it('Should update lib versions', function() { - cdn.setVersion('grid', '1.1.1'); - assert.equal(cdn.grid, 'https://cdn.form.io/grid/1.1.1'); - }); + it('Should update lib versions', function () { + cdn.setVersion('grid', '1.1.1'); + assert.equal(cdn.grid, 'https://cdn.form.io/grid/1.1.1'); + }); - it('Shoudl override CDN urls', function() { - cdn.setOverrideUrl('grid', 'http://cdn.test-form.io'); - cdn.setVersion('grid', 'latest'); - assert.equal(cdn.grid, 'http://cdn.test-form.io/grid'); + it('Shoudl override CDN urls', function () { + cdn.setOverrideUrl('grid', 'http://cdn.test-form.io'); + cdn.setVersion('grid', 'latest'); + assert.equal(cdn.grid, 'http://cdn.test-form.io/grid'); - cdn.setOverrideUrl('ace', 'http://cdn.test-form.io'); - }); + cdn.setOverrideUrl('ace', 'http://cdn.test-form.io'); + }); - it('Should remove overrides', function() { - cdn.removeOverrides(); - for (const lib in cdn.libs) { - let expectedUrl = `${cdn.baseUrl}/${lib}/${cdn.libs[lib]}`; - if (cdn.libs[lib] === '' || cdn.libs[lib] === 'latest') { - expectedUrl = `${cdn.baseUrl}/${lib}`; - } - assert.equal(cdn[lib], expectedUrl); - } - }); + it('Should remove overrides', function () { + cdn.removeOverrides(); + for (const lib in cdn.libs) { + let expectedUrl = `${cdn.baseUrl}/${lib}/${cdn.libs[lib]}`; + if (cdn.libs[lib] === '' || cdn.libs[lib] === 'latest') { + expectedUrl = `${cdn.baseUrl}/${lib}`; + } + assert.equal(cdn[lib], expectedUrl); + } + }); }); diff --git a/src/Element.js b/src/Element.js index 17c890c388..5ae3215186 100644 --- a/src/Element.js +++ b/src/Element.js @@ -10,628 +10,670 @@ import maskInput from '@formio/vanilla-text-mask'; * The root component for all elements within the Form.io renderer. */ export default class Element { - constructor(options) { + constructor(options) { + /** + * The options for this component. + * @type {{}} + */ + this.options = Object.assign( + { + language: 'en', + highlightErrors: true, + componentErrorClass: 'formio-error-wrapper', + componentWarningClass: 'formio-warning-wrapper', + row: '', + namespace: 'formio', + }, + options || {}, + ); + + /** + * The ID of this component. This value is auto-generated when the component is created, but + * can also be provided from the component.id value passed into the constructor. + * @type {string} + */ + this.id = FormioUtils.getRandomComponentId(); + /** + * An array of event handlers so that the destry command can deregister them. + * @type {Array} + */ + this.eventHandlers = []; + + // Use the i18next that is passed in, otherwise use the global version. + this.options.i18n = this.options.i18n || {}; + if (this.options?.language) { + this.options.i18n.language = this.options.language; + } + this.options.i18next = this.i18next = + this.options.i18next || I18n.init(this.options.i18n); + + /** + * An instance of the EventEmitter class to handle the emitting and registration of events. + * + * @type {EventEmitter} + */ + this.events = + options && options.events ? options.events : new EventEmitter(); + + this.defaultMask = null; + /** + * Conditional to show or hide helplinks in editForm + * + * @type {*|boolean} + */ + this.helplinks = + this.options.helplinks === 'false' + ? false + : this.options.helplinks || 'https://help.form.io'; + } + /** - * The options for this component. - * @type {{}} + * Register for a new event within this component. + * + * @example + * let component = new BaseComponent({ + * type: 'textfield', + * label: 'First Name', + * key: 'firstName' + * }); + * component.on('componentChange', (changed) => { + * console.log('this element is changed.'); + * }); + * + * + * @param {string} event - The event you wish to register the handler for. + * @param {function} cb - The callback handler to handle this event. */ - this.options = Object.assign({ - language: 'en', - highlightErrors: true, - componentErrorClass: 'formio-error-wrapper', - componentWarningClass: 'formio-warning-wrapper', - row: '', - namespace: 'formio' - }, options || {}); + on(event, cb, internal, once = false) { + if (!this.events) { + return; + } + const type = `${this.options.namespace}.${event}`; + + // Store the component id in the handler so that we can determine which events are for this component. + cb.id = this.id; + cb.key = this.key; + cb.internal = internal; + + // Register for this event. + return this.events[once ? 'once' : 'on'](type, cb); + } /** - * The ID of this component. This value is auto-generated when the component is created, but - * can also be provided from the component.id value passed into the constructor. - * @type {string} + * Register for a new single-fire event within this component. + * + * @param {string} event - The event you wish to register the handler for. + * @param {function} cb - The callback handler to handle this event. */ - this.id = FormioUtils.getRandomComponentId(); + once(event, cb, internal) { + return this.on(event, cb, internal, true); + } + /** - * An array of event handlers so that the destry command can deregister them. - * @type {Array} + * Allow catching any event. + * + * @param cb + * @returns {this} */ - this.eventHandlers = []; + onAny(cb) { + if (!this.events) { + return; + } - // Use the i18next that is passed in, otherwise use the global version. - this.options.i18n = this.options.i18n || {}; - if (this.options?.language) { - this.options.i18n.language = this.options.language; + return this.events.onAny(cb); } - this.options.i18next = this.i18next = this.options.i18next || I18n.init(this.options.i18n); /** - * An instance of the EventEmitter class to handle the emitting and registration of events. + * Removes the listener that will be fired when any event is emitted. * - * @type {EventEmitter} + * @param cb + * @returns {this} */ - this.events = (options && options.events) ? options.events : new EventEmitter(); + offAny(cb) { + if (!this.events) { + return; + } + + return this.events.offAny(cb); + } - this.defaultMask = null; /** - * Conditional to show or hide helplinks in editForm + * Removes a listener for a certain event. Not passing the 2nd arg will remove all listeners for that event. * - * @type {*|boolean} + * @param {string} event - The event you wish to register the handler for. + * @param {function|undefined} cb - The callback handler to handle this event. */ - this.helplinks = (this.options.helplinks === 'false') ? false : (this.options.helplinks || 'https://help.form.io'); - } - - /** - * Register for a new event within this component. - * - * @example - * let component = new BaseComponent({ - * type: 'textfield', - * label: 'First Name', - * key: 'firstName' - * }); - * component.on('componentChange', (changed) => { - * console.log('this element is changed.'); - * }); - * - * - * @param {string} event - The event you wish to register the handler for. - * @param {function} cb - The callback handler to handle this event. - */ - on(event, cb, internal, once = false) { - if (!this.events) { - return; - } - const type = `${this.options.namespace}.${event}`; - - // Store the component id in the handler so that we can determine which events are for this component. - cb.id = this.id; - cb.key = this.key; - cb.internal = internal; - - // Register for this event. - return this.events[once ? 'once' : 'on'](type, cb); - } - - /** - * Register for a new single-fire event within this component. - * - * @param {string} event - The event you wish to register the handler for. - * @param {function} cb - The callback handler to handle this event. - */ - once(event, cb, internal) { - return this.on(event, cb, internal, true); - } - - /** - * Allow catching any event. - * - * @param cb - * @returns {this} - */ - onAny(cb) { - if (!this.events) { - return; - } - - return this.events.onAny(cb); - } - - /** - * Removes the listener that will be fired when any event is emitted. - * - * @param cb - * @returns {this} - */ - offAny(cb) { - if (!this.events) { - return; - } - - return this.events.offAny(cb); - } - - /** - * Removes a listener for a certain event. Not passing the 2nd arg will remove all listeners for that event. - * - * @param {string} event - The event you wish to register the handler for. - * @param {function|undefined} cb - The callback handler to handle this event. - */ - off(event, cb) { - if (!this.events) { - return; - } - - const type = `${this.options.namespace}.${event}`; - - this.events.listeners(type).forEach((listener) => { - // Ensure the listener is for this element - if (!listener || listener.id !== this.id) { - return; - } - - // If there is a given callback, only deal with the match - if (cb && cb !== listener) { - return; - } - - this.events.off(type, listener); - }); - } - - /** - * Emit a new event. - * - * @param {string} event - The event to emit. - * @param {Object} data - The data to emit with the handler. - */ - emit(event, ...data) { - if (this.events) { - this.events.emit(`${this.options.namespace}.${event}`, ...data); - } - } - - /** - * Check if the component has an event handler set up for the event. - * - * @param {string} event - The event name. - * @returns {boolean} - */ - hasEventHandler(event) { - if (!this.events) { - return false; - } - - const type = `${this.options.namespace}.${event}`; - - return this.events.listeners(type).some((listener) => { - if (!listener) { - return false; - } - - return listener.id === this.id || listener.key === this.key; - }); - } - - /** - * Wrapper method to add an event listener to an HTML element. - * - * @param obj - * The DOM element to add the event to. - * @param type - * The event name to add. - * @param func - * The callback function to be executed when the listener is triggered. - * @param persistent - * If this listener should persist beyond "destroy" commands. - */ - addEventListener(obj, type, func, persistent, capture) { - if (!obj) { - return; - } - if (!persistent) { - this.eventHandlers.push({ id: this.id, obj, type, func }); - } - if ('addEventListener' in obj) { - obj.addEventListener(type, func, !!capture); - } - else if ('attachEvent' in obj) { - obj.attachEvent(`on${type}`, func); - } - - return this; - } - - /** - * Remove an event listener from the object. - * - * @param obj - * @param type - */ - removeEventListener(obj, type, func = null) { - const indexes = []; - if (!obj) { - return; - } - - this.eventHandlers.forEach((handler, index) => { - if ( - (handler.id === this.id) - && obj.removeEventListener - && (handler.type === type) - && (!func || handler.func === func) - ) { - obj.removeEventListener(type, handler.func); - indexes.push(index); - } - }); - if (indexes.length) { - _.pullAt(this.eventHandlers, indexes); - } - - return this; - } - - removeEventListeners() { - this.eventHandlers.forEach(handler => { - if ((this.id === handler.id) && handler.type && handler.obj && handler.obj.removeEventListener) { - handler.obj.removeEventListener(handler.type, handler.func); - } - }); - this.eventHandlers = []; - } - - removeAllEvents(includeExternal) { - if (this.events) { - _.each(this.events._events, (events, type) => { - _.each(events, (listener) => { - if (listener && (this.id === listener.id) && (includeExternal || listener.internal)) { + off(event, cb) { + if (!this.events) { + return; + } + + const type = `${this.options.namespace}.${event}`; + + this.events.listeners(type).forEach((listener) => { + // Ensure the listener is for this element + if (!listener || listener.id !== this.id) { + return; + } + + // If there is a given callback, only deal with the match + if (cb && cb !== listener) { + return; + } + this.events.off(type, listener); - } }); - }); - } - } - - teardown() { - delete this.i18next; - delete this.events; - } - - /** - * Removes all event listeners attached to this component. - */ - destroy(all = false) { - this.removeEventListeners(); - this.removeAllEvents(); - if (all) { - this.teardown(); - } - } - - /** - * Append an HTML DOM element to a container. - * - * @param element - * @param container - */ - appendTo(element, container) { - container?.appendChild(element); - return this; - } - - /** - * Prepend an HTML DOM element to a container. - * - * @param {HTMLElement} element - The DOM element to prepend. - * @param {HTMLElement} container - The DOM element that is the container of the element getting prepended. - */ - prependTo(element, container) { - if (container) { - if (container.firstChild) { - try { - container.insertBefore(element, container.firstChild); + } + + /** + * Emit a new event. + * + * @param {string} event - The event to emit. + * @param {Object} data - The data to emit with the handler. + */ + emit(event, ...data) { + if (this.events) { + this.events.emit(`${this.options.namespace}.${event}`, ...data); + } + } + + /** + * Check if the component has an event handler set up for the event. + * + * @param {string} event - The event name. + * @returns {boolean} + */ + hasEventHandler(event) { + if (!this.events) { + return false; + } + + const type = `${this.options.namespace}.${event}`; + + return this.events.listeners(type).some((listener) => { + if (!listener) { + return false; + } + + return listener.id === this.id || listener.key === this.key; + }); + } + + /** + * Wrapper method to add an event listener to an HTML element. + * + * @param obj + * The DOM element to add the event to. + * @param type + * The event name to add. + * @param func + * The callback function to be executed when the listener is triggered. + * @param persistent + * If this listener should persist beyond "destroy" commands. + */ + addEventListener(obj, type, func, persistent, capture) { + if (!obj) { + return; } - catch (err) { - console.warn(err); - container.appendChild(element); + if (!persistent) { + this.eventHandlers.push({ id: this.id, obj, type, func }); } - } - else { - container.appendChild(element); - } - } - - return this; - } - - /** - * Removes an HTML DOM element from its bounding container. - * - * @param {HTMLElement} element - The element to remove. - * @param {HTMLElement} container - The DOM element that is the container of the element to remove. - */ - removeChildFrom(element, container) { - if (container && container.contains(element)) { - try { - container.removeChild(element); - } - catch (err) { - console.warn(err); - } - } - - return this; - } - - /** - * Alias for document.createElement. - * - * @param {string} type - The type of element to create - * @param {Object} attr - The element attributes to add to the created element. - * @param {Various} children - Child elements. Can be a DOM Element, string or array of both. - * - * @return {HTMLElement} - The created element. - */ - ce(type, attr, children = null) { - // console.warn('Call to deprecated this.ce(). Dom elements should be created with templates, not manually with ce.'); - // Create the element. - const element = document.createElement(type); - - // Add attributes. - if (attr) { - this.attr(element, attr); - } - - // Append the children. - this.appendChild(element, children); - return element; - } - - /** - * Append different types of children. - * - * @param child - */ - appendChild(element, child) { - if (Array.isArray(child)) { - child.forEach((oneChild) => this.appendChild(element, oneChild)); - } - else if (child instanceof HTMLElement || child instanceof Text) { - element.appendChild(child); - } - else if (child) { - element.appendChild(this.text(child.toString())); - } - - return this; - } - - /** - * Creates a new input mask placeholder. - * @param {HTMLElement} mask - The input mask. - * @returns {string} - The placeholder that will exist within the input as they type. - */ - maskPlaceholder(mask) { - return mask.map((char) => (char instanceof RegExp) ? this.placeholderChar : char).join(''); - } - - get placeholderChar() { - return this.component?.inputMaskPlaceholderChar || '_'; - } - - /** - * Sets the input mask for an input. - * - * @param {HTMLElement} input - The html input to apply the mask to. - * @param {String} inputMask - The input mask to add to this input. - * @param {Boolean} usePlaceholder - Set the mask placeholder on the input. - */ - setInputMask(input, inputMask, usePlaceholder) { - if (input && inputMask) { - const mask = FormioUtils.getInputMask(inputMask, this.placeholderChar); - this.defaultMask = mask; - - try { - //destroy previous mask - if (input.mask) { - input.mask.destroy(); + if ('addEventListener' in obj) { + obj.addEventListener(type, func, !!capture); + } else if ('attachEvent' in obj) { + obj.attachEvent(`on${type}`, func); } - input.mask = maskInput({ - inputElement: input, - mask, - placeholderChar: this.placeholderChar, - shadowRoot: this.root ? this.root.shadowRoot : null + return this; + } + + /** + * Remove an event listener from the object. + * + * @param obj + * @param type + */ + removeEventListener(obj, type, func = null) { + const indexes = []; + if (!obj) { + return; + } + + this.eventHandlers.forEach((handler, index) => { + if ( + handler.id === this.id && + obj.removeEventListener && + handler.type === type && + (!func || handler.func === func) + ) { + obj.removeEventListener(type, handler.func); + indexes.push(index); + } + }); + if (indexes.length) { + _.pullAt(this.eventHandlers, indexes); + } + + return this; + } + + removeEventListeners() { + this.eventHandlers.forEach((handler) => { + if ( + this.id === handler.id && + handler.type && + handler.obj && + handler.obj.removeEventListener + ) { + handler.obj.removeEventListener(handler.type, handler.func); + } }); - } - catch (e) { - // Don't pass error up, to prevent form rejection. - // Internal bug of vanilla-text-mask on iOS (`selectionEnd`); - console.warn(e); - } - if (mask.numeric) { - input.setAttribute('pattern', '\\d*'); - } - if (usePlaceholder) { - input.setAttribute('placeholder', this.maskPlaceholder(mask)); - } - } - } - - /** - * Translate a text using the i18n system. - * - * @param {string|Array} text - The i18n identifier. - * @param {Object} params - The i18n parameters to use for translation. - */ - t(text, ...args) { - return this.i18next ? this.i18next.t(text, ...args): text; - } - - /** - * Alias to create a text node. - * @param text - * @returns {Text} - */ - text(text) { - return document.createTextNode(this.t(text)); - } - - /** - * Adds an object of attributes onto an element. - * @param {HtmlElement} element - The element to add the attributes to. - * @param {Object} attr - The attributes to add to the input element. - */ - attr(element, attr) { - if (!element) { - return; - } - _.each(attr, (value, key) => { - if (typeof value !== 'undefined') { - if (key.indexOf('on') === 0) { - // If this is an event, add a listener. - this.addEventListener(element, key.substr(2).toLowerCase(), value); + this.eventHandlers = []; + } + + removeAllEvents(includeExternal) { + if (this.events) { + _.each(this.events._events, (events, type) => { + _.each(events, (listener) => { + if ( + listener && + this.id === listener.id && + (includeExternal || listener.internal) + ) { + this.events.off(type, listener); + } + }); + }); + } + } + + teardown() { + delete this.i18next; + delete this.events; + } + + /** + * Removes all event listeners attached to this component. + */ + destroy(all = false) { + this.removeEventListeners(); + this.removeAllEvents(); + if (all) { + this.teardown(); + } + } + + /** + * Append an HTML DOM element to a container. + * + * @param element + * @param container + */ + appendTo(element, container) { + container?.appendChild(element); + return this; + } + + /** + * Prepend an HTML DOM element to a container. + * + * @param {HTMLElement} element - The DOM element to prepend. + * @param {HTMLElement} container - The DOM element that is the container of the element getting prepended. + */ + prependTo(element, container) { + if (container) { + if (container.firstChild) { + try { + container.insertBefore(element, container.firstChild); + } catch (err) { + console.warn(err); + container.appendChild(element); + } + } else { + container.appendChild(element); + } + } + + return this; + } + + /** + * Removes an HTML DOM element from its bounding container. + * + * @param {HTMLElement} element - The element to remove. + * @param {HTMLElement} container - The DOM element that is the container of the element to remove. + */ + removeChildFrom(element, container) { + if (container && container.contains(element)) { + try { + container.removeChild(element); + } catch (err) { + console.warn(err); + } } - else { - // Otherwise it is just an attribute. - element.setAttribute(key, value); + + return this; + } + + /** + * Alias for document.createElement. + * + * @param {string} type - The type of element to create + * @param {Object} attr - The element attributes to add to the created element. + * @param {Various} children - Child elements. Can be a DOM Element, string or array of both. + * + * @return {HTMLElement} - The created element. + */ + ce(type, attr, children = null) { + // console.warn('Call to deprecated this.ce(). Dom elements should be created with templates, not manually with ce.'); + // Create the element. + const element = document.createElement(type); + + // Add attributes. + if (attr) { + this.attr(element, attr); } - } - }); - } - - /** - * Determines if an element has a class. - * - * Taken from jQuery https://j11y.io/jquery/#v=1.5.0&fn=jQuery.fn.hasClass - */ - hasClass(element, className) { - if (!element) { - return false; - } - // Allow templates to intercept. - className = ` ${className} `; - return ((` ${element.className} `).replace(/[\n\t\r]/g, ' ').indexOf(className) > -1); - } - - /** - * Adds a class to a DOM element. - * - * @param element - * The element to add a class to. - * @param className - * The name of the class to add. - */ - addClass(element, className) { - if (!element || !(element instanceof HTMLElement)) { - return this; - } - // Allow templates to intercept. - const classes = element.getAttribute('class'); - if (!classes?.includes(className)) { - element.setAttribute('class', `${classes} ${className}`); - } - - return this; - } - - /** - * Remove a class from a DOM element. - * - * @param element - * The DOM element to remove the class from. - * @param className - * The name of the class that is to be removed. - */ - removeClass(element, className) { - if (!element || !className || !(element instanceof HTMLElement)) { - return this; - } - // Allow templates to intercept. - let cls = element.getAttribute('class'); - if (cls) { - cls = cls.replace(new RegExp(` ${className}`, 'g'), ''); - element.setAttribute('class', cls); - } - - return this; - } - - /** - * Empty's an HTML DOM element. - * - * @param {HTMLElement} element - The element you wish to empty. - */ - empty(element) { - if (element) { - while (element.firstChild) { - element.removeChild(element.firstChild); - } - } - } - - /** - * Create an evaluation context for all script executions and interpolations. - * - * @param additional - * @return {*} - */ - evalContext(additional) { - return Object.assign({ - _, - utils: FormioUtils, - util: FormioUtils, - user: Formio.getUser(), - moment, - instance: this, - self: this, - token: Formio.getToken({ - decode: true - }), - options: this.options, - config: this.root && this.root.form && this.root.form.config - ? this.root.form.config - : this.options?.formConfig - ? this.options.formConfig - : {}, - }, additional, _.get(this.root, 'options.evalContext', {})); - } - - /** - * Performs an interpolation using the evaluation context of this component. - * - * @param string - * @param data - * @return {XML|string|*|void} - */ - interpolate(string, data, options = {}) { - if (typeof string !== 'function' && (this.component.content || this.component.html) - && !FormioUtils.Evaluator.templateSettings.interpolate.test(string)) { - string = FormioUtils.translateHTMLTemplate(String(string), (value) => this.t(value)); - } - - if (this.component.filter === string && !this.options.building) { - const evalContext = this.evalContext(data); - evalContext.data = _.mapValues(evalContext.data, (val) => _.isString(val) ? encodeURIComponent(val) : val); - return FormioUtils.interpolate(string, evalContext, options); - } - return FormioUtils.interpolate(string, this.evalContext(data), options); - } - - /** - * Performs an evaluation using the evaluation context of this component. - * - * @param func - * @param args - * @param ret - * @param tokenize - * @return {*} - */ - evaluate(func, args, ret, tokenize) { - return FormioUtils.evaluate(func, this.evalContext(args), ret, tokenize); - } - - /** - * Allow for options to hook into the functionality of this renderer. - * @return {*} - */ - hook() { - const name = arguments[0]; - if ( - this.options && - this.options.hooks && - this.options.hooks[name] - ) { - return this.options.hooks[name].apply(this, Array.prototype.slice.call(arguments, 1)); - } - else { - // If this is an async hook instead of a sync. - const fn = (typeof arguments[arguments.length - 1] === 'function') ? arguments[arguments.length - 1] : null; - if (fn) { - return fn(null, arguments[1]); - } - else { - return arguments[1]; - } - } - } + + // Append the children. + this.appendChild(element, children); + return element; + } + + /** + * Append different types of children. + * + * @param child + */ + appendChild(element, child) { + if (Array.isArray(child)) { + child.forEach((oneChild) => this.appendChild(element, oneChild)); + } else if (child instanceof HTMLElement || child instanceof Text) { + element.appendChild(child); + } else if (child) { + element.appendChild(this.text(child.toString())); + } + + return this; + } + + /** + * Creates a new input mask placeholder. + * @param {HTMLElement} mask - The input mask. + * @returns {string} - The placeholder that will exist within the input as they type. + */ + maskPlaceholder(mask) { + return mask + .map((char) => + char instanceof RegExp ? this.placeholderChar : char, + ) + .join(''); + } + + get placeholderChar() { + return this.component?.inputMaskPlaceholderChar || '_'; + } + + /** + * Sets the input mask for an input. + * + * @param {HTMLElement} input - The html input to apply the mask to. + * @param {String} inputMask - The input mask to add to this input. + * @param {Boolean} usePlaceholder - Set the mask placeholder on the input. + */ + setInputMask(input, inputMask, usePlaceholder) { + if (input && inputMask) { + const mask = FormioUtils.getInputMask( + inputMask, + this.placeholderChar, + ); + this.defaultMask = mask; + + try { + //destroy previous mask + if (input.mask) { + input.mask.destroy(); + } + + input.mask = maskInput({ + inputElement: input, + mask, + placeholderChar: this.placeholderChar, + shadowRoot: this.root ? this.root.shadowRoot : null, + }); + } catch (e) { + // Don't pass error up, to prevent form rejection. + // Internal bug of vanilla-text-mask on iOS (`selectionEnd`); + console.warn(e); + } + if (mask.numeric) { + input.setAttribute('pattern', '\\d*'); + } + if (usePlaceholder) { + input.setAttribute('placeholder', this.maskPlaceholder(mask)); + } + } + } + + /** + * Translate a text using the i18n system. + * + * @param {string|Array} text - The i18n identifier. + * @param {Object} params - The i18n parameters to use for translation. + */ + t(text, ...args) { + return this.i18next ? this.i18next.t(text, ...args) : text; + } + + /** + * Alias to create a text node. + * @param text + * @returns {Text} + */ + text(text) { + return document.createTextNode(this.t(text)); + } + + /** + * Adds an object of attributes onto an element. + * @param {HtmlElement} element - The element to add the attributes to. + * @param {Object} attr - The attributes to add to the input element. + */ + attr(element, attr) { + if (!element) { + return; + } + _.each(attr, (value, key) => { + if (typeof value !== 'undefined') { + if (key.indexOf('on') === 0) { + // If this is an event, add a listener. + this.addEventListener( + element, + key.substr(2).toLowerCase(), + value, + ); + } else { + // Otherwise it is just an attribute. + element.setAttribute(key, value); + } + } + }); + } + + /** + * Determines if an element has a class. + * + * Taken from jQuery https://j11y.io/jquery/#v=1.5.0&fn=jQuery.fn.hasClass + */ + hasClass(element, className) { + if (!element) { + return false; + } + // Allow templates to intercept. + className = ` ${className} `; + return ( + ` ${element.className} ` + .replace(/[\n\t\r]/g, ' ') + .indexOf(className) > -1 + ); + } + + /** + * Adds a class to a DOM element. + * + * @param element + * The element to add a class to. + * @param className + * The name of the class to add. + */ + addClass(element, className) { + if (!element || !(element instanceof HTMLElement)) { + return this; + } + // Allow templates to intercept. + const classes = element.getAttribute('class'); + if (!classes?.includes(className)) { + element.setAttribute('class', `${classes} ${className}`); + } + + return this; + } + + /** + * Remove a class from a DOM element. + * + * @param element + * The DOM element to remove the class from. + * @param className + * The name of the class that is to be removed. + */ + removeClass(element, className) { + if (!element || !className || !(element instanceof HTMLElement)) { + return this; + } + // Allow templates to intercept. + let cls = element.getAttribute('class'); + if (cls) { + cls = cls.replace(new RegExp(` ${className}`, 'g'), ''); + element.setAttribute('class', cls); + } + + return this; + } + + /** + * Empty's an HTML DOM element. + * + * @param {HTMLElement} element - The element you wish to empty. + */ + empty(element) { + if (element) { + while (element.firstChild) { + element.removeChild(element.firstChild); + } + } + } + + /** + * Create an evaluation context for all script executions and interpolations. + * + * @param additional + * @return {*} + */ + evalContext(additional) { + return Object.assign( + { + _, + utils: FormioUtils, + util: FormioUtils, + user: Formio.getUser(), + moment, + instance: this, + self: this, + token: Formio.getToken({ + decode: true, + }), + options: this.options, + config: + this.root && this.root.form && this.root.form.config + ? this.root.form.config + : this.options?.formConfig + ? this.options.formConfig + : {}, + }, + additional, + _.get(this.root, 'options.evalContext', {}), + ); + } + + /** + * Performs an interpolation using the evaluation context of this component. + * + * @param string + * @param data + * @return {XML|string|*|void} + */ + interpolate(string, data, options = {}) { + if ( + typeof string !== 'function' && + (this.component.content || this.component.html) && + !FormioUtils.Evaluator.templateSettings.interpolate.test(string) + ) { + string = FormioUtils.translateHTMLTemplate( + String(string), + (value) => this.t(value), + ); + } + + if (this.component.filter === string && !this.options.building) { + const evalContext = this.evalContext(data); + evalContext.data = _.mapValues(evalContext.data, (val) => + _.isString(val) ? encodeURIComponent(val) : val, + ); + return FormioUtils.interpolate(string, evalContext, options); + } + return FormioUtils.interpolate(string, this.evalContext(data), options); + } + + /** + * Performs an evaluation using the evaluation context of this component. + * + * @param func + * @param args + * @param ret + * @param tokenize + * @return {*} + */ + evaluate(func, args, ret, tokenize) { + return FormioUtils.evaluate( + func, + this.evalContext(args), + ret, + tokenize, + ); + } + + /** + * Allow for options to hook into the functionality of this renderer. + * @return {*} + */ + hook() { + const name = arguments[0]; + if (this.options && this.options.hooks && this.options.hooks[name]) { + return this.options.hooks[name].apply( + this, + Array.prototype.slice.call(arguments, 1), + ); + } else { + // If this is an async hook instead of a sync. + const fn = + typeof arguments[arguments.length - 1] === 'function' + ? arguments[arguments.length - 1] + : null; + if (fn) { + return fn(null, arguments[1]); + } else { + return arguments[1]; + } + } + } } diff --git a/src/Embed.js b/src/Embed.js index 97908a1f34..b65f92262b 100644 --- a/src/Embed.js +++ b/src/Embed.js @@ -37,11 +37,13 @@ export class Formio { static createElement(type, attrs, children) { const element = document.createElement(type); - Object.keys(attrs).forEach(key => { + Object.keys(attrs).forEach((key) => { element.setAttribute(key, attrs[key]); }); - (children || []).forEach(child => { - element.appendChild(Formio.createElement(child.tag, child.attrs, child.children)); + (children || []).forEach((child) => { + element.appendChild( + Formio.createElement(child.tag, child.attrs, child.children), + ); }); return element; } @@ -51,16 +53,20 @@ export class Formio { return Promise.resolve(); } if (typeof src !== 'string' && src.length) { - return Promise.all(src.map(ref => Formio.addScript(wrapper, ref))); + return Promise.all( + src.map((ref) => Formio.addScript(wrapper, ref)), + ); } if (name && Formio.global(name)) { Formio.debug(`${name} already loaded.`); return Promise.resolve(Formio.global(name)); } Formio.debug('Adding Script', src); - wrapper.appendChild(Formio.createElement('script', { - src - })); + wrapper.appendChild( + Formio.createElement('script', { + src, + }), + ); if (!name) { return Promise.resolve(); } @@ -81,20 +87,26 @@ export class Formio { return; } if (typeof href !== 'string' && href.length) { - href.forEach(ref => Formio.addStyles(wrapper, ref)); + href.forEach((ref) => Formio.addStyles(wrapper, ref)); return; } Formio.debug('Adding Styles', href); - wrapper.appendChild(Formio.createElement('link', { - rel: 'stylesheet', - href - })); + wrapper.appendChild( + Formio.createElement('link', { + rel: 'stylesheet', + href, + }), + ); } static async submitDone(instance, submission) { Formio.debug('Submision Complete', submission); const successMessage = (Formio.config.success || '').toString(); - if (successMessage && successMessage.toLowerCase() !== 'false' && instance.element) { + if ( + successMessage && + successMessage.toLowerCase() !== 'false' && + instance.element + ) { instance.element.innerHTML = ``; } let returnUrl = Formio.config.redirect; @@ -102,17 +114,15 @@ export class Formio { // Allow form based configuration for return url. if ( !returnUrl && - ( - instance._form && - instance._form.settings && - ( - instance._form.settings.returnUrl || - instance._form.settings.redirect - ) - ) + instance._form && + instance._form.settings && + (instance._form.settings.returnUrl || + instance._form.settings.redirect) ) { Formio.debug('Return url found in form configuration'); - returnUrl = instance._form.settings.returnUrl || instance._form.settings.redirect; + returnUrl = + instance._form.settings.returnUrl || + instance._form.settings.redirect; } if (returnUrl) { @@ -151,24 +161,30 @@ export class Formio { css: `${Formio.cdn.uswds}/uswds.min.css`, }, fontawesome: { - css: `${Formio.cdn['font-awesome']}/css/font-awesome.min.css` + css: `${Formio.cdn['font-awesome']}/css/font-awesome.min.css`, }, bootstrap: { - css: `${Formio.cdn.bootstrap}/css/bootstrap.min.css` - } + css: `${Formio.cdn.bootstrap}/css/bootstrap.min.css`, + }, }; - const id = Formio.config.id || `formio-${Math.random().toString(36).substring(7)}`; + const id = + Formio.config.id || + `formio-${Math.random().toString(36).substring(7)}`; // Create a new wrapper and add the element inside of a new wrapper. let wrapper = Formio.createElement('div', { - 'id': `"${id}-wrapper"` + id: `"${id}-wrapper"`, }); element.parentNode.insertBefore(wrapper, element); // If we include the libraries, then we will attempt to run this in shadow dom. - if (Formio.config.includeLibs && (typeof wrapper.attachShadow === 'function') && !Formio.config.premium) { + if ( + Formio.config.includeLibs && + typeof wrapper.attachShadow === 'function' && + !Formio.config.premium + ) { wrapper = wrapper.attachShadow({ - mode: 'open' + mode: 'open', }); options.shadowRoot = wrapper; } @@ -177,31 +193,49 @@ export class Formio { wrapper.appendChild(element); // Load the renderer styles. - await Formio.addStyles(wrapper, Formio.config.embedCSS || `${Formio.cdn.js}/formio.embed.css`); + await Formio.addStyles( + wrapper, + Formio.config.embedCSS || `${Formio.cdn.js}/formio.embed.css`, + ); // Add a loader. - wrapper.appendChild(Formio.createElement('div', { - 'class': 'formio-loader' - }, [{ - tag: 'div', - attrs: { - class: 'loader-wrapper' - }, - children: [{ - tag: 'div', - attrs: { - class: 'loader text-center' - } - }] - }])); + wrapper.appendChild( + Formio.createElement( + 'div', + { + class: 'formio-loader', + }, + [ + { + tag: 'div', + attrs: { + class: 'loader-wrapper', + }, + children: [ + { + tag: 'div', + attrs: { + class: 'loader text-center', + }, + }, + ], + }, + ], + ), + ); Formio.FormioClass = await Formio.addScript( wrapper, - Formio.formioScript(Formio.config.script || `${Formio.cdn.js}/formio.form.min.js`, builder), - 'Formio' + Formio.formioScript( + Formio.config.script || `${Formio.cdn.js}/formio.form.min.js`, + builder, + ), + 'Formio', ); Formio.FormioClass.setBaseUrl(Formio.baseUrl || Formio.config.base); - Formio.FormioClass.setProjectUrl(Formio.projectUrl || Formio.config.project); + Formio.FormioClass.setProjectUrl( + Formio.projectUrl || Formio.config.project, + ); Formio.FormioClass.language = Formio.language; // Add premium modules @@ -217,21 +251,37 @@ export class Formio { if (Formio.config.template) { if (Formio.config.includeLibs) { - await Formio.addStyles(wrapper, Formio.config.libs[Formio.config.template].css); - await Formio.addScript(wrapper, Formio.config.libs[Formio.config.template].js); + await Formio.addStyles( + wrapper, + Formio.config.libs[Formio.config.template].css, + ); + await Formio.addScript( + wrapper, + Formio.config.libs[Formio.config.template].js, + ); if (Formio.config.libs[Formio.config.template].fa) { - await Formio.addStyles(wrapper, Formio.config.libs.fontawesome.css); + await Formio.addStyles( + wrapper, + Formio.config.libs.fontawesome.css, + ); } } if (Formio.cdn[Formio.config.template]) { - const templateSrc = `${Formio.cdn[Formio.config.template]}/${Formio.config.template}.min`; + const templateSrc = `${Formio.cdn[Formio.config.template]}/${ + Formio.config.template + }.min`; await Formio.addStyles(wrapper, `${templateSrc}.css`); Formio.debug(`Using ${Formio.config.template}`); - Formio.FormioClass.use(await Formio.addScript(wrapper, `${templateSrc}.js`, Formio.config.template)); + Formio.FormioClass.use( + await Formio.addScript( + wrapper, + `${templateSrc}.js`, + Formio.config.template, + ), + ); } - } - else if (Formio.global('uswds')) { + } else if (Formio.global('uswds')) { Formio.debug('Using uswds module.'); Formio.FormioClass.use(Formio.global('uswds')); } @@ -243,12 +293,28 @@ export class Formio { if (Formio.config.premium) { await Formio.addStyles(wrapper, Formio.config.premium.css); Formio.debug('Using premium'); - Formio.FormioClass.use(await Formio.addScript(wrapper, Formio.config.premium.js, 'premium')); + Formio.FormioClass.use( + await Formio.addScript( + wrapper, + Formio.config.premium.js, + 'premium', + ), + ); } - await Formio.addStyles(wrapper, Formio.formioScript(Formio.config.style || `${Formio.cdn.js}/formio.form.min.css`, builder)); + await Formio.addStyles( + wrapper, + Formio.formioScript( + Formio.config.style || `${Formio.cdn.js}/formio.form.min.css`, + builder, + ), + ); if (Formio.config.before) { - await Formio.config.before(Formio.FormioClass, element, Formio.config); + await Formio.config.before( + Formio.FormioClass, + element, + Formio.config, + ); } Formio.FormioClass.license = true; return wrapper; @@ -258,7 +324,7 @@ export class Formio { const wrapper = await Formio.init(element, options); return Formio.FormioClass.createForm(element, form, { ...options, - ...{ noLoader: true } + ...{ noLoader: true }, }).then((instance) => { Formio.debug('Form created', instance); @@ -288,18 +354,20 @@ export class Formio { static async builder(element, form, options) { const wrapper = await Formio.init(element, options, true); - return Formio.FormioClass.builder(element, form, options).then((instance) => { - Formio.debug('Builder created', instance); - Formio.debug('Removing loader'); - wrapper.removeChild(wrapper.querySelector('.formio-loader')); - Formio.debug('Triggering embed event'); - Formio.FormioClass.events.emit('builderEmbedded', instance); - if (Formio.config.after) { - Formio.debug('Calling ready callback'); - Formio.config.after(instance, Formio.config); - } - return instance; - }); + return Formio.FormioClass.builder(element, form, options).then( + (instance) => { + Formio.debug('Builder created', instance); + Formio.debug('Removing loader'); + wrapper.removeChild(wrapper.querySelector('.formio-loader')); + Formio.debug('Triggering embed event'); + Formio.FormioClass.events.emit('builderEmbedded', instance); + if (Formio.config.after) { + Formio.debug('Calling ready callback'); + Formio.config.after(instance, Formio.config); + } + return instance; + }, + ); } } @@ -312,7 +380,7 @@ export class Form { this.instance = { proxy: true, ready: this.ready, - destroy: () => {} + destroy: () => {}, }; } diff --git a/src/EventEmitter.js b/src/EventEmitter.js index fea504df48..5aaa3f940a 100644 --- a/src/EventEmitter.js +++ b/src/EventEmitter.js @@ -1,32 +1,35 @@ import { EventEmitter as EventEmitter3 } from 'eventemitter3'; import * as utils from './utils/utils'; export default class EventEmitter extends EventEmitter3 { - constructor(conf = {}) { - const { loadLimit = 1000, eventsSafeInterval = 300 } = conf; - super(); + constructor(conf = {}) { + const { loadLimit = 1000, eventsSafeInterval = 300 } = conf; + super(); - const overloadHandler = () => { - console.warn(`There were more than ${loadLimit} events emitted in ${eventsSafeInterval} ms. It might be caused by events' infinite loop`, this.id); - }; + const overloadHandler = () => { + console.warn( + `There were more than ${loadLimit} events emitted in ${eventsSafeInterval} ms. It might be caused by events' infinite loop`, + this.id, + ); + }; - const dispatch = utils.observeOverload(overloadHandler, { - limit: loadLimit, - delay: eventsSafeInterval - }); + const dispatch = utils.observeOverload(overloadHandler, { + limit: loadLimit, + delay: eventsSafeInterval, + }); - this.emit = (...args) => { - super.emit(...args); - super.emit('any', ...args); + this.emit = (...args) => { + super.emit(...args); + super.emit('any', ...args); - dispatch(); - }; - } + dispatch(); + }; + } - onAny = (fn) => { - this.on('any', fn); - }; + onAny = (fn) => { + this.on('any', fn); + }; - offAny = (fn) => { - this.off('any', fn); - }; + offAny = (fn) => { + this.off('any', fn); + }; } diff --git a/src/Form.js b/src/Form.js index ea1476927d..3ae3e7ab1d 100644 --- a/src/Form.js +++ b/src/Form.js @@ -5,360 +5,378 @@ import templates from './templates'; import * as FormioUtils from './utils/utils'; export default class Form extends Element { - /** - * Creates an easy to use interface for embedding webforms, pdfs, and wizards into your application. - * - * @param {Object} element - The DOM element you wish to render this form within. - * @param {Object | string} form - Either a Form JSON schema or the URL of a hosted form via. form.io. - * @param {Object} options - The options to create a new form instance. - * @param {boolean} options.readOnly - Set this form to readOnly - * @param {boolean} options.noAlerts - Set to true to disable the alerts dialog. - * @param {boolean} options.i18n - The translation file for this rendering. @see https://github.com/formio/formio.js/blob/master/i18n.js - * @param {boolean} options.template - Provides a way to inject custom logic into the creation of every element rendered within the form. - * - * @example - * import Form from '@formio/js/Form'; - * const form = new Form(document.getElementById('formio'), 'https://examples.form.io/example'); - * form.build(); - */ - constructor(...args) { - let options = args[0] instanceof HTMLElement ? args[2] : args[1]; - if (Formio.options && Formio.options.form) { - options = Object.assign(options, Formio.options.form); - } + /** + * Creates an easy to use interface for embedding webforms, pdfs, and wizards into your application. + * + * @param {Object} element - The DOM element you wish to render this form within. + * @param {Object | string} form - Either a Form JSON schema or the URL of a hosted form via. form.io. + * @param {Object} options - The options to create a new form instance. + * @param {boolean} options.readOnly - Set this form to readOnly + * @param {boolean} options.noAlerts - Set to true to disable the alerts dialog. + * @param {boolean} options.i18n - The translation file for this rendering. @see https://github.com/formio/formio.js/blob/master/i18n.js + * @param {boolean} options.template - Provides a way to inject custom logic into the creation of every element rendered within the form. + * + * @example + * import Form from '@formio/js/Form'; + * const form = new Form(document.getElementById('formio'), 'https://examples.form.io/example'); + * form.build(); + */ + constructor(...args) { + let options = args[0] instanceof HTMLElement ? args[2] : args[1]; + if (Formio.options && Formio.options.form) { + options = Object.assign(options, Formio.options.form); + } - super(options); + super(options); - if (this.options.useSessionToken) { - Formio.useSessionToken(this.options); - } + if (this.options.useSessionToken) { + Formio.useSessionToken(this.options); + } - this.ready = new Promise((resolve, reject) => { - this.readyResolve = resolve; - this.readyReject = reject; - }); + this.ready = new Promise((resolve, reject) => { + this.readyResolve = resolve; + this.readyReject = reject; + }); - this.instance = null; - if (args[0] instanceof HTMLElement) { - if (this.element) { - delete this.element.component; - } - this.element = args[0]; - this.options = args[2] || {}; - this.options.events = this.events; - this.setForm(args[1]) - .then(() => this.readyResolve(this.instance)) - .catch(this.readyReject); - } - else if (args[0]) { - this.element = null; - this.options = args[1] || {}; - this.options.events = this.events; - this.setForm(args[0]) - .then(() => this.readyResolve(this.instance)) - .catch(this.readyReject); - } - else { - this.element = null; - this.options = {}; - this.options.events = this.events; + this.instance = null; + if (args[0] instanceof HTMLElement) { + if (this.element) { + delete this.element.component; + } + this.element = args[0]; + this.options = args[2] || {}; + this.options.events = this.events; + this.setForm(args[1]) + .then(() => this.readyResolve(this.instance)) + .catch(this.readyReject); + } else if (args[0]) { + this.element = null; + this.options = args[1] || {}; + this.options.events = this.events; + this.setForm(args[0]) + .then(() => this.readyResolve(this.instance)) + .catch(this.readyReject); + } else { + this.element = null; + this.options = {}; + this.options.events = this.events; + } + this.display = ''; } - this.display = ''; - } - createElement(tag, attrs, children) { - const element = document.createElement(tag); - for (const attr in attrs) { - if (Object.prototype.hasOwnProperty.call(attrs, attr)) { - element.setAttribute(attr, attrs[attr]); - } + createElement(tag, attrs, children) { + const element = document.createElement(tag); + for (const attr in attrs) { + if (Object.prototype.hasOwnProperty.call(attrs, attr)) { + element.setAttribute(attr, attrs[attr]); + } + } + (children || []).forEach((child) => { + element.appendChild( + this.createElement(child.tag, child.attrs, child.children), + ); + }); + return element; } - (children || []).forEach(child => { - element.appendChild(this.createElement(child.tag, child.attrs, child.children)); - }); - return element; - } - set loading(load) { - if (!this.element || this.options.noLoader) { - return; - } - if (load) { - if (this.loader) { - return; - } - this.loader = this.createElement('div', { - 'class': 'formio-loader' - }, [{ - tag: 'div', - attrs: { - class: 'loader-wrapper' - }, - children: [{ - tag: 'div', - attrs: { - class: 'loader text-center' - } - }] - }]); - this.element.appendChild(this.loader); - } - else if (this.loader) { - this.element.removeChild(this.loader); - this.loader = null; + set loading(load) { + if (!this.element || this.options.noLoader) { + return; + } + if (load) { + if (this.loader) { + return; + } + this.loader = this.createElement( + 'div', + { + class: 'formio-loader', + }, + [ + { + tag: 'div', + attrs: { + class: 'loader-wrapper', + }, + children: [ + { + tag: 'div', + attrs: { + class: 'loader text-center', + }, + }, + ], + }, + ], + ); + this.element.appendChild(this.loader); + } else if (this.loader) { + this.element.removeChild(this.loader); + this.loader = null; + } } - } - /** - * Create a new form instance provided the display of the form. - * - * @param {string} display - The display of the form, either "wizard", "form", or "pdf" - * @return {*} - */ - create(display) { - if (this.options && (this.options.flatten || this.options.renderMode === 'flat')) { - display = 'form'; - } - this.display = display; - if (Displays.displays[display]) { - return new Displays.displays[display](this.element, this.options); + /** + * Create a new form instance provided the display of the form. + * + * @param {string} display - The display of the form, either "wizard", "form", or "pdf" + * @return {*} + */ + create(display) { + if ( + this.options && + (this.options.flatten || this.options.renderMode === 'flat') + ) { + display = 'form'; + } + this.display = display; + if (Displays.displays[display]) { + return new Displays.displays[display](this.element, this.options); + } else { + // eslint-disable-next-line new-cap + return new Displays.displays['webform'](this.element, this.options); + } } - else { - // eslint-disable-next-line new-cap - return new Displays.displays['webform'](this.element, this.options); + + /** + * Sets the form. Either as JSON or a URL to a form JSON schema. + * + * @param {string|object} formParam - Either the form JSON or the URL of the form json. + * @return {*} + */ + set form(formParam) { + this.setForm(formParam); } - } - /** - * Sets the form. Either as JSON or a URL to a form JSON schema. - * - * @param {string|object} formParam - Either the form JSON or the URL of the form json. - * @return {*} - */ - set form(formParam) { - this.setForm(formParam); - } + errorForm(err) { + return { + components: [ + { + label: 'HTML', + tag: 'div', + className: + 'error error-message alert alert-danger ui red message', + attrs: [ + { + attr: 'role', + value: 'alert', + }, + ], + key: 'errorMessage', + type: 'htmlelement', + input: false, + content: typeof err === 'string' ? err : err.message, + }, + ], + }; + } - errorForm(err) { - return { - components: [ - { - 'label': 'HTML', - 'tag': 'div', - 'className': 'error error-message alert alert-danger ui red message', - 'attrs': [ - { - 'attr': 'role', - 'value': 'alert' - } - ], - 'key': 'errorMessage', - 'type': 'htmlelement', - 'input': false, - 'content': typeof err === 'string' ? err : err.message, + setForm(formParam) { + let result; + formParam = formParam || this.form; + if (typeof formParam === 'string') { + const formio = new Formio(formParam); + let error; + this.loading = true; + result = this.getSubmission(formio, this.options) + .catch((err) => { + error = err; + }) + .then((submission) => { + return ( + formio + .loadForm() + // If the form returned an error, show it instead of the form. + .catch((err) => { + error = err; + }) + .then((form) => { + // If the submission returned an error, show it instead of the form. + if (error) { + form = this.errorForm(error); + } + this.loading = false; + this.instance = + this.instance || this.create(form.display); + this.instance.url = formParam; + this.instance.nosubmit = false; + this._form = this.instance.form = form; + if (submission) { + this.instance.submission = submission; + } + if (error) { + throw error; + } + return this.instance; + }) + ); + }); + } else { + this.instance = this.instance || this.create(formParam.display); + this._form = this.instance.form = formParam; + result = this.instance.ready; } - ] - }; - } - setForm(formParam) { - let result; - formParam = formParam || this.form; - if (typeof formParam === 'string') { - const formio = new Formio(formParam); - let error; - this.loading = true; - result = this.getSubmission(formio, this.options) - .catch((err) => { - error = err; - }) - .then((submission) => { - return formio.loadForm() - // If the form returned an error, show it instead of the form. - .catch(err => { - error = err; - }) - .then((form) => { - // If the submission returned an error, show it instead of the form. - if (error) { - form = this.errorForm(error); - } - this.loading = false; - this.instance = this.instance || this.create(form.display); - this.instance.url = formParam; - this.instance.nosubmit = false; - this._form = this.instance.form = form; - if (submission) { - this.instance.submission = submission; - } - if (error) { - throw error; - } - return this.instance; - }); + // A redraw has occurred so save off the new element in case of a setDisplay causing a rebuild. + return result.then(() => { + if (this.element) { + delete this.element.component; + } + this.element = this.instance.element; + return this.instance; }); } - else { - this.instance = this.instance || this.create(formParam.display); - this._form = this.instance.form = formParam; - result = this.instance.ready; - } - - // A redraw has occurred so save off the new element in case of a setDisplay causing a rebuild. - return result.then(() => { - if (this.element) { - delete this.element.component; - } - this.element = this.instance.element; - return this.instance; - }); - } - getSubmission(formio, opts) { - if (formio.submissionId) { - return formio.loadSubmission(null, opts); + getSubmission(formio, opts) { + if (formio.submissionId) { + return formio.loadSubmission(null, opts); + } + return Promise.resolve(); } - return Promise.resolve(); - } - - /** - * Returns the loaded forms JSON. - * - * @return {object} - The loaded form's JSON - */ - get form() { - return this._form; - } - /** - * Changes the display of the form. - * - * @param {string} display - The display to set this form. Either "wizard", "form", or "pdf" - * @return {Promise} - */ - setDisplay(display) { - if ((this.display === display) && this.instance) { - return Promise.resolve(this.instance); + /** + * Returns the loaded forms JSON. + * + * @return {object} - The loaded form's JSON + */ + get form() { + return this._form; } - this.form.display = display; - this.instance.destroy(); - this.instance = this.create(display); - return this.setForm(this.form); - } + /** + * Changes the display of the form. + * + * @param {string} display - The display to set this form. Either "wizard", "form", or "pdf" + * @return {Promise} + */ + setDisplay(display) { + if (this.display === display && this.instance) { + return Promise.resolve(this.instance); + } - empty() { - if (this.element) { - while (this.element.firstChild) { - this.element.removeChild(this.element.firstChild); - } + this.form.display = display; + this.instance.destroy(); + this.instance = this.create(display); + return this.setForm(this.form); } - } - static embed(embed) { - return new Promise((resolve) => { - if (!embed || !embed.src) { - resolve(); - } - const id = this.id || `formio-${Math.random().toString(36).substring(7)}`; - const className = embed.class || 'formio-form-wrapper'; - let code = embed.styles ? `` : ''; - code += `
`; - document.write(code); - let attempts = 0; - const wait = setInterval(() => { - attempts++; - const formElement = document.getElementById(id); - if (formElement || attempts > 10) { - resolve(new Form(formElement, embed.src).ready); - clearInterval(wait); + empty() { + if (this.element) { + while (this.element.firstChild) { + this.element.removeChild(this.element.firstChild); + } } - }, 10); - }); - } - - /** - * Sanitize an html string. - * - * @param string - * @returns {*} - */ - sanitize(dirty, forceSanitize) { - // If Sanitize is turned off - if (this.options.sanitize === false && !forceSanitize) { - return dirty; } - return FormioUtils.sanitize(dirty, this.options); - } - setContent(element, content, forceSanitize) { - if (element instanceof HTMLElement) { - element.innerHTML = this.sanitize(content, forceSanitize); - return true; + static embed(embed) { + return new Promise((resolve) => { + if (!embed || !embed.src) { + resolve(); + } + const id = + this.id || `formio-${Math.random().toString(36).substring(7)}`; + const className = embed.class || 'formio-form-wrapper'; + let code = embed.styles + ? `` + : ''; + code += `
`; + document.write(code); + let attempts = 0; + const wait = setInterval(() => { + attempts++; + const formElement = document.getElementById(id); + if (formElement || attempts > 10) { + resolve(new Form(formElement, embed.src).ready); + clearInterval(wait); + } + }, 10); + }); } - return false; - } - /** - * Build a new form. - * - * @return {Promise} - */ - build() { - if (!this.instance) { - return Promise.reject('Form not ready. Use form.ready promise'); + /** + * Sanitize an html string. + * + * @param string + * @returns {*} + */ + sanitize(dirty, forceSanitize) { + // If Sanitize is turned off + if (this.options.sanitize === false && !forceSanitize) { + return dirty; + } + return FormioUtils.sanitize(dirty, this.options); } - if (!this.element) { - return Promise.reject('No DOM element for form.'); + setContent(element, content, forceSanitize) { + if (element instanceof HTMLElement) { + element.innerHTML = this.sanitize(content, forceSanitize); + return true; + } + return false; } - // Add temporary loader. - const template = (this.options && this.options.template) ? this.options.template : 'bootstrap'; - const loader = templates[template].loader || templates.bootstrap.loader; - this.setContent(this.element, loader.form); + /** + * Build a new form. + * + * @return {Promise} + */ + build() { + if (!this.instance) { + return Promise.reject('Form not ready. Use form.ready promise'); + } + + if (!this.element) { + return Promise.reject('No DOM element for form.'); + } - return this.render().then(html => { - this.setContent(this.element, html); - return this.attach(this.element).then(() => this.instance); - }) - .then((param) => { - this.emit('build', param); - return param; - }); - } + // Add temporary loader. + const template = + this.options && this.options.template + ? this.options.template + : 'bootstrap'; + const loader = templates[template].loader || templates.bootstrap.loader; + this.setContent(this.element, loader.form); - render() { - if (!this.instance) { - return Promise.reject('Form not ready. Use form.ready promise'); + return this.render() + .then((html) => { + this.setContent(this.element, html); + return this.attach(this.element).then(() => this.instance); + }) + .then((param) => { + this.emit('build', param); + return param; + }); } - return Promise.resolve(this.instance.render()) - .then((param) => { - this.emit('render', param); - return param; - }); - } - attach(element) { - if (!this.instance) { - return Promise.reject('Form not ready. Use form.ready promise'); + render() { + if (!this.instance) { + return Promise.reject('Form not ready. Use form.ready promise'); + } + return Promise.resolve(this.instance.render()).then((param) => { + this.emit('render', param); + return param; + }); } - if (this.element) { - delete this.element.component; + + attach(element) { + if (!this.instance) { + return Promise.reject('Form not ready. Use form.ready promise'); + } + if (this.element) { + delete this.element.component; + } + this.element = element; + return this.instance.attach(this.element).then((param) => { + this.emit('attach', param); + return param; + }); } - this.element = element; - return this.instance.attach(this.element) - .then((param) => { - this.emit('attach', param); - return param; - }); - } - teardown() { - super.teardown(); - delete this.instance; - delete this.ready; - } + teardown() { + super.teardown(); + delete this.instance; + delete this.ready; + } } // Allow simple embedding. @@ -374,7 +392,7 @@ Formio.embedForm = (embed) => Form.embed(embed); * @return {Promise} - When the form is instance is ready. */ Formio.createForm = (...args) => { - return (new Form(...args)).ready; + return new Form(...args).ready; }; Formio.Form = Form; diff --git a/src/FormBuilder.js b/src/FormBuilder.js index 56e7a2e404..80c3fff253 100644 --- a/src/FormBuilder.js +++ b/src/FormBuilder.js @@ -3,26 +3,31 @@ import Builders from './builders'; import Form from './Form'; export default class FormBuilder extends Form { - static options = {}; - constructor(element, form, options) { - form = form || {}; - options = options || {}; - super(element, form, Object.assign( - options, - FormBuilder.options, - ((Formio.options && Formio.options.builder) ? Formio.options.builder : {}) - )); - } - - create(display) { - if (Builders.builders[display]) { - return new Builders.builders[display](this.element, this.options); + static options = {}; + constructor(element, form, options) { + form = form || {}; + options = options || {}; + super( + element, + form, + Object.assign( + options, + FormBuilder.options, + Formio.options && Formio.options.builder + ? Formio.options.builder + : {}, + ), + ); } - else { - // eslint-disable-next-line new-cap - return new Builders.builders['webform'](this.element, this.options); + + create(display) { + if (Builders.builders[display]) { + return new Builders.builders[display](this.element, this.options); + } else { + // eslint-disable-next-line new-cap + return new Builders.builders['webform'](this.element, this.options); + } } - } } /** @@ -35,7 +40,7 @@ export default class FormBuilder extends Form { * @return {Promise} - When the form is instance is ready. */ Formio.builder = (...args) => { - return (new FormBuilder(...args)).ready; + return new FormBuilder(...args).ready; }; Formio.FormBuilder = FormBuilder; diff --git a/src/Formio.js b/src/Formio.js index 339b8a1715..980bab0a93 100644 --- a/src/Formio.js +++ b/src/Formio.js @@ -6,91 +6,108 @@ Formio.Providers = Providers; Formio.version = 'FORMIO_VERSION'; const isNil = (val) => val === null || val === undefined; -Formio.prototype.uploadFile = function(storage, file, fileName, dir, progressCallback, url, options, fileKey, groupPermissions, groupId, uploadStartCallback, abortCallback, multipartOptions) { - const requestArgs = { - provider: storage, - method: 'upload', - file: file, - fileName: fileName, - dir: dir - }; - fileKey = fileKey || 'file'; - const request = Formio.pluginWait('preRequest', requestArgs) - .then(() => { - return Formio.pluginGet('fileRequest', requestArgs) - .then((result) => { - if (storage && isNil(result)) { - const Provider = Providers.getProvider('storage', storage); - if (Provider) { - const provider = new Provider(this); - if (uploadStartCallback) { - uploadStartCallback(); - } - return provider.uploadFile(file, fileName, dir, progressCallback, url, options, fileKey, groupPermissions, groupId, abortCallback, multipartOptions); +Formio.prototype.uploadFile = function ( + storage, + file, + fileName, + dir, + progressCallback, + url, + options, + fileKey, + groupPermissions, + groupId, + uploadStartCallback, + abortCallback, + multipartOptions, +) { + const requestArgs = { + provider: storage, + method: 'upload', + file: file, + fileName: fileName, + dir: dir, + }; + fileKey = fileKey || 'file'; + const request = Formio.pluginWait('preRequest', requestArgs).then(() => { + return Formio.pluginGet('fileRequest', requestArgs).then((result) => { + if (storage && isNil(result)) { + const Provider = Providers.getProvider('storage', storage); + if (Provider) { + const provider = new Provider(this); + if (uploadStartCallback) { + uploadStartCallback(); + } + return provider.uploadFile( + file, + fileName, + dir, + progressCallback, + url, + options, + fileKey, + groupPermissions, + groupId, + abortCallback, + multipartOptions, + ); + } else { + throw 'Storage provider not found'; + } } - else { - throw ('Storage provider not found'); - } - } - return result || { url: '' }; + return result || { url: '' }; }); }); - return Formio.pluginAlter('wrapFileRequestPromise', request, requestArgs); + return Formio.pluginAlter('wrapFileRequestPromise', request, requestArgs); }; -Formio.prototype.downloadFile = function(file, options) { - const requestArgs = { - method: 'download', - file: file - }; +Formio.prototype.downloadFile = function (file, options) { + const requestArgs = { + method: 'download', + file: file, + }; - const request = Formio.pluginWait('preRequest', requestArgs) - .then(() => { - return Formio.pluginGet('fileRequest', requestArgs) - .then((result) => { - if (file.storage && isNil(result)) { - const Provider = Providers.getProvider('storage', file.storage); - if (Provider) { - const provider = new Provider(this); - return provider.downloadFile(file, options); - } - else { - throw ('Storage provider not found'); + const request = Formio.pluginWait('preRequest', requestArgs).then(() => { + return Formio.pluginGet('fileRequest', requestArgs).then((result) => { + if (file.storage && isNil(result)) { + const Provider = Providers.getProvider('storage', file.storage); + if (Provider) { + const provider = new Provider(this); + return provider.downloadFile(file, options); + } else { + throw 'Storage provider not found'; + } } - } - return result || { url: '' }; + return result || { url: '' }; }); }); - return Formio.pluginAlter('wrapFileRequestPromise', request, requestArgs); + return Formio.pluginAlter('wrapFileRequestPromise', request, requestArgs); }; -Formio.prototype.deleteFile = function(file, options) { - const requestArgs = { - method: 'delete', - file: file - }; +Formio.prototype.deleteFile = function (file, options) { + const requestArgs = { + method: 'delete', + file: file, + }; - const request = Formio.pluginWait('preRequest', requestArgs) - .then(() => { - return Formio.pluginGet('fileRequest', requestArgs) - .then((result) => { - if (file.storage && isNil(result)) { - const Provider = Providers.getProvider('storage', file.storage); - if (Provider) { - const provider = new Provider(this); - return provider.deleteFile(file, options); - } - else { - throw ('Storage provider not found'); + const request = Formio.pluginWait('preRequest', requestArgs).then(() => { + return Formio.pluginGet('fileRequest', requestArgs).then((result) => { + if (file.storage && isNil(result)) { + const Provider = Providers.getProvider('storage', file.storage); + if (Provider) { + const provider = new Provider(this); + return provider.deleteFile(file, options); + } else { + throw 'Storage provider not found'; + } } - } - return result || { url: '' }; + return result || { url: '' }; }); }); - return Formio.pluginAlter('wrapFileRequestPromise', request, requestArgs); + return Formio.pluginAlter('wrapFileRequestPromise', request, requestArgs); }; // For reverse compatability. diff --git a/src/Formio.unit.js b/src/Formio.unit.js index 1ce7123091..3d001b186a 100644 --- a/src/Formio.unit.js +++ b/src/Formio.unit.js @@ -1,2597 +1,2836 @@ -import { Formio } from "./index"; -import { fastCloneDeep } from "./utils/utils"; -import assert from "power-assert"; -import sinon from "sinon"; -import fetchMock from "fetch-mock/es5/server"; -import _ from "lodash"; -import Chance from "chance"; +import { Formio } from './index'; +import { fastCloneDeep } from './utils/utils'; +import assert from 'power-assert'; +import sinon from 'sinon'; +import fetchMock from 'fetch-mock/es5/server'; +import _ from 'lodash'; +import Chance from 'chance'; const chance = Chance(); -const protocol = "https"; -const domain = "localhost:3000"; +const protocol = 'https'; +const domain = 'localhost:3000'; const baseUrl = `${protocol}://api.${domain}`; Formio.setBaseUrl(baseUrl); Formio.setToken(null); Formio.fetch = fetchMock.fetchHandler; -const projectId = "59bbe2ec8c246100079191aa"; -const formId = "59bbe2ec8c246100079191ab"; -const submissionId = "59bbe2ec8c246100079191ac"; -const actionId = "59bbe2ec8c246100079191ad"; +const projectId = '59bbe2ec8c246100079191aa'; +const formId = '59bbe2ec8c246100079191ab'; +const submissionId = '59bbe2ec8c246100079191ac'; +const actionId = '59bbe2ec8c246100079191ad'; const generateID = function () { - return chance.string({ length: 24, pool: "0123456789abcdef" }); + return chance.string({ length: 24, pool: '0123456789abcdef' }); }; const runTests = function (fn, options) { - const tests = {}; - const noBefore = fn(tests); - if (!noBefore) { - beforeEach(function () { - Formio.setBaseUrl(baseUrl); - Formio.projectUrlSet = false; - Formio.projectUrl = "https://api.form.io"; - }); - } - _.each(tests, (testFn, path) => { - it(`Should initialize for ${path}`, function (done) { - if (typeof testFn === "function") { - testFn(); - } else { - const formio = new Formio(path, options); - for (const param in testFn) { - assert.equal( - formio[param], - testFn[param], - `${param} is not equal. ${formio[param]} == ${testFn[param]}\n`, - ); - } - } - done(); + const tests = {}; + const noBefore = fn(tests); + if (!noBefore) { + beforeEach(function () { + Formio.setBaseUrl(baseUrl); + Formio.projectUrlSet = false; + Formio.projectUrl = 'https://api.form.io'; + }); + } + _.each(tests, (testFn, path) => { + it(`Should initialize for ${path}`, function (done) { + if (typeof testFn === 'function') { + testFn(); + } else { + const formio = new Formio(path, options); + for (const param in testFn) { + assert.equal( + formio[param], + testFn[param], + `${param} is not equal. ${formio[param]} == ${testFn[param]}\n`, + ); + } + } + done(); + }); }); - }); }; -describe("Formio.js Tests", function () { - describe("Formio Constructor Tests", function () { - runTests((tests) => { - tests[`http://form.io/project/${projectId}/form/${formId}`] = { - projectUrl: `http://form.io/project/${projectId}`, - projectsUrl: "http://form.io/project", - projectId: projectId, - formsUrl: `http://form.io/project/${projectId}/form`, - formUrl: `http://form.io/project/${projectId}/form/${formId}`, - formId: formId, - actionsUrl: `http://form.io/project/${projectId}/form/${formId}/action`, - actionUrl: "", - actionId: "", - submissionsUrl: `http://form.io/project/${projectId}/form/${formId}/submission`, - submissionUrl: "", - submissionId: "", - query: "", - }; - tests[`http://form.io/form/${formId}`] = { - projectUrl: "http://form.io", - projectsUrl: `${baseUrl}/project`, - projectId: "", - formsUrl: "http://form.io/form", - formUrl: `http://form.io/form/${formId}`, - formId: formId, - actionsUrl: `http://form.io/form/${formId}/action`, - actionUrl: "", - actionId: "", - submissionsUrl: `http://form.io/form/${formId}/submission`, - submissionUrl: "", - submissionId: "", - query: "", - }; - tests[`http://form.io/form/${formId}/submission/${submissionId}`] = { - projectUrl: "http://form.io", - projectsUrl: `${baseUrl}/project`, - projectId: "", - formsUrl: "http://form.io/form", - formUrl: `http://form.io/form/${formId}`, - formId: formId, - actionsUrl: `http://form.io/form/${formId}/action`, - actionUrl: "", - actionId: "", - submissionsUrl: `http://form.io/form/${formId}/submission`, - submissionUrl: `http://form.io/form/${formId}/submission/${submissionId}`, - submissionId: submissionId, - query: "", - }; - tests[`http://form.io/form/${formId}/action/${actionId}`] = { - projectUrl: "http://form.io", - projectsUrl: `${baseUrl}/project`, - projectId: "", - formsUrl: "http://form.io/form", - formUrl: `http://form.io/form/${formId}`, - formId: formId, - actionsUrl: `http://form.io/form/${formId}/action`, - actionUrl: `http://form.io/form/${formId}/action/${actionId}`, - actionId: actionId, - submissionsUrl: `http://form.io/form/${formId}/submission`, - submissionUrl: "", - submissionId: "", - query: "", - }; - tests[ - `http://form.io/project/${projectId}/form/${formId}/action/${actionId}` - ] = { - projectUrl: `http://form.io/project/${projectId}`, - projectsUrl: "http://form.io/project", - projectId: projectId, - formsUrl: `http://form.io/project/${projectId}/form`, - formUrl: `http://form.io/project/${projectId}/form/${formId}`, - formId: formId, - actionsUrl: `http://form.io/project/${projectId}/form/${formId}/action`, - actionUrl: `http://form.io/project/${projectId}/form/${formId}/action/${actionId}`, - actionId: actionId, - submissionsUrl: `http://form.io/project/${projectId}/form/${formId}/submission`, - submissionUrl: "", - submissionId: "", - query: "", - }; - tests[`http://api.form.io/project/${projectId}`] = { - projectUrl: `http://api.form.io/project/${projectId}`, - projectsUrl: "http://api.form.io/project", - projectId: projectId, - formsUrl: `http://api.form.io/project/${projectId}/form`, - formUrl: "", - formId: "", - actionsUrl: `http://api.form.io/project/${projectId}/action`, - actionUrl: "", - actionId: "", - submissionsUrl: `http://api.form.io/project/${projectId}/submission`, - submissionUrl: "", - submissionId: "", - query: "", - }; - tests[ - `http://form.io/project/${projectId}/form/${formId}/submission/${submissionId}` - ] = { - projectUrl: `http://form.io/project/${projectId}`, - projectsUrl: "http://form.io/project", - projectId: projectId, - formsUrl: `http://form.io/project/${projectId}/form`, - formUrl: `http://form.io/project/${projectId}/form/${formId}`, - formId: formId, - actionsUrl: `http://form.io/project/${projectId}/form/${formId}/action`, - actionUrl: "", - actionId: "", - submissionsUrl: `http://form.io/project/${projectId}/form/${formId}/submission`, - submissionUrl: `http://form.io/project/${projectId}/form/${formId}/submission/${submissionId}`, - submissionId: submissionId, - query: "", - }; - tests[ - `http://form.io/project/${projectId}/form/${formId}?test=hello&test2=there` - ] = { - projectUrl: `http://form.io/project/${projectId}`, - projectsUrl: "http://form.io/project", - projectId: projectId, - formsUrl: `http://form.io/project/${projectId}/form`, - formUrl: `http://form.io/project/${projectId}/form/${formId}`, - formId: formId, - actionsUrl: `http://form.io/project/${projectId}/form/${formId}/action`, - actionUrl: "", - actionId: "", - submissionsUrl: `http://form.io/project/${projectId}/form/${formId}/submission`, - submissionUrl: "", - submissionId: "", - query: "?test=hello&test2=there", - }; - tests["http://project.form.io/user/login"] = { - projectUrl: "http://project.form.io", - projectsUrl: `${baseUrl}/project`, - projectId: "project", - formsUrl: "http://project.form.io/form", - formUrl: "http://project.form.io/user/login", - formId: "user/login", - actionsUrl: "http://project.form.io/user/login/action", - actionUrl: "", - actionId: "", - submissionsUrl: "http://project.form.io/user/login/submission", - submissionUrl: "", - submissionId: "", - query: "", - }; - tests[`http://project.form.io/user/login/submission/${submissionId}`] = { - projectUrl: "http://project.form.io", - projectsUrl: `${baseUrl}/project`, - projectId: "project", - formsUrl: "http://project.form.io/form", - formUrl: "http://project.form.io/user/login", - formId: "user/login", - actionsUrl: "http://project.form.io/user/login/action", - actionUrl: "", - actionId: "", - submissionsUrl: "http://project.form.io/user/login/submission", - submissionUrl: `http://project.form.io/user/login/submission/${submissionId}`, - submissionId: submissionId, - query: "", - }; - tests[`http://project.form.io/user/login/action/${actionId}`] = { - projectUrl: "http://project.form.io", - projectsUrl: `${baseUrl}/project`, - projectId: "project", - formsUrl: "http://project.form.io/form", - formUrl: "http://project.form.io/user/login", - formId: "user/login", - actionsUrl: "http://project.form.io/user/login/action", - actionUrl: `http://project.form.io/user/login/action/${actionId}`, - actionId: actionId, - submissionsUrl: "http://project.form.io/user/login/submission", - submissionUrl: "", - submissionId: "", - query: "", - }; - tests[`http://project.form.io/user/login/action/${actionId}?test=test2`] = - { - projectUrl: "http://project.form.io", - projectsUrl: `${baseUrl}/project`, - projectId: "project", - formsUrl: "http://project.form.io/form", - formUrl: "http://project.form.io/user/login", - formId: "user/login", - actionsUrl: "http://project.form.io/user/login/action", - actionUrl: `http://project.form.io/user/login/action/${actionId}`, - actionId: actionId, - submissionsUrl: "http://project.form.io/user/login/submission", - submissionUrl: "", - submissionId: "", - query: "?test=test2", - }; - tests[ - `http://project.form.io/user/loginform/action/${actionId}?test=test2` - ] = { - projectUrl: "http://project.form.io", - projectsUrl: `${baseUrl}/project`, - projectId: "project", - formsUrl: "http://project.form.io/form", - formUrl: "http://project.form.io/user/loginform", - formId: "user/loginform", - actionsUrl: "http://project.form.io/user/loginform/action", - actionUrl: `http://project.form.io/user/loginform/action/${actionId}`, - actionId: actionId, - submissionsUrl: "http://project.form.io/user/loginform/submission", - submissionUrl: "", - submissionId: "", - query: "?test=test2", - }; - tests["http://project.form.io/user/loginform/submission"] = { - projectUrl: "http://project.form.io", - projectsUrl: `${baseUrl}/project`, - projectId: "project", - formsUrl: "http://project.form.io/form", - formUrl: "http://project.form.io/user/loginform", - formId: "user/loginform", - actionsUrl: "http://project.form.io/user/loginform/action", - actionUrl: "", - actionId: "", - submissionsUrl: "http://project.form.io/user/loginform/submission", - submissionUrl: "", - submissionId: "", - query: "", - }; - tests["http://project.form.io/user"] = { - projectUrl: "http://project.form.io", - projectsUrl: `${baseUrl}/project`, - projectId: "project", - formsUrl: "http://project.form.io/form", - formUrl: "http://project.form.io/user", - formId: "user", - actionsUrl: "http://project.form.io/user/action", - actionUrl: "", - actionId: "", - submissionsUrl: "http://project.form.io/user/submission", - submissionUrl: "", - submissionId: "", - query: "", - }; - tests[ - `http://project.form.io/user/actionform/submission/${submissionId}` - ] = { - projectUrl: "http://project.form.io", - projectsUrl: `${baseUrl}/project`, - projectId: "project", - formsUrl: "http://project.form.io/form", - formUrl: "http://project.form.io/user/actionform", - formId: "user/actionform", - actionsUrl: "http://project.form.io/user/actionform/action", - actionUrl: "", - actionId: "", - submissionsUrl: "http://project.form.io/user/actionform/submission", - submissionUrl: `http://project.form.io/user/actionform/submission/${submissionId}`, - submissionId: submissionId, - query: "", - }; - tests["http://project.form.io/user/actionform/?test=foo"] = { - projectUrl: "http://project.form.io", - projectsUrl: `${baseUrl}/project`, - projectId: "project", - formsUrl: "http://project.form.io/form", - formUrl: "http://project.form.io/user/actionform", - formId: "user/actionform", - actionsUrl: "http://project.form.io/user/actionform/action", - actionUrl: "", - actionId: "", - submissionsUrl: "http://project.form.io/user/actionform/submission", - submissionUrl: "", - submissionId: "", - query: "?test=foo", - }; +describe('Formio.js Tests', function () { + describe('Formio Constructor Tests', function () { + runTests((tests) => { + tests[`http://form.io/project/${projectId}/form/${formId}`] = { + projectUrl: `http://form.io/project/${projectId}`, + projectsUrl: 'http://form.io/project', + projectId: projectId, + formsUrl: `http://form.io/project/${projectId}/form`, + formUrl: `http://form.io/project/${projectId}/form/${formId}`, + formId: formId, + actionsUrl: `http://form.io/project/${projectId}/form/${formId}/action`, + actionUrl: '', + actionId: '', + submissionsUrl: `http://form.io/project/${projectId}/form/${formId}/submission`, + submissionUrl: '', + submissionId: '', + query: '', + }; + tests[`http://form.io/form/${formId}`] = { + projectUrl: 'http://form.io', + projectsUrl: `${baseUrl}/project`, + projectId: '', + formsUrl: 'http://form.io/form', + formUrl: `http://form.io/form/${formId}`, + formId: formId, + actionsUrl: `http://form.io/form/${formId}/action`, + actionUrl: '', + actionId: '', + submissionsUrl: `http://form.io/form/${formId}/submission`, + submissionUrl: '', + submissionId: '', + query: '', + }; + tests[`http://form.io/form/${formId}/submission/${submissionId}`] = + { + projectUrl: 'http://form.io', + projectsUrl: `${baseUrl}/project`, + projectId: '', + formsUrl: 'http://form.io/form', + formUrl: `http://form.io/form/${formId}`, + formId: formId, + actionsUrl: `http://form.io/form/${formId}/action`, + actionUrl: '', + actionId: '', + submissionsUrl: `http://form.io/form/${formId}/submission`, + submissionUrl: `http://form.io/form/${formId}/submission/${submissionId}`, + submissionId: submissionId, + query: '', + }; + tests[`http://form.io/form/${formId}/action/${actionId}`] = { + projectUrl: 'http://form.io', + projectsUrl: `${baseUrl}/project`, + projectId: '', + formsUrl: 'http://form.io/form', + formUrl: `http://form.io/form/${formId}`, + formId: formId, + actionsUrl: `http://form.io/form/${formId}/action`, + actionUrl: `http://form.io/form/${formId}/action/${actionId}`, + actionId: actionId, + submissionsUrl: `http://form.io/form/${formId}/submission`, + submissionUrl: '', + submissionId: '', + query: '', + }; + tests[ + `http://form.io/project/${projectId}/form/${formId}/action/${actionId}` + ] = { + projectUrl: `http://form.io/project/${projectId}`, + projectsUrl: 'http://form.io/project', + projectId: projectId, + formsUrl: `http://form.io/project/${projectId}/form`, + formUrl: `http://form.io/project/${projectId}/form/${formId}`, + formId: formId, + actionsUrl: `http://form.io/project/${projectId}/form/${formId}/action`, + actionUrl: `http://form.io/project/${projectId}/form/${formId}/action/${actionId}`, + actionId: actionId, + submissionsUrl: `http://form.io/project/${projectId}/form/${formId}/submission`, + submissionUrl: '', + submissionId: '', + query: '', + }; + tests[`http://api.form.io/project/${projectId}`] = { + projectUrl: `http://api.form.io/project/${projectId}`, + projectsUrl: 'http://api.form.io/project', + projectId: projectId, + formsUrl: `http://api.form.io/project/${projectId}/form`, + formUrl: '', + formId: '', + actionsUrl: `http://api.form.io/project/${projectId}/action`, + actionUrl: '', + actionId: '', + submissionsUrl: `http://api.form.io/project/${projectId}/submission`, + submissionUrl: '', + submissionId: '', + query: '', + }; + tests[ + `http://form.io/project/${projectId}/form/${formId}/submission/${submissionId}` + ] = { + projectUrl: `http://form.io/project/${projectId}`, + projectsUrl: 'http://form.io/project', + projectId: projectId, + formsUrl: `http://form.io/project/${projectId}/form`, + formUrl: `http://form.io/project/${projectId}/form/${formId}`, + formId: formId, + actionsUrl: `http://form.io/project/${projectId}/form/${formId}/action`, + actionUrl: '', + actionId: '', + submissionsUrl: `http://form.io/project/${projectId}/form/${formId}/submission`, + submissionUrl: `http://form.io/project/${projectId}/form/${formId}/submission/${submissionId}`, + submissionId: submissionId, + query: '', + }; + tests[ + `http://form.io/project/${projectId}/form/${formId}?test=hello&test2=there` + ] = { + projectUrl: `http://form.io/project/${projectId}`, + projectsUrl: 'http://form.io/project', + projectId: projectId, + formsUrl: `http://form.io/project/${projectId}/form`, + formUrl: `http://form.io/project/${projectId}/form/${formId}`, + formId: formId, + actionsUrl: `http://form.io/project/${projectId}/form/${formId}/action`, + actionUrl: '', + actionId: '', + submissionsUrl: `http://form.io/project/${projectId}/form/${formId}/submission`, + submissionUrl: '', + submissionId: '', + query: '?test=hello&test2=there', + }; + tests['http://project.form.io/user/login'] = { + projectUrl: 'http://project.form.io', + projectsUrl: `${baseUrl}/project`, + projectId: 'project', + formsUrl: 'http://project.form.io/form', + formUrl: 'http://project.form.io/user/login', + formId: 'user/login', + actionsUrl: 'http://project.form.io/user/login/action', + actionUrl: '', + actionId: '', + submissionsUrl: 'http://project.form.io/user/login/submission', + submissionUrl: '', + submissionId: '', + query: '', + }; + tests[ + `http://project.form.io/user/login/submission/${submissionId}` + ] = { + projectUrl: 'http://project.form.io', + projectsUrl: `${baseUrl}/project`, + projectId: 'project', + formsUrl: 'http://project.form.io/form', + formUrl: 'http://project.form.io/user/login', + formId: 'user/login', + actionsUrl: 'http://project.form.io/user/login/action', + actionUrl: '', + actionId: '', + submissionsUrl: 'http://project.form.io/user/login/submission', + submissionUrl: `http://project.form.io/user/login/submission/${submissionId}`, + submissionId: submissionId, + query: '', + }; + tests[`http://project.form.io/user/login/action/${actionId}`] = { + projectUrl: 'http://project.form.io', + projectsUrl: `${baseUrl}/project`, + projectId: 'project', + formsUrl: 'http://project.form.io/form', + formUrl: 'http://project.form.io/user/login', + formId: 'user/login', + actionsUrl: 'http://project.form.io/user/login/action', + actionUrl: `http://project.form.io/user/login/action/${actionId}`, + actionId: actionId, + submissionsUrl: 'http://project.form.io/user/login/submission', + submissionUrl: '', + submissionId: '', + query: '', + }; + tests[ + `http://project.form.io/user/login/action/${actionId}?test=test2` + ] = { + projectUrl: 'http://project.form.io', + projectsUrl: `${baseUrl}/project`, + projectId: 'project', + formsUrl: 'http://project.form.io/form', + formUrl: 'http://project.form.io/user/login', + formId: 'user/login', + actionsUrl: 'http://project.form.io/user/login/action', + actionUrl: `http://project.form.io/user/login/action/${actionId}`, + actionId: actionId, + submissionsUrl: 'http://project.form.io/user/login/submission', + submissionUrl: '', + submissionId: '', + query: '?test=test2', + }; + tests[ + `http://project.form.io/user/loginform/action/${actionId}?test=test2` + ] = { + projectUrl: 'http://project.form.io', + projectsUrl: `${baseUrl}/project`, + projectId: 'project', + formsUrl: 'http://project.form.io/form', + formUrl: 'http://project.form.io/user/loginform', + formId: 'user/loginform', + actionsUrl: 'http://project.form.io/user/loginform/action', + actionUrl: `http://project.form.io/user/loginform/action/${actionId}`, + actionId: actionId, + submissionsUrl: + 'http://project.form.io/user/loginform/submission', + submissionUrl: '', + submissionId: '', + query: '?test=test2', + }; + tests['http://project.form.io/user/loginform/submission'] = { + projectUrl: 'http://project.form.io', + projectsUrl: `${baseUrl}/project`, + projectId: 'project', + formsUrl: 'http://project.form.io/form', + formUrl: 'http://project.form.io/user/loginform', + formId: 'user/loginform', + actionsUrl: 'http://project.form.io/user/loginform/action', + actionUrl: '', + actionId: '', + submissionsUrl: + 'http://project.form.io/user/loginform/submission', + submissionUrl: '', + submissionId: '', + query: '', + }; + tests['http://project.form.io/user'] = { + projectUrl: 'http://project.form.io', + projectsUrl: `${baseUrl}/project`, + projectId: 'project', + formsUrl: 'http://project.form.io/form', + formUrl: 'http://project.form.io/user', + formId: 'user', + actionsUrl: 'http://project.form.io/user/action', + actionUrl: '', + actionId: '', + submissionsUrl: 'http://project.form.io/user/submission', + submissionUrl: '', + submissionId: '', + query: '', + }; + tests[ + `http://project.form.io/user/actionform/submission/${submissionId}` + ] = { + projectUrl: 'http://project.form.io', + projectsUrl: `${baseUrl}/project`, + projectId: 'project', + formsUrl: 'http://project.form.io/form', + formUrl: 'http://project.form.io/user/actionform', + formId: 'user/actionform', + actionsUrl: 'http://project.form.io/user/actionform/action', + actionUrl: '', + actionId: '', + submissionsUrl: + 'http://project.form.io/user/actionform/submission', + submissionUrl: `http://project.form.io/user/actionform/submission/${submissionId}`, + submissionId: submissionId, + query: '', + }; + tests['http://project.form.io/user/actionform/?test=foo'] = { + projectUrl: 'http://project.form.io', + projectsUrl: `${baseUrl}/project`, + projectId: 'project', + formsUrl: 'http://project.form.io/form', + formUrl: 'http://project.form.io/user/actionform', + formId: 'user/actionform', + actionsUrl: 'http://project.form.io/user/actionform/action', + actionUrl: '', + actionId: '', + submissionsUrl: + 'http://project.form.io/user/actionform/submission', + submissionUrl: '', + submissionId: '', + query: '?test=foo', + }; + }); }); - }); - - describe("Localhost Constructor Tests", function () { - const testBaseUrl = "localhost:3000"; - const projectName = "myproject"; - const projectUrl = `${protocol}://${projectName}.${testBaseUrl}`; - runTests( - (tests) => { - tests[`${projectUrl}/user/actionform/?test=foo`] = { - projectUrl: projectUrl, - projectsUrl: `${baseUrl}/project`, - projectId: projectName, - formsUrl: `${projectUrl}/form`, - formUrl: `${projectUrl}/user/actionform`, - formId: "user/actionform", - actionsUrl: `${projectUrl}/user/actionform/action`, - actionUrl: "", - actionId: "", - submissionsUrl: `${projectUrl}/user/actionform/submission`, - submissionUrl: "", - submissionId: "", - query: "?test=foo", - }; - tests[`${projectUrl}/user`] = { - projectUrl: projectUrl, - projectsUrl: `${baseUrl}/project`, - projectId: projectName, - formsUrl: `${projectUrl}/form`, - formUrl: `${projectUrl}/user`, - formId: "user", - actionsUrl: `${projectUrl}/user/action`, - actionUrl: "", - actionId: "", - submissionsUrl: `${projectUrl}/user/submission`, - submissionUrl: "", - submissionId: "", - query: "", - }; - }, - { base: baseUrl }, - ); - }); - - describe("Subdomain Constructor Tests", function () { - const testBaseUrl = "foo.blah.form.io"; - const projectName = "myproject"; - const projectUrl = `${protocol}://${projectName}.${testBaseUrl}`; - runTests( - (tests) => { - tests[`${projectUrl}/user/actionform/?test=foo`] = { - projectUrl: projectUrl, - projectsUrl: `${baseUrl}/project`, - projectId: projectName, - formsUrl: `${projectUrl}/form`, - formUrl: `${projectUrl}/user/actionform`, - formId: "user/actionform", - actionsUrl: `${projectUrl}/user/actionform/action`, - actionUrl: "", - actionId: "", - submissionsUrl: `${projectUrl}/user/actionform/submission`, - submissionUrl: "", - submissionId: "", - query: "?test=foo", - }; - tests[`${projectUrl}/user`] = { - projectUrl: projectUrl, - projectsUrl: `${baseUrl}/project`, - projectId: projectName, - formsUrl: `${projectUrl}/form`, - formUrl: `${projectUrl}/user`, - formId: "user", - actionsUrl: `${projectUrl}/user/action`, - actionUrl: "", - actionId: "", - submissionsUrl: `${projectUrl}/user/submission`, - submissionUrl: "", - submissionId: "", - query: "", - }; - }, - { base: baseUrl }, - ); - }); - - describe("Subdirectory Constructor Tests", function () { - const testBaseUrl = "foo.blah.form.io"; - const projectName = "myproject"; - const projectUrl = `${protocol}://${testBaseUrl}/${projectName}`; - runTests( - (tests) => { - tests[`${projectUrl}/user/actionform/?test=foo`] = { - projectUrl: projectUrl, - projectsUrl: `${protocol}://${testBaseUrl}/project`, - projectId: projectName, - formsUrl: `${projectUrl}/form`, - formUrl: `${projectUrl}/user/actionform`, - formId: "user/actionform", - actionsUrl: `${projectUrl}/user/actionform/action`, - actionUrl: "", - actionId: "", - submissionsUrl: `${projectUrl}/user/actionform/submission`, - submissionUrl: "", - submissionId: "", - query: "?test=foo", - }; - tests[`${projectUrl}/user`] = { - projectUrl: projectUrl, - projectsUrl: `${protocol}://${testBaseUrl}/project`, - projectId: projectName, - formsUrl: `${projectUrl}/form`, - formUrl: `${projectUrl}/user`, - formId: "user", - actionsUrl: `${projectUrl}/user/action`, - actionUrl: "", - actionId: "", - submissionsUrl: `${projectUrl}/user/submission`, - submissionUrl: "", - submissionId: "", - query: "", - }; - tests[projectUrl] = { - projectUrl: projectUrl, - projectsUrl: `${protocol}://${testBaseUrl}/project`, - projectId: projectName, - formsUrl: `${projectUrl}/form`, - formUrl: "", - formId: "", - actionsUrl: `${projectUrl}/action`, - actionUrl: "", - actionId: "", - submissionsUrl: `${projectUrl}/submission`, - submissionUrl: "", - submissionId: "", - query: "", - }; - }, - { base: `${protocol}://${testBaseUrl}` }, - ); - }); - describe("Simple Form Constructor Tests", function () { - runTests((tests) => { - tests["init"] = () => { - Formio.setBaseUrl("https://api.form.io"); - Formio.projectUrlSet = false; - Formio.projectUrl = "https://api.form.io"; - }; - tests["https://examples.form.io/example"] = { - projectUrl: "https://examples.form.io", - projectsUrl: "", - projectId: "", - formsUrl: "https://examples.form.io/form", - formUrl: "https://examples.form.io/example", - formId: "example", - actionsUrl: "https://examples.form.io/example/action", - actionUrl: "", - actionId: "", - submissionsUrl: "https://examples.form.io/example/submission", - submissionUrl: "", - submissionId: "", - query: "", - }; - return true; + describe('Localhost Constructor Tests', function () { + const testBaseUrl = 'localhost:3000'; + const projectName = 'myproject'; + const projectUrl = `${protocol}://${projectName}.${testBaseUrl}`; + runTests( + (tests) => { + tests[`${projectUrl}/user/actionform/?test=foo`] = { + projectUrl: projectUrl, + projectsUrl: `${baseUrl}/project`, + projectId: projectName, + formsUrl: `${projectUrl}/form`, + formUrl: `${projectUrl}/user/actionform`, + formId: 'user/actionform', + actionsUrl: `${projectUrl}/user/actionform/action`, + actionUrl: '', + actionId: '', + submissionsUrl: `${projectUrl}/user/actionform/submission`, + submissionUrl: '', + submissionId: '', + query: '?test=foo', + }; + tests[`${projectUrl}/user`] = { + projectUrl: projectUrl, + projectsUrl: `${baseUrl}/project`, + projectId: projectName, + formsUrl: `${projectUrl}/form`, + formUrl: `${projectUrl}/user`, + formId: 'user', + actionsUrl: `${projectUrl}/user/action`, + actionUrl: '', + actionId: '', + submissionsUrl: `${projectUrl}/user/submission`, + submissionUrl: '', + submissionId: '', + query: '', + }; + }, + { base: baseUrl }, + ); }); - }); - - describe("Open Source Constructor Tests", function () { - const formBaseUrl = "http://localhost:3000"; - runTests( - (tests) => { - tests[`${formBaseUrl}/user`] = { - projectUrl: formBaseUrl, - projectsUrl: "", - projectId: "", - formsUrl: `${formBaseUrl}/form`, - formUrl: `${formBaseUrl}/user`, - formId: "user", - actionsUrl: `${formBaseUrl}/user/action`, - actionUrl: "", - actionId: "", - submissionsUrl: `${formBaseUrl}/user/submission`, - submissionUrl: "", - submissionId: "", - query: "", - }; - tests[`${formBaseUrl}/user/actionform/?test=foo`] = { - projectUrl: formBaseUrl, - projectsUrl: "", - projectId: "", - formsUrl: `${formBaseUrl}/form`, - formUrl: `${formBaseUrl}/user/actionform`, - formId: "user/actionform", - actionsUrl: `${formBaseUrl}/user/actionform/action`, - actionUrl: "", - actionId: "", - submissionsUrl: `${formBaseUrl}/user/actionform/submission`, - submissionUrl: "", - submissionId: "", - query: "?test=foo", - }; - }, - { base: formBaseUrl, project: formBaseUrl }, - ); - }); - - describe("Plugins", function () { - let plugin = null; - beforeEach(function () { - assert.equal( - Formio.getPlugin("test-plugin"), - undefined, - "No plugin may be returned under the name `test-plugin`", - ); - plugin = { init: sinon.spy() }; - Formio.registerPlugin(plugin, "test-plugin"); - assert.ok( - plugin.init.calledOnce, - "plugin.init must be called exactly once", - ); - assert.ok( - plugin.init.calledOn(plugin), - "plugin.init must be called on plugin as `this`", - ); - assert.ok( - plugin.init.calledWithExactly(Formio), - "plugin.init must be given Formio as argument", - ); - assert.equal( - Formio.getPlugin("test-plugin"), - plugin, - "getPlugin must return plugin", - ); + describe('Subdomain Constructor Tests', function () { + const testBaseUrl = 'foo.blah.form.io'; + const projectName = 'myproject'; + const projectUrl = `${protocol}://${projectName}.${testBaseUrl}`; + runTests( + (tests) => { + tests[`${projectUrl}/user/actionform/?test=foo`] = { + projectUrl: projectUrl, + projectsUrl: `${baseUrl}/project`, + projectId: projectName, + formsUrl: `${projectUrl}/form`, + formUrl: `${projectUrl}/user/actionform`, + formId: 'user/actionform', + actionsUrl: `${projectUrl}/user/actionform/action`, + actionUrl: '', + actionId: '', + submissionsUrl: `${projectUrl}/user/actionform/submission`, + submissionUrl: '', + submissionId: '', + query: '?test=foo', + }; + tests[`${projectUrl}/user`] = { + projectUrl: projectUrl, + projectsUrl: `${baseUrl}/project`, + projectId: projectName, + formsUrl: `${projectUrl}/form`, + formUrl: `${projectUrl}/user`, + formId: 'user', + actionsUrl: `${projectUrl}/user/action`, + actionUrl: '', + actionId: '', + submissionsUrl: `${projectUrl}/user/submission`, + submissionUrl: '', + submissionId: '', + query: '', + }; + }, + { base: baseUrl }, + ); }); - afterEach(function () { - assert.equal( - Formio.getPlugin("test-plugin"), - plugin, - "getPlugin must return plugin", - ); - plugin.deregister = sinon.spy(); - Formio.deregisterPlugin(plugin, "test-plugin"); - assert.ok( - plugin.deregister.calledOnce, - "plugin.deregister must be called exactly once", - ); - assert.ok( - plugin.deregister.calledOn(plugin), - "plugin.deregister must be called on plugin as `this`", - ); - assert.ok( - plugin.deregister.calledWithExactly(Formio), - "plugin.deregister must be given Formio as argument", - ); - assert.equal( - Formio.getPlugin("test-plugin"), - undefined, - "No plugin may be returned under the name `test-plugin`", - ); + describe('Subdirectory Constructor Tests', function () { + const testBaseUrl = 'foo.blah.form.io'; + const projectName = 'myproject'; + const projectUrl = `${protocol}://${testBaseUrl}/${projectName}`; + runTests( + (tests) => { + tests[`${projectUrl}/user/actionform/?test=foo`] = { + projectUrl: projectUrl, + projectsUrl: `${protocol}://${testBaseUrl}/project`, + projectId: projectName, + formsUrl: `${projectUrl}/form`, + formUrl: `${projectUrl}/user/actionform`, + formId: 'user/actionform', + actionsUrl: `${projectUrl}/user/actionform/action`, + actionUrl: '', + actionId: '', + submissionsUrl: `${projectUrl}/user/actionform/submission`, + submissionUrl: '', + submissionId: '', + query: '?test=foo', + }; + tests[`${projectUrl}/user`] = { + projectUrl: projectUrl, + projectsUrl: `${protocol}://${testBaseUrl}/project`, + projectId: projectName, + formsUrl: `${projectUrl}/form`, + formUrl: `${projectUrl}/user`, + formId: 'user', + actionsUrl: `${projectUrl}/user/action`, + actionUrl: '', + actionId: '', + submissionsUrl: `${projectUrl}/user/submission`, + submissionUrl: '', + submissionId: '', + query: '', + }; + tests[projectUrl] = { + projectUrl: projectUrl, + projectsUrl: `${protocol}://${testBaseUrl}/project`, + projectId: projectName, + formsUrl: `${projectUrl}/form`, + formUrl: '', + formId: '', + actionsUrl: `${projectUrl}/action`, + actionUrl: '', + actionId: '', + submissionsUrl: `${projectUrl}/submission`, + submissionUrl: '', + submissionId: '', + query: '', + }; + }, + { base: `${protocol}://${testBaseUrl}` }, + ); }); - // Test a request to see if the plugin flow order is correct - const testRequest = function testRequest(url, method, type) { - let fnName; - switch (method) { - case "GET": - fnName = `load${_.capitalize(type)}`; - break; - case "POST": - case "PUT": - fnName = `save${_.capitalize(type)}`; - break; - case "DELETE": - fnName = `delete${_.capitalize(type)}`; - break; - } + describe('Simple Form Constructor Tests', function () { + runTests((tests) => { + tests['init'] = () => { + Formio.setBaseUrl('https://api.form.io'); + Formio.projectUrlSet = false; + Formio.projectUrl = 'https://api.form.io'; + }; + tests['https://examples.form.io/example'] = { + projectUrl: 'https://examples.form.io', + projectsUrl: '', + projectId: '', + formsUrl: 'https://examples.form.io/form', + formUrl: 'https://examples.form.io/example', + formId: 'example', + actionsUrl: 'https://examples.form.io/example/action', + actionUrl: '', + actionId: '', + submissionsUrl: 'https://examples.form.io/example/submission', + submissionUrl: '', + submissionId: '', + query: '', + }; + return true; + }); + }); - it(`Plugin ${method} ${fnName}`, function (done) { - let step = 0; - const formio = new Formio(url); - method = method.toUpperCase(); - const testData = { testRequest: "TEST_REQUEST" }; - const testOpts = { testOption: true }; - const testResult = { _id: "TEST_ID", testResult: "TEST_RESULT" }; + describe('Open Source Constructor Tests', function () { + const formBaseUrl = 'http://localhost:3000'; + runTests( + (tests) => { + tests[`${formBaseUrl}/user`] = { + projectUrl: formBaseUrl, + projectsUrl: '', + projectId: '', + formsUrl: `${formBaseUrl}/form`, + formUrl: `${formBaseUrl}/user`, + formId: 'user', + actionsUrl: `${formBaseUrl}/user/action`, + actionUrl: '', + actionId: '', + submissionsUrl: `${formBaseUrl}/user/submission`, + submissionUrl: '', + submissionId: '', + query: '', + }; + tests[`${formBaseUrl}/user/actionform/?test=foo`] = { + projectUrl: formBaseUrl, + projectsUrl: '', + projectId: '', + formsUrl: `${formBaseUrl}/form`, + formUrl: `${formBaseUrl}/user/actionform`, + formId: 'user/actionform', + actionsUrl: `${formBaseUrl}/user/actionform/action`, + actionUrl: '', + actionId: '', + submissionsUrl: `${formBaseUrl}/user/actionform/submission`, + submissionUrl: '', + submissionId: '', + query: '?test=foo', + }; + }, + { base: formBaseUrl, project: formBaseUrl }, + ); + }); - const expectedArgs = { - formio: formio, - type: type, - method: method, - url: formio[type + (method === "POST" ? "sUrl" : "Url")], - data: _.startsWith(fnName, "save") ? testData : null, - opts: testOpts, - }; + describe('Plugins', function () { + let plugin = null; - // Set up plugin hooks - plugin.preRequest = function (requestArgs) { - assert.equal(++step, 1, "preRequest hook should be called first"); - assert.deepEqual( - requestArgs, - expectedArgs, - "Request hook arguments match expected arguments", - ); - return Promise.resolve().then(() => { - assert.equal(++step, 3, "preRequest promise should resolve third"); - // TODO - }); - }; - plugin.request = function (requestArgs) { - assert.equal(++step, 4, "request hook should be called fourth"); - assert.deepEqual( - requestArgs, - expectedArgs, - "Request hook arguments match expected arguments", - ); - return Promise.resolve().then(() => { - assert.equal(++step, 5, "request promise should resolve fifth"); - return testResult; - }); - }; - plugin.wrapRequestPromise = function (promise, requestArgs) { - assert.equal( - ++step, - 2, - "wrapRequestPromise hook should be called second", - ); - assert.deepEqual( - requestArgs, - expectedArgs, - "Request hook arguments match expected arguments", - ); - return promise.then((result) => { + beforeEach(function () { assert.equal( - ++step, - 6, - "wrapRequestPromise post-result promise should resolve sixth", + Formio.getPlugin('test-plugin'), + undefined, + 'No plugin may be returned under the name `test-plugin`', ); - assert.deepEqual( - result, - testResult, - "Result should match result from request hook", + plugin = { init: sinon.spy() }; + Formio.registerPlugin(plugin, 'test-plugin'); + assert.ok( + plugin.init.calledOnce, + 'plugin.init must be called exactly once', + ); + assert.ok( + plugin.init.calledOn(plugin), + 'plugin.init must be called on plugin as `this`', + ); + assert.ok( + plugin.init.calledWithExactly(Formio), + 'plugin.init must be given Formio as argument', + ); + assert.equal( + Formio.getPlugin('test-plugin'), + plugin, + 'getPlugin must return plugin', ); - return result; - }); - }; - - let promise; - if (_.startsWith(fnName, "save")) { - promise = formio[fnName](testData, testOpts); - } else if (_.startsWith(fnName, "load")) { - promise = formio[fnName](null, testOpts); - } else { - promise = formio[fnName](testOpts); - } - promise.then((result) => { - assert.equal(++step, 7, "post request promise should resolve last"); - assert.deepEqual( - result, - testResult, - "Result should match result from request hook", - ); - done(); }); - }); - }; - const tests = [ - { - url: "https://api.localhost:3000/project/myproject", - method: "GET", - type: "project", - }, - { - url: "", - method: "POST", - type: "project", - }, - { - url: "https://api.localhost:3000/project/myproject", - method: "PUT", - type: "project", - }, - { - url: "https://api.localhost:3000/project/myproject", - method: "DELETE", - type: "project", - }, + afterEach(function () { + assert.equal( + Formio.getPlugin('test-plugin'), + plugin, + 'getPlugin must return plugin', + ); + plugin.deregister = sinon.spy(); + Formio.deregisterPlugin(plugin, 'test-plugin'); + assert.ok( + plugin.deregister.calledOnce, + 'plugin.deregister must be called exactly once', + ); + assert.ok( + plugin.deregister.calledOn(plugin), + 'plugin.deregister must be called on plugin as `this`', + ); + assert.ok( + plugin.deregister.calledWithExactly(Formio), + 'plugin.deregister must be given Formio as argument', + ); + assert.equal( + Formio.getPlugin('test-plugin'), + undefined, + 'No plugin may be returned under the name `test-plugin`', + ); + }); - { - url: "https://api.localhost:3000/project/myproject/form/0123456789ABCDEF01234567", - method: "GET", - type: "form", - }, - { - url: "https://api.localhost:3000/project/myproject", - method: "POST", - type: "form", - }, - { - url: "https://api.localhost:3000/project/myproject/form/0123456789ABCDEF01234567", - method: "PUT", - type: "form", - }, - { - url: "/project/myproject/form/0123456789ABCDEF01234567", - method: "PUT", - type: "form", - }, - { - url: "https://api.localhost:3000/project/myproject/form/0123456789ABCDEF01234567", - method: "DELETE", - type: "form", - }, - { - url: "https://api.localhost:3000/project/myproject/", - method: "GET", - type: "forms", - }, + // Test a request to see if the plugin flow order is correct + const testRequest = function testRequest(url, method, type) { + let fnName; + switch (method) { + case 'GET': + fnName = `load${_.capitalize(type)}`; + break; + case 'POST': + case 'PUT': + fnName = `save${_.capitalize(type)}`; + break; + case 'DELETE': + fnName = `delete${_.capitalize(type)}`; + break; + } - { - url: "https://api.localhost:3000/project/myproject/form/0123456789ABCDEF01234567/submission/76543210FEDCBA9876543210", - method: "GET", - type: "submission", - }, - { - url: "https://api.localhost:3000/project/myproject/form/0123456789ABCDEF01234567", - method: "POST", - type: "submission", - }, - { - url: "https://api.localhost:3000/project/myproject/form/0123456789ABCDEF01234567/submission/76543210FEDCBA9876543210", - method: "PUT", - type: "submission", - }, - { - url: "https://api.localhost:3000/project/myproject/form/0123456789ABCDEF01234567/submission/76543210FEDCBA9876543210", - method: "DELETE", - type: "submission", - }, - { - url: "https://api.localhost:3000/project/myproject/form/0123456789ABCDEF01234567", - method: "GET", - type: "submissions", - }, + it(`Plugin ${method} ${fnName}`, function (done) { + let step = 0; + const formio = new Formio(url); + method = method.toUpperCase(); + const testData = { testRequest: 'TEST_REQUEST' }; + const testOpts = { testOption: true }; + const testResult = { + _id: 'TEST_ID', + testResult: 'TEST_RESULT', + }; - { - url: "https://api.localhost:3000/project/myproject/form/0123456789ABCDEF01234567/action/76543210FEDCBA9876543210", - method: "GET", - type: "action", - }, - { - url: "https://api.localhost:3000/project/myproject/form/0123456789ABCDEF01234567", - method: "POST", - type: "action", - }, - { - url: "https://api.localhost:3000/project/myproject/form/0123456789ABCDEF01234567/action/76543210FEDCBA9876543210", - method: "PUT", - type: "action", - }, - { - url: "https://api.localhost:3000/project/myproject/form/0123456789ABCDEF01234567/action/76543210FEDCBA9876543210", - method: "DELETE", - type: "action", - }, - { - url: "https://api.localhost:3000/project/myproject/form/0123456789ABCDEF01234567", - method: "GET", - type: "actions", - }, - ]; + const expectedArgs = { + formio: formio, + type: type, + method: method, + url: formio[type + (method === 'POST' ? 'sUrl' : 'Url')], + data: _.startsWith(fnName, 'save') ? testData : null, + opts: testOpts, + }; - tests.forEach((test) => { - testRequest(test.url, test.method, test.type); - }); + // Set up plugin hooks + plugin.preRequest = function (requestArgs) { + assert.equal( + ++step, + 1, + 'preRequest hook should be called first', + ); + assert.deepEqual( + requestArgs, + expectedArgs, + 'Request hook arguments match expected arguments', + ); + return Promise.resolve().then(() => { + assert.equal( + ++step, + 3, + 'preRequest promise should resolve third', + ); + // TODO + }); + }; + plugin.request = function (requestArgs) { + assert.equal( + ++step, + 4, + 'request hook should be called fourth', + ); + assert.deepEqual( + requestArgs, + expectedArgs, + 'Request hook arguments match expected arguments', + ); + return Promise.resolve().then(() => { + assert.equal( + ++step, + 5, + 'request promise should resolve fifth', + ); + return testResult; + }); + }; + plugin.wrapRequestPromise = function (promise, requestArgs) { + assert.equal( + ++step, + 2, + 'wrapRequestPromise hook should be called second', + ); + assert.deepEqual( + requestArgs, + expectedArgs, + 'Request hook arguments match expected arguments', + ); + return promise.then((result) => { + assert.equal( + ++step, + 6, + 'wrapRequestPromise post-result promise should resolve sixth', + ); + assert.deepEqual( + result, + testResult, + 'Result should match result from request hook', + ); + return result; + }); + }; - const testStaticRequest = function testStaticRequest(fnName, url, method) { - it(`Plugin ${fnName}`, function (done) { - let step = 0; - const testResult = { _id: "TEST_ID", testResult: "TEST_RESULT" }; - const expectedArgs = { - url: url, - method: method, - data: null, - opts: {}, + let promise; + if (_.startsWith(fnName, 'save')) { + promise = formio[fnName](testData, testOpts); + } else if (_.startsWith(fnName, 'load')) { + promise = formio[fnName](null, testOpts); + } else { + promise = formio[fnName](testOpts); + } + promise.then((result) => { + assert.equal( + ++step, + 7, + 'post request promise should resolve last', + ); + assert.deepEqual( + result, + testResult, + 'Result should match result from request hook', + ); + done(); + }); + }); }; - // Set up plugin hooks - plugin.preRequest = function (requestArgs) { - assert.equal(++step, 1, "preRequest hook should be called first"); - assert.deepEqual( - requestArgs, - expectedArgs, - "Request hook arguments match expected arguments", - ); - return Promise.resolve().then(() => { - assert.equal(++step, 3, "preRequest promise should resolve third"); - // TODO - }); - }; - plugin.staticRequest = function (requestArgs) { - assert.equal(++step, 4, "request hook should be called fourth"); - assert.deepEqual( - requestArgs, - expectedArgs, - "Request hook arguments match expected arguments", - ); - return Promise.resolve().then(() => { - assert.equal(++step, 5, "request promise should resolve fifth"); - return testResult; - }); - }; - plugin.wrapStaticRequestPromise = function (promise, requestArgs) { - assert.equal( - ++step, - 2, - "wrapRequestPromise hook should be called second", - ); - assert.deepEqual( - requestArgs, - expectedArgs, - "Request hook arguments match expected arguments", - ); - return promise.then((result) => { - assert.equal( - ++step, - 6, - "wrapRequestPromise post-result promise should resolve sixth", - ); - assert.deepEqual( - result, - testResult, - "Result should match result from request hook", - ); - return result; - }); - }; + const tests = [ + { + url: 'https://api.localhost:3000/project/myproject', + method: 'GET', + type: 'project', + }, + { + url: '', + method: 'POST', + type: 'project', + }, + { + url: 'https://api.localhost:3000/project/myproject', + method: 'PUT', + type: 'project', + }, + { + url: 'https://api.localhost:3000/project/myproject', + method: 'DELETE', + type: 'project', + }, - Formio[fnName]().then((result) => { - assert.equal(++step, 7, "post request promise should resolve last"); - assert.deepEqual( - result, - testResult, - "Result should match result from request hook", - ); - done(); - }); - }); - }; + { + url: 'https://api.localhost:3000/project/myproject/form/0123456789ABCDEF01234567', + method: 'GET', + type: 'form', + }, + { + url: 'https://api.localhost:3000/project/myproject', + method: 'POST', + type: 'form', + }, + { + url: 'https://api.localhost:3000/project/myproject/form/0123456789ABCDEF01234567', + method: 'PUT', + type: 'form', + }, + { + url: '/project/myproject/form/0123456789ABCDEF01234567', + method: 'PUT', + type: 'form', + }, + { + url: 'https://api.localhost:3000/project/myproject/form/0123456789ABCDEF01234567', + method: 'DELETE', + type: 'form', + }, + { + url: 'https://api.localhost:3000/project/myproject/', + method: 'GET', + type: 'forms', + }, - const staticTests = [ - { - fnName: "loadProjects", - url: "https://api.localhost:3000/project", - method: "GET", - }, - { - fnName: "logout", - url: "https://api.localhost:3000/logout", - method: "GET", - }, - ]; + { + url: 'https://api.localhost:3000/project/myproject/form/0123456789ABCDEF01234567/submission/76543210FEDCBA9876543210', + method: 'GET', + type: 'submission', + }, + { + url: 'https://api.localhost:3000/project/myproject/form/0123456789ABCDEF01234567', + method: 'POST', + type: 'submission', + }, + { + url: 'https://api.localhost:3000/project/myproject/form/0123456789ABCDEF01234567/submission/76543210FEDCBA9876543210', + method: 'PUT', + type: 'submission', + }, + { + url: 'https://api.localhost:3000/project/myproject/form/0123456789ABCDEF01234567/submission/76543210FEDCBA9876543210', + method: 'DELETE', + type: 'submission', + }, + { + url: 'https://api.localhost:3000/project/myproject/form/0123456789ABCDEF01234567', + method: 'GET', + type: 'submissions', + }, - staticTests.forEach((test) => { - testStaticRequest(test.fnName, test.url, test.method, test.type); - }); + { + url: 'https://api.localhost:3000/project/myproject/form/0123456789ABCDEF01234567/action/76543210FEDCBA9876543210', + method: 'GET', + type: 'action', + }, + { + url: 'https://api.localhost:3000/project/myproject/form/0123456789ABCDEF01234567', + method: 'POST', + type: 'action', + }, + { + url: 'https://api.localhost:3000/project/myproject/form/0123456789ABCDEF01234567/action/76543210FEDCBA9876543210', + method: 'PUT', + type: 'action', + }, + { + url: 'https://api.localhost:3000/project/myproject/form/0123456789ABCDEF01234567/action/76543210FEDCBA9876543210', + method: 'DELETE', + type: 'action', + }, + { + url: 'https://api.localhost:3000/project/myproject/form/0123456789ABCDEF01234567', + method: 'GET', + type: 'actions', + }, + ]; - const testFileRequest = function testFileRequest(fnName, formUrl, args) { - it(`Plugin ${fnName}`, function (done) { - let step = 0; - const testResult = { _id: "TEST_ID", testResult: "TEST_RESULT" }; - let expectedArgs; + tests.forEach((test) => { + testRequest(test.url, test.method, test.type); + }); - if (fnName === "downloadFile") { - expectedArgs = { - method: "download", - file: args[0], - }; - } else if (fnName === "uploadFile") { - expectedArgs = { - provider: args[0], - method: "upload", - file: args[1], - fileName: args[2], - dir: args[3], - }; - } + const testStaticRequest = function testStaticRequest( + fnName, + url, + method, + ) { + it(`Plugin ${fnName}`, function (done) { + let step = 0; + const testResult = { + _id: 'TEST_ID', + testResult: 'TEST_RESULT', + }; + const expectedArgs = { + url: url, + method: method, + data: null, + opts: {}, + }; - // Set up plugin hooks - plugin.preRequest = function (requestArgs) { - assert.equal(++step, 1, "preRequest hook should be called first"); - assert.deepEqual( - requestArgs, - expectedArgs, - "Request hook arguments match expected arguments", - ); - return Promise.resolve().then(() => { - assert.equal(++step, 3, "preRequest promise should resolve third"); - // TODO - }); - }; - plugin.fileRequest = function (requestArgs) { - assert.equal(++step, 4, "request hook should be called fourth"); - assert.deepEqual( - requestArgs, - expectedArgs, - "Request hook arguments match expected arguments", - ); - return Promise.resolve().then(() => { - assert.equal(++step, 5, "request promise should resolve fifth"); - return testResult; - }); - }; - plugin.wrapFileRequestPromise = function (promise, requestArgs) { - assert.equal( - ++step, - 2, - "wrapFileRequestPromise hook should be called second", - ); - assert.deepEqual( - requestArgs, - expectedArgs, - "Request hook arguments match expected arguments", - ); - return promise.then((result) => { - assert.equal( - ++step, - 6, - "wrapFileRequestPromise post-result promise should resolve sixth", - ); - assert.deepEqual( - result, - testResult, - "Result should match result from request hook", - ); - return result; - }); + // Set up plugin hooks + plugin.preRequest = function (requestArgs) { + assert.equal( + ++step, + 1, + 'preRequest hook should be called first', + ); + assert.deepEqual( + requestArgs, + expectedArgs, + 'Request hook arguments match expected arguments', + ); + return Promise.resolve().then(() => { + assert.equal( + ++step, + 3, + 'preRequest promise should resolve third', + ); + // TODO + }); + }; + plugin.staticRequest = function (requestArgs) { + assert.equal( + ++step, + 4, + 'request hook should be called fourth', + ); + assert.deepEqual( + requestArgs, + expectedArgs, + 'Request hook arguments match expected arguments', + ); + return Promise.resolve().then(() => { + assert.equal( + ++step, + 5, + 'request promise should resolve fifth', + ); + return testResult; + }); + }; + plugin.wrapStaticRequestPromise = function ( + promise, + requestArgs, + ) { + assert.equal( + ++step, + 2, + 'wrapRequestPromise hook should be called second', + ); + assert.deepEqual( + requestArgs, + expectedArgs, + 'Request hook arguments match expected arguments', + ); + return promise.then((result) => { + assert.equal( + ++step, + 6, + 'wrapRequestPromise post-result promise should resolve sixth', + ); + assert.deepEqual( + result, + testResult, + 'Result should match result from request hook', + ); + return result; + }); + }; + + Formio[fnName]().then((result) => { + assert.equal( + ++step, + 7, + 'post request promise should resolve last', + ); + assert.deepEqual( + result, + testResult, + 'Result should match result from request hook', + ); + done(); + }); + }); }; - const formio = new Formio(formUrl); - formio[fnName].apply(null, args).then((result) => { - assert.equal(++step, 7, "post request promise should resolve last"); - assert.deepEqual( - result, - testResult, - "Result should match result from request hook", - ); - done(); + const staticTests = [ + { + fnName: 'loadProjects', + url: 'https://api.localhost:3000/project', + method: 'GET', + }, + { + fnName: 'logout', + url: 'https://api.localhost:3000/logout', + method: 'GET', + }, + ]; + + staticTests.forEach((test) => { + testStaticRequest(test.fnName, test.url, test.method, test.type); }); - }); - }; - const fileTests = [ - { - fnName: "uploadFile", - formUrl: "https://api.localhost:3000/project/123/form/123", - args: ["s3", "FILE", "file.jpg", "dir/"], - }, - { - fnName: "uploadFile", - formUrl: "https://api.localhost:3000/project/123/form/123", - args: ["dropbox", "FILE", "file.jpg", "dir/"], - }, - { - fnName: "downloadFile", - formUrl: "https://api.localhost:3000/project/123/form/123", - args: [ - { - storage: "s3", - name: "test", - }, - ], - }, - { - fnName: "downloadFile", - formUrl: "https://api.localhost:3000/project/123/form/123", - args: [ - { - storage: "dropbox", - name: "test", - }, - ], - }, - ]; + const testFileRequest = function testFileRequest( + fnName, + formUrl, + args, + ) { + it(`Plugin ${fnName}`, function (done) { + let step = 0; + const testResult = { + _id: 'TEST_ID', + testResult: 'TEST_RESULT', + }; + let expectedArgs; - fileTests.forEach((test) => { - testFileRequest(test.fnName, test.formUrl, test.args); - }); - }); + if (fnName === 'downloadFile') { + expectedArgs = { + method: 'download', + file: args[0], + }; + } else if (fnName === 'uploadFile') { + expectedArgs = { + provider: args[0], + method: 'upload', + file: args[1], + fileName: args[2], + dir: args[3], + }; + } - describe("Test Formio.js capabilities", function () { - const testCapability = function (test) { - it(test.name, function (done) { - // need to clear Formio cache before every test, otherwise mock results might be ignored for same URLs - Formio.clearCache(); - if (test.mock) { - const mock = test.mock(); - if (mock instanceof Array) { - _.each(mock, (_mock) => { - fetchMock.mock(_mock.url, _mock.response, { - method: _mock.method, - }); - }); - } else { - fetchMock.mock(mock.url, mock.response, { method: mock.method }); - } - } - Promise.resolve() - .then(() => { - return test.test(); - }) - .then(() => { - if (test.mock) { - fetchMock.restore(); - } - done(); - }) - .catch((err) => { - if (test.mock) { - fetchMock.restore(); - } - done(typeof err === "string" ? new Error(err) : err); - }); - }); - }; + // Set up plugin hooks + plugin.preRequest = function (requestArgs) { + assert.equal( + ++step, + 1, + 'preRequest hook should be called first', + ); + assert.deepEqual( + requestArgs, + expectedArgs, + 'Request hook arguments match expected arguments', + ); + return Promise.resolve().then(() => { + assert.equal( + ++step, + 3, + 'preRequest promise should resolve third', + ); + // TODO + }); + }; + plugin.fileRequest = function (requestArgs) { + assert.equal( + ++step, + 4, + 'request hook should be called fourth', + ); + assert.deepEqual( + requestArgs, + expectedArgs, + 'Request hook arguments match expected arguments', + ); + return Promise.resolve().then(() => { + assert.equal( + ++step, + 5, + 'request promise should resolve fifth', + ); + return testResult; + }); + }; + plugin.wrapFileRequestPromise = function ( + promise, + requestArgs, + ) { + assert.equal( + ++step, + 2, + 'wrapFileRequestPromise hook should be called second', + ); + assert.deepEqual( + requestArgs, + expectedArgs, + 'Request hook arguments match expected arguments', + ); + return promise.then((result) => { + assert.equal( + ++step, + 6, + 'wrapFileRequestPromise post-result promise should resolve sixth', + ); + assert.deepEqual( + result, + testResult, + 'Result should match result from request hook', + ); + return result; + }); + }; - let user; - let userPassword; - let userToken = chance.string({ length: 450 }); - const userFormId = generateID(); - let project; - let form; - let submission; + const formio = new Formio(formUrl); + formio[fnName].apply(null, args).then((result) => { + assert.equal( + ++step, + 7, + 'post request promise should resolve last', + ); + assert.deepEqual( + result, + testResult, + 'Result should match result from request hook', + ); + done(); + }); + }); + }; - const tests = [ - { - name: "Registering user.", - test() { - const req = { - data: { - "user.name": chance.string({ - length: 10, - pool: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", - }), - "user.email": chance.email(), - "user.password": chance.string({ length: 12 }), - }, - }; - Formio.setProjectUrl(Formio.getBaseUrl()); - const formio = new Formio(`${Formio.getBaseUrl()}/user/register`); - return formio.saveSubmission(req).then((response) => { - assert.deepEqual( - response, - user, - "saveSubmission response should match test user", - ); - assert.equal( - Formio.getToken(), - userToken, - "Formio should save the user token", - ); - }); - }, - mock() { - return [ + const fileTests = [ { - url: `${Formio.getBaseUrl()}/current`, - method: "GET", - response() { - return { - headers: { - "Content-Type": "application/json", - "x-jwt-token": userToken, - }, - body: user, - }; - }, + fnName: 'uploadFile', + formUrl: 'https://api.localhost:3000/project/123/form/123', + args: ['s3', 'FILE', 'file.jpg', 'dir/'], }, { - url: `${Formio.getBaseUrl()}/user/register/submission`, - method: "POST", - response(url, opts) { - const body = JSON.parse(opts.body); - const userId = generateID(); - user = { - _id: userId, - created: new Date().toISOString(), - modified: new Date().toISOString(), - data: { - email: body.data["user.email"], - name: body.data["user.name"], - }, - externalIds: [], - externalTokens: [], - form: userFormId, - owner: userId, - }; - userPassword = body.data["user.password"]; - return { - headers: { - "Content-Type": "application/json", - "x-jwt-token": userToken, - }, - body: user, - }; - }, + fnName: 'uploadFile', + formUrl: 'https://api.localhost:3000/project/123/form/123', + args: ['dropbox', 'FILE', 'file.jpg', 'dir/'], }, - ]; - }, - }, - { - name: "Logging in.", - test() { - const req = { - data: { - "user.email": user.data.email, - "user.password": userPassword, + { + fnName: 'downloadFile', + formUrl: 'https://api.localhost:3000/project/123/form/123', + args: [ + { + storage: 's3', + name: 'test', + }, + ], }, - }; - const formio = new Formio(`${Formio.getBaseUrl()}/user/login`); - return formio.saveSubmission(req).then((response) => { - assert.deepEqual( - response, - user, - "saveSubmission response should match test user", - ); - assert.equal( - Formio.getToken(), - userToken, - "Formio should save the user token", - ); - }); - }, - mock() { - return { - url: `${Formio.getBaseUrl()}/user/login/submission`, - method: "POST", - response(url, opts) { - const body = JSON.parse(opts.body); - userToken = chance.string({ length: 450 }); - assert.equal( - body.data["user.email"], - user.data.email, - "Login email must be correct.", - ); - assert.equal( - body.data["user.password"], - userPassword, - "Login password must be correct.", - ); - return { - headers: { - "Content-Type": "application/json", - "x-jwt-token": userToken, - }, - body: user, - }; + { + fnName: 'downloadFile', + formUrl: 'https://api.localhost:3000/project/123/form/123', + args: [ + { + storage: 'dropbox', + name: 'test', + }, + ], }, - }; - }, - }, - { - name: "Current user.", - test() { - return Formio.currentUser() - .then((response) => { - assert.deepEqual( - response, - user, - "currentUser response should match test user", - ); - return Formio.currentUser(); - }) - .then((response) => { - assert.deepEqual( - response, - user, - "currentUser response should match test user", - ); + ]; + + fileTests.forEach((test) => { + testFileRequest(test.fnName, test.formUrl, test.args); + }); + }); + + describe('Test Formio.js capabilities', function () { + const testCapability = function (test) { + it(test.name, function (done) { + // need to clear Formio cache before every test, otherwise mock results might be ignored for same URLs + Formio.clearCache(); + if (test.mock) { + const mock = test.mock(); + if (mock instanceof Array) { + _.each(mock, (_mock) => { + fetchMock.mock(_mock.url, _mock.response, { + method: _mock.method, + }); + }); + } else { + fetchMock.mock(mock.url, mock.response, { + method: mock.method, + }); + } + } + Promise.resolve() + .then(() => { + return test.test(); + }) + .then(() => { + if (test.mock) { + fetchMock.restore(); + } + done(); + }) + .catch((err) => { + if (test.mock) { + fetchMock.restore(); + } + done(typeof err === 'string' ? new Error(err) : err); + }); }); - }, - mock() { - let called = false; - return { - url: `${Formio.getBaseUrl()}/current`, - method: "GET", - response() { - assert.ok(!called, "User should be requested only once."); - called = true; - return { - headers: { - "Content-Type": "application/json", - "x-jwt-token": userToken, + }; + + let user; + let userPassword; + let userToken = chance.string({ length: 450 }); + const userFormId = generateID(); + let project; + let form; + let submission; + + const tests = [ + { + name: 'Registering user.', + test() { + const req = { + data: { + 'user.name': chance.string({ + length: 10, + pool: 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', + }), + 'user.email': chance.email(), + 'user.password': chance.string({ length: 12 }), + }, + }; + Formio.setProjectUrl(Formio.getBaseUrl()); + const formio = new Formio( + `${Formio.getBaseUrl()}/user/register`, + ); + return formio.saveSubmission(req).then((response) => { + assert.deepEqual( + response, + user, + 'saveSubmission response should match test user', + ); + assert.equal( + Formio.getToken(), + userToken, + 'Formio should save the user token', + ); + }); + }, + mock() { + return [ + { + url: `${Formio.getBaseUrl()}/current`, + method: 'GET', + response() { + return { + headers: { + 'Content-Type': 'application/json', + 'x-jwt-token': userToken, + }, + body: user, + }; + }, + }, + { + url: `${Formio.getBaseUrl()}/user/register/submission`, + method: 'POST', + response(url, opts) { + const body = JSON.parse(opts.body); + const userId = generateID(); + user = { + _id: userId, + created: new Date().toISOString(), + modified: new Date().toISOString(), + data: { + email: body.data['user.email'], + name: body.data['user.name'], + }, + externalIds: [], + externalTokens: [], + form: userFormId, + owner: userId, + }; + userPassword = body.data['user.password']; + return { + headers: { + 'Content-Type': 'application/json', + 'x-jwt-token': userToken, + }, + body: user, + }; + }, + }, + ]; }, - body: user, - }; - }, - }; - }, - }, - { - name: "Create Project", - test() { - const formio = new Formio(); - const req = { - title: chance.string({ - length: 10, - pool: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", - }), - name: chance.string({ - length: 10, - pool: "abcdefghijklmnopqrstuvwxyz", - }), - description: chance.paragraph({ sentences: 1 }), - settings: { - cors: "*", }, - template: "http://help.form.io/templates/empty.json", - }; - return formio.saveProject(req).then((response) => { - assert.deepEqual( - response, - project, - "saveProject response should match test user", - ); - }); - }, - mock() { - return { - url: `${Formio.getBaseUrl()}/project`, - method: "POST", - response(url, opts) { - const body = JSON.parse(opts.body); - const projectId = generateID(); - project = { - _id: projectId, - created: new Date().toISOString(), - modified: new Date().toISOString(), - apiCalls: { - used: 0, - limit: 1000, - remaining: 1000, - reset: new Date(Date.now() + 2.628e9).toISOString(), // ~1 month later + { + name: 'Logging in.', + test() { + const req = { + data: { + 'user.email': user.data.email, + 'user.password': userPassword, + }, + }; + const formio = new Formio( + `${Formio.getBaseUrl()}/user/login`, + ); + return formio.saveSubmission(req).then((response) => { + assert.deepEqual( + response, + user, + 'saveSubmission response should match test user', + ); + assert.equal( + Formio.getToken(), + userToken, + 'Formio should save the user token', + ); + }); }, - access: [], - title: body.title, - name: body.name, - description: body.description, - plan: "basic", - owner: user._id, - }; - return { - headers: { - "Content-Type": "application/json", - "x-jwt-token": userToken, + mock() { + return { + url: `${Formio.getBaseUrl()}/user/login/submission`, + method: 'POST', + response(url, opts) { + const body = JSON.parse(opts.body); + userToken = chance.string({ length: 450 }); + assert.equal( + body.data['user.email'], + user.data.email, + 'Login email must be correct.', + ); + assert.equal( + body.data['user.password'], + userPassword, + 'Login password must be correct.', + ); + return { + headers: { + 'Content-Type': 'application/json', + 'x-jwt-token': userToken, + }, + body: user, + }; + }, + }; }, - body: project, - }; }, - }; - }, - }, - { - name: "Getting Projects", - test() { - return Formio.loadProjects().then((projects) => { - assert.equal(projects.length, 1, "Should return only one project."); - assert.equal(projects.skip, 0, "skip should be 0."); - assert.equal(projects.limit, 1, "limit should be 1."); - assert.equal(projects.serverCount, 1, "serverCount should be 1."); - assert.deepEqual(projects[0], project, "Should match project"); - }); - }, - mock() { - return { - url: `${Formio.getBaseUrl()}/project`, - method: "GET", - response() { - return { - headers: { - "Content-Type": "application/json", - "Content-Range": "0-0/1", - "Range-Unit": "items", - "x-jwt-token": userToken, + { + name: 'Current user.', + test() { + return Formio.currentUser() + .then((response) => { + assert.deepEqual( + response, + user, + 'currentUser response should match test user', + ); + return Formio.currentUser(); + }) + .then((response) => { + assert.deepEqual( + response, + user, + 'currentUser response should match test user', + ); + }); }, - body: [project], - }; - }, - }; - }, - }, - { - name: "Read Project", - test() { - const formio = new Formio( - `${Formio.getBaseUrl()}/project/${project._id}`, - ); - return formio.loadProject().then((response) => { - assert.deepEqual(response, project, "Should match project"); - }); - }, - mock() { - return { - url: `${Formio.getBaseUrl()}/project/${project._id}`, - method: "GET", - response() { - return { - headers: { - "Content-Type": "application/json", - "x-jwt-token": userToken, + mock() { + let called = false; + return { + url: `${Formio.getBaseUrl()}/current`, + method: 'GET', + response() { + assert.ok( + !called, + 'User should be requested only once.', + ); + called = true; + return { + headers: { + 'Content-Type': 'application/json', + 'x-jwt-token': userToken, + }, + body: user, + }; + }, + }; }, - body: project, - }; }, - }; - }, - }, - { - name: "Update Project", - test() { - const formio = new Formio(`/project/${project._id}`); - const newProject = fastCloneDeep(project); - newProject.name = chance.string({ - length: 10, - pool: "abcdefghijklmnopqrstuvwxyz", - }); - newProject.title = chance.string({ - length: 10, - pool: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", - }); - newProject.description = chance.paragraph({ sentences: 1 }); - return formio.saveProject(newProject).then((response) => { - assert.deepEqual(response, project, "Project should match"); - }); - }, - mock() { - return { - url: `${Formio.getBaseUrl()}/project/${project._id}`, - method: "PUT", - response(url, opts) { - const body = JSON.parse(opts.body); - project = body; - return { - headers: { - "Content-Type": "application/json", - "x-jwt-token": userToken, + { + name: 'Create Project', + test() { + const formio = new Formio(); + const req = { + title: chance.string({ + length: 10, + pool: 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', + }), + name: chance.string({ + length: 10, + pool: 'abcdefghijklmnopqrstuvwxyz', + }), + description: chance.paragraph({ sentences: 1 }), + settings: { + cors: '*', + }, + template: 'http://help.form.io/templates/empty.json', + }; + return formio.saveProject(req).then((response) => { + assert.deepEqual( + response, + project, + 'saveProject response should match test user', + ); + }); + }, + mock() { + return { + url: `${Formio.getBaseUrl()}/project`, + method: 'POST', + response(url, opts) { + const body = JSON.parse(opts.body); + const projectId = generateID(); + project = { + _id: projectId, + created: new Date().toISOString(), + modified: new Date().toISOString(), + apiCalls: { + used: 0, + limit: 1000, + remaining: 1000, + reset: new Date( + Date.now() + 2.628e9, + ).toISOString(), // ~1 month later + }, + access: [], + title: body.title, + name: body.name, + description: body.description, + plan: 'basic', + owner: user._id, + }; + return { + headers: { + 'Content-Type': 'application/json', + 'x-jwt-token': userToken, + }, + body: project, + }; + }, + }; }, - body: project, - }; }, - }; - }, - }, - { - name: "Create Form", - test() { - const formio = new Formio(`/project/${project._id}/form`); - const req = { - title: chance.string({ - length: 10, - pool: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", - }), - name: chance.string({ - length: 10, - pool: "abcdefghijklmnopqrstuvwxyz", - }), - path: chance.string({ - length: 10, - pool: "abcdefghijklmnopqrstuvwxyz", - }), - components: [ - { - defaultValue: "", - input: true, - inputMask: "", - inputType: "text", - isNew: false, - key: "fieldLabel", - label: "Field Label", - multiple: false, - persistent: true, - placeholder: "", - prefix: "", - protected: false, - suffix: "", - tableView: true, - type: "textfield", - unique: false, - validate: { - required: false, - minLength: "", - maxLength: "", - pattern: "", - custom: "", - customPrivate: false, + { + name: 'Getting Projects', + test() { + return Formio.loadProjects().then((projects) => { + assert.equal( + projects.length, + 1, + 'Should return only one project.', + ); + assert.equal(projects.skip, 0, 'skip should be 0.'); + assert.equal(projects.limit, 1, 'limit should be 1.'); + assert.equal( + projects.serverCount, + 1, + 'serverCount should be 1.', + ); + assert.deepEqual( + projects[0], + project, + 'Should match project', + ); + }); }, - }, - { - action: "submit", - block: false, - disableOnInvalid: true, - input: true, - key: "submit", - label: "Submit", - leftIcon: "", - rightIcon: "", - size: "md", - tableView: false, - theme: "primary", - type: "button", - }, - ], - type: "form", - access: [], - submissionAccess: [], - }; - return formio.saveForm(req).then((response) => { - assert.deepEqual(response, form, "Form should match"); - }); - }, - mock() { - return { - url: `${Formio.getBaseUrl()}/project/${project._id}/form`, - method: "POST", - response(url, opts) { - const body = JSON.parse(opts.body); - const formId = generateID(); - form = fastCloneDeep(body); - _.assign(form, { - _id: formId, - created: new Date().toISOString(), - modified: new Date().toISOString(), - project: project._id, - owner: user._id, - }); - return { - headers: { - "Content-Type": "application/json", - "x-jwt-token": userToken, + mock() { + return { + url: `${Formio.getBaseUrl()}/project`, + method: 'GET', + response() { + return { + headers: { + 'Content-Type': 'application/json', + 'Content-Range': '0-0/1', + 'Range-Unit': 'items', + 'x-jwt-token': userToken, + }, + body: [project], + }; + }, + }; }, - body: form, - }; }, - }; - }, - }, - { - name: "Load Forms", - test() { - const formio = new Formio(`/project/${project._id}/form`); - return formio.loadForms().then((forms) => { - assert.equal(forms.length, 1, "Should return only one form."); - assert.equal(forms.skip, 0, "skip should be 0."); - assert.equal(forms.limit, 1, "limit should be 1."); - assert.equal(forms.serverCount, 1, "serverCount should be 1."); - assert.deepEqual(forms[0], form, "Should match form"); - }); - }, - mock() { - return { - url: `${Formio.getBaseUrl()}/project/${project._id}/form`, - method: "GET", - response() { - return { - headers: { - "Content-Type": "application/json", - "Content-Range": "0-0/1", - "Range-Unit": "items", - "x-jwt-token": userToken, + { + name: 'Read Project', + test() { + const formio = new Formio( + `${Formio.getBaseUrl()}/project/${project._id}`, + ); + return formio.loadProject().then((response) => { + assert.deepEqual( + response, + project, + 'Should match project', + ); + }); }, - body: [form], - }; - }, - }; - }, - }, - { - name: "Read Form", - test() { - const formio = new Formio(`/project/${project._id}/form/${form._id}`); - return formio.loadForm().then((response) => { - assert.deepEqual(response, form, "Form should match"); - }); - }, - mock() { - return { - url: `${Formio.getBaseUrl()}/project/${project._id}/form/${form._id}`, - method: "GET", - response() { - return { - headers: { - "Content-Type": "application/json", - "x-jwt-token": userToken, + mock() { + return { + url: `${Formio.getBaseUrl()}/project/${project._id}`, + method: 'GET', + response() { + return { + headers: { + 'Content-Type': 'application/json', + 'x-jwt-token': userToken, + }, + body: project, + }; + }, + }; }, - body: form, - }; }, - }; - }, - }, - { - name: "Update Form", - test() { - const formio = new Formio(`/project/${project._id}/form/${form._id}`); - const newForm = fastCloneDeep(form); - newForm.title = chance.string({ - length: 10, - pool: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", - }); - newForm.name = chance.string({ - length: 10, - pool: "abcdefghijklmnopqrstuvwxyz", - }); - newForm.path = chance.string({ - length: 10, - pool: "abcdefghijklmnopqrstuvwxyz", - }); - return formio.saveForm(newForm).then((response) => { - assert.deepEqual(response, form, "Form should match"); - }); - }, - mock() { - return { - url: `${Formio.getBaseUrl()}/project/${project._id}/form/${form._id}`, - method: "PUT", - response(url, opts) { - const body = JSON.parse(opts.body); - form = body; - return { - headers: { - "Content-Type": "application/json", - "x-jwt-token": userToken, + { + name: 'Update Project', + test() { + const formio = new Formio(`/project/${project._id}`); + const newProject = fastCloneDeep(project); + newProject.name = chance.string({ + length: 10, + pool: 'abcdefghijklmnopqrstuvwxyz', + }); + newProject.title = chance.string({ + length: 10, + pool: 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', + }); + newProject.description = chance.paragraph({ sentences: 1 }); + return formio.saveProject(newProject).then((response) => { + assert.deepEqual( + response, + project, + 'Project should match', + ); + }); }, - body: form, - }; - }, - }; - }, - }, - { - name: "Create Submission", - test() { - const formio = new Formio( - `/project/${project._id}/form/${form._id}/submission`, - ); - const req = { - data: { - fieldLabel: chance.string(), - }, - }; - return formio.saveSubmission(req).then((response) => { - assert.deepEqual(response, submission, "Submission should match"); - }); - }, - mock() { - return { - url: `${Formio.getBaseUrl()}/project/${project._id}/form/${form._id}/submission`, - method: "POST", - response(url, opts) { - const body = JSON.parse(opts.body); - const submissionId = generateID(); - submission = { - _id: submissionId, - created: new Date().toISOString(), - modified: new Date().toISOString(), - data: body.data, - externalIds: [], - externalTokens: [], - form: form._id, - owner: user._id, - roles: [], - }; - return { - headers: { - "Content-Type": "application/json", - "x-jwt-token": userToken, + mock() { + return { + url: `${Formio.getBaseUrl()}/project/${project._id}`, + method: 'PUT', + response(url, opts) { + const body = JSON.parse(opts.body); + project = body; + return { + headers: { + 'Content-Type': 'application/json', + 'x-jwt-token': userToken, + }, + body: project, + }; + }, + }; }, - body: submission, - }; }, - }; - }, - }, - { - name: "Load Submissions", - test() { - const formio = new Formio( - `/project/${project._id}/form/${form._id}/submission`, - ); - return formio.loadSubmissions().then((submissions) => { - assert.equal( - submissions.length, - 1, - "Should return only one submission.", - ); - assert.equal(submissions.skip, 0, "skip should be 0."); - assert.equal(submissions.limit, 1, "limit should be 1."); - assert.equal( - submissions.serverCount, - 1, - "serverCount should be 1.", - ); - assert.deepEqual( - submissions[0], - submission, - "Should match submission", - ); - }); - }, - mock() { - return { - url: `${Formio.getBaseUrl()}/project/${project._id}/form/${form._id}/submission`, - method: "GET", - response() { - return { - headers: { - "Content-Type": "application/json", - "Content-Range": "0-0/1", - "Range-Unit": "items", - "x-jwt-token": userToken, + { + name: 'Create Form', + test() { + const formio = new Formio(`/project/${project._id}/form`); + const req = { + title: chance.string({ + length: 10, + pool: 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', + }), + name: chance.string({ + length: 10, + pool: 'abcdefghijklmnopqrstuvwxyz', + }), + path: chance.string({ + length: 10, + pool: 'abcdefghijklmnopqrstuvwxyz', + }), + components: [ + { + defaultValue: '', + input: true, + inputMask: '', + inputType: 'text', + isNew: false, + key: 'fieldLabel', + label: 'Field Label', + multiple: false, + persistent: true, + placeholder: '', + prefix: '', + protected: false, + suffix: '', + tableView: true, + type: 'textfield', + unique: false, + validate: { + required: false, + minLength: '', + maxLength: '', + pattern: '', + custom: '', + customPrivate: false, + }, + }, + { + action: 'submit', + block: false, + disableOnInvalid: true, + input: true, + key: 'submit', + label: 'Submit', + leftIcon: '', + rightIcon: '', + size: 'md', + tableView: false, + theme: 'primary', + type: 'button', + }, + ], + type: 'form', + access: [], + submissionAccess: [], + }; + return formio.saveForm(req).then((response) => { + assert.deepEqual(response, form, 'Form should match'); + }); }, - body: [submission], - }; - }, - }; - }, - }, - { - name: "Read Submission", - test() { - const formio = new Formio( - `/project/${project._id}/form/${form._id}/submission/${submission._id}`, - ); - return formio.loadSubmission().then((response) => { - assert.deepEqual(response, submission, "Submission should match"); - }); - }, - mock() { - return { - url: `${Formio.getBaseUrl()}/project/${project._id}/form/${form._id}/submission/${submission._id}`, - method: "GET", - response() { - return { - headers: { - "Content-Type": "application/json", - "x-jwt-token": userToken, + mock() { + return { + url: `${Formio.getBaseUrl()}/project/${ + project._id + }/form`, + method: 'POST', + response(url, opts) { + const body = JSON.parse(opts.body); + const formId = generateID(); + form = fastCloneDeep(body); + _.assign(form, { + _id: formId, + created: new Date().toISOString(), + modified: new Date().toISOString(), + project: project._id, + owner: user._id, + }); + return { + headers: { + 'Content-Type': 'application/json', + 'x-jwt-token': userToken, + }, + body: form, + }; + }, + }; }, - body: submission, - }; }, - }; - }, - }, - { - name: "Update Submission", - test() { - const formio = new Formio( - `/project/${project._id}/form/${form._id}/submission/${submission._id}`, - ); - const newSubmission = fastCloneDeep(submission); - newSubmission.data.fieldLabel = chance.string(); - return formio.saveSubmission(newSubmission).then((response) => { - assert.deepEqual(response, submission, "Submission should match"); - }); - }, - mock() { - return { - url: `${Formio.getBaseUrl()}/project/${project._id}/form/${form._id}/submission/${submission._id}`, - method: "PUT", - response(url, opts) { - const body = JSON.parse(opts.body); - submission = body; - return { - headers: { - "Content-Type": "application/json", - "x-jwt-token": userToken, + { + name: 'Load Forms', + test() { + const formio = new Formio(`/project/${project._id}/form`); + return formio.loadForms().then((forms) => { + assert.equal( + forms.length, + 1, + 'Should return only one form.', + ); + assert.equal(forms.skip, 0, 'skip should be 0.'); + assert.equal(forms.limit, 1, 'limit should be 1.'); + assert.equal( + forms.serverCount, + 1, + 'serverCount should be 1.', + ); + assert.deepEqual(forms[0], form, 'Should match form'); + }); }, - body: submission, - }; - }, - }; - }, - }, - { - name: "Update Submission without ID", - test() { - const formio = new Formio(`/project/${project._id}/form/${form._id}`); - const newSubmission = fastCloneDeep(submission); - newSubmission.data.fieldLabel = chance.string(); - return formio.saveSubmission(newSubmission).then((response) => { - assert.deepEqual(response, submission, "Submission should match"); - }); - }, - mock() { - return { - url: `${Formio.getBaseUrl()}/project/${project._id}/form/${form._id}/submission/${submission._id}`, - method: "PUT", - response(url, opts) { - const body = JSON.parse(opts.body); - submission = body; - return { - headers: { - "Content-Type": "application/json", - "x-jwt-token": userToken, + mock() { + return { + url: `${Formio.getBaseUrl()}/project/${ + project._id + }/form`, + method: 'GET', + response() { + return { + headers: { + 'Content-Type': 'application/json', + 'Content-Range': '0-0/1', + 'Range-Unit': 'items', + 'x-jwt-token': userToken, + }, + body: [form], + }; + }, + }; }, - body: submission, - }; - }, - }; - }, - }, - // Actions - // Available Actions - // Action Info - { - name: "Delete Submission", - test() { - const formio = new Formio( - `/project/${project._id}/form/${form._id}/submission/${submission._id}`, - ); - return formio.deleteSubmission().then((response) => { - assert.equal(response, "OK", "Submission should be deleted."); - }); - }, - mock() { - return { - url: `${Formio.getBaseUrl()}/project/${project._id}/form/${form._id}/submission/${submission._id}`, - method: "DELETE", - response: { - status: 200, - body: "OK", - headers: { - "Content-Type": "text/plain; charset=utf-8", - "x-jwt-token": userToken, - }, - }, - }; - }, - }, - { - name: "Delete Form", - test() { - const formio = new Formio(`/project/${project._id}/form/${form._id}`); - return formio.deleteForm().then((response) => { - assert.equal(response, "OK", "Submission should be deleted."); - }); - }, - mock() { - return { - url: `${Formio.getBaseUrl()}/project/${project._id}/form/${form._id}`, - method: "DELETE", - response: { - status: 200, - body: "OK", - headers: { - "Content-Type": "text/plain; charset=utf-8", - "x-jwt-token": userToken, - }, - }, - }; - }, - }, - { - name: "Delete Project", - test() { - const formio = new Formio(`/project/${project._id}`); - return formio.deleteProject().then((response) => { - assert.equal(response, "OK", "Submission should be deleted."); - }); - }, - mock() { - return { - url: `${Formio.getBaseUrl()}/project/${project._id}`, - method: "DELETE", - response: { - status: 200, - body: "OK", - headers: { - "Content-Type": "text/plain; charset=utf-8", - "x-jwt-token": userToken, - }, }, - }; - }, - }, - { - name: "Getting Projects", - test() { - return Formio.loadProjects().then((projects) => { - assert.equal(projects.length, 0, "Should return no projects."); - assert.equal(projects.skip, undefined, "skip should be undefined."); - assert.equal( - projects.limit, - undefined, - "limit should be undefined.", - ); - assert.equal(projects.serverCount, 0, "serverCount should be 0."); - }); - }, - mock() { - return { - url: `${Formio.getBaseUrl()}/project`, - method: "GET", - response() { - return { - headers: { - "Content-Type": "application/json", - "Content-Range": "*/0", - "Range-Unit": "items", - "x-jwt-token": userToken, + { + name: 'Read Form', + test() { + const formio = new Formio( + `/project/${project._id}/form/${form._id}`, + ); + return formio.loadForm().then((response) => { + assert.deepEqual(response, form, 'Form should match'); + }); }, - body: [], - }; - }, - }; - }, - }, - { - name: "Temporary Token", - test() { - const formio = new Formio(`/project/${project._id}`); - return formio.getTempToken(200, "GET:/current").then((tempToken) => { - assert.equal(tempToken, userToken); - }); - }, - mock() { - return { - url: `${Formio.getBaseUrl()}/project/${project._id}/token`, - method: "GET", - response() { - return { - status: 200, - body: userToken, - headers: { - "Content-Type": "text/plain; charset=utf-8", - "x-jwt-token": userToken, + mock() { + return { + url: `${Formio.getBaseUrl()}/project/${ + project._id + }/form/${form._id}`, + method: 'GET', + response() { + return { + headers: { + 'Content-Type': 'application/json', + 'x-jwt-token': userToken, + }, + body: form, + }; + }, + }; }, - }; }, - }; - }, - }, - { - name: "Logging Out", - test() { - return Formio.logout().then(() => { - assert.equal(Formio.getToken(), "", "Logged out"); - }); - }, - mock() { - return { - url: `${Formio.getBaseUrl()}/logout`, - method: "GET", - response() { - userToken = null; - return { - status: 200, - body: "OK", - headers: { - "Content-Type": "text/plain; charset=utf-8", - "x-jwt-token": "", + { + name: 'Update Form', + test() { + const formio = new Formio( + `/project/${project._id}/form/${form._id}`, + ); + const newForm = fastCloneDeep(form); + newForm.title = chance.string({ + length: 10, + pool: 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', + }); + newForm.name = chance.string({ + length: 10, + pool: 'abcdefghijklmnopqrstuvwxyz', + }); + newForm.path = chance.string({ + length: 10, + pool: 'abcdefghijklmnopqrstuvwxyz', + }); + return formio.saveForm(newForm).then((response) => { + assert.deepEqual(response, form, 'Form should match'); + }); + }, + mock() { + return { + url: `${Formio.getBaseUrl()}/project/${ + project._id + }/form/${form._id}`, + method: 'PUT', + response(url, opts) { + const body = JSON.parse(opts.body); + form = body; + return { + headers: { + 'Content-Type': 'application/json', + 'x-jwt-token': userToken, + }, + body: form, + }; + }, + }; }, - }; }, - }; - }, - }, - { - name: "userPermissions method should give create_all permission", - test() { - const user = { - _id: "test_user_id", - roles: ["test_role_id"], - }; - - const formio = new Formio(`${Formio.getBaseUrl()}/testform`); - return formio.userPermissions(user).then((permissions) => { - assert.equal(permissions.create, true); - assert.equal(permissions.edit, false); - assert.equal(permissions.delete, false); - assert.equal(permissions.read, false); - }); - }, - mock() { - return [ { - url: `${Formio.getBaseUrl()}/testform`, - method: "GET", - response() { - return { - status: 200, - body: { - submissionAccess: [ - { - type: "create_all", - roles: ["test_role_id"], - }, - ], - }, - }; - }, + name: 'Create Submission', + test() { + const formio = new Formio( + `/project/${project._id}/form/${form._id}/submission`, + ); + const req = { + data: { + fieldLabel: chance.string(), + }, + }; + return formio.saveSubmission(req).then((response) => { + assert.deepEqual( + response, + submission, + 'Submission should match', + ); + }); + }, + mock() { + return { + url: `${Formio.getBaseUrl()}/project/${ + project._id + }/form/${form._id}/submission`, + method: 'POST', + response(url, opts) { + const body = JSON.parse(opts.body); + const submissionId = generateID(); + submission = { + _id: submissionId, + created: new Date().toISOString(), + modified: new Date().toISOString(), + data: body.data, + externalIds: [], + externalTokens: [], + form: form._id, + owner: user._id, + roles: [], + }; + return { + headers: { + 'Content-Type': 'application/json', + 'x-jwt-token': userToken, + }, + body: submission, + }; + }, + }; + }, }, { - url: `${Formio.getBaseUrl()}/access`, - method: "GET", - response() { - return { - status: 200, - body: { - roles: [], - }, - }; - }, + name: 'Load Submissions', + test() { + const formio = new Formio( + `/project/${project._id}/form/${form._id}/submission`, + ); + return formio.loadSubmissions().then((submissions) => { + assert.equal( + submissions.length, + 1, + 'Should return only one submission.', + ); + assert.equal(submissions.skip, 0, 'skip should be 0.'); + assert.equal( + submissions.limit, + 1, + 'limit should be 1.', + ); + assert.equal( + submissions.serverCount, + 1, + 'serverCount should be 1.', + ); + assert.deepEqual( + submissions[0], + submission, + 'Should match submission', + ); + }); + }, + mock() { + return { + url: `${Formio.getBaseUrl()}/project/${ + project._id + }/form/${form._id}/submission`, + method: 'GET', + response() { + return { + headers: { + 'Content-Type': 'application/json', + 'Content-Range': '0-0/1', + 'Range-Unit': 'items', + 'x-jwt-token': userToken, + }, + body: [submission], + }; + }, + }; + }, }, - ]; - }, - }, - { - name: "userPermissions method should give create_own permission", - test() { - const userId = "test_user_id"; - const user = { - _id: userId, - roles: ["test_role_id"], - }; - const submission = { - owner: userId, - }; - const formio = new Formio(`${Formio.getBaseUrl()}/testform`); - return formio - .userPermissions(user, undefined, submission) - .then((permissions) => { - assert.equal(permissions.create, true); - assert.equal(permissions.edit, false); - assert.equal(permissions.read, false); - assert.equal(permissions.delete, false); - }); - }, - mock() { - return [ { - url: `${Formio.getBaseUrl()}/testform`, - method: "GET", - response() { - return { - status: 200, - body: { - submissionAccess: [ - { - type: "create_own", - roles: ["test_role_id"], - }, - ], - }, - }; - }, + name: 'Read Submission', + test() { + const formio = new Formio( + `/project/${project._id}/form/${form._id}/submission/${submission._id}`, + ); + return formio.loadSubmission().then((response) => { + assert.deepEqual( + response, + submission, + 'Submission should match', + ); + }); + }, + mock() { + return { + url: `${Formio.getBaseUrl()}/project/${ + project._id + }/form/${form._id}/submission/${submission._id}`, + method: 'GET', + response() { + return { + headers: { + 'Content-Type': 'application/json', + 'x-jwt-token': userToken, + }, + body: submission, + }; + }, + }; + }, }, { - url: `${Formio.getBaseUrl()}/access`, - method: "GET", - response() { - return { - status: 200, - body: { - roles: [], - }, - }; - }, + name: 'Update Submission', + test() { + const formio = new Formio( + `/project/${project._id}/form/${form._id}/submission/${submission._id}`, + ); + const newSubmission = fastCloneDeep(submission); + newSubmission.data.fieldLabel = chance.string(); + return formio + .saveSubmission(newSubmission) + .then((response) => { + assert.deepEqual( + response, + submission, + 'Submission should match', + ); + }); + }, + mock() { + return { + url: `${Formio.getBaseUrl()}/project/${ + project._id + }/form/${form._id}/submission/${submission._id}`, + method: 'PUT', + response(url, opts) { + const body = JSON.parse(opts.body); + submission = body; + return { + headers: { + 'Content-Type': 'application/json', + 'x-jwt-token': userToken, + }, + body: submission, + }; + }, + }; + }, }, - ]; - }, - }, - { - name: "userPermissions method should give permissions for Anonymous role", - test() { - const user = { - _id: false, - roles: [], - }; - const formio = new Formio(`${Formio.getBaseUrl()}/testform`); - return formio.userPermissions(user).then((permissions) => { - assert.equal(permissions.create, true); - assert.equal(permissions.edit, false); - assert.equal(permissions.read, false); - assert.equal(permissions.delete, false); - }); - }, - mock() { - return [ { - url: `${Formio.getBaseUrl()}/testform`, - method: "GET", - response() { - return { - status: 200, - body: { - submissionAccess: [ - { - type: "create_all", - roles: ["test_anonymous_role_id"], - }, - ], - }, - }; - }, + name: 'Update Submission without ID', + test() { + const formio = new Formio( + `/project/${project._id}/form/${form._id}`, + ); + const newSubmission = fastCloneDeep(submission); + newSubmission.data.fieldLabel = chance.string(); + return formio + .saveSubmission(newSubmission) + .then((response) => { + assert.deepEqual( + response, + submission, + 'Submission should match', + ); + }); + }, + mock() { + return { + url: `${Formio.getBaseUrl()}/project/${ + project._id + }/form/${form._id}/submission/${submission._id}`, + method: 'PUT', + response(url, opts) { + const body = JSON.parse(opts.body); + submission = body; + return { + headers: { + 'Content-Type': 'application/json', + 'x-jwt-token': userToken, + }, + body: submission, + }; + }, + }; + }, }, + // Actions + // Available Actions + // Action Info { - url: `${Formio.getBaseUrl()}/access`, - method: "GET", - response() { - return { - status: 200, - body: { - roles: [ - { - _id: "test_anonymous_role_id", - default: true, - }, - ], - }, - }; - }, + name: 'Delete Submission', + test() { + const formio = new Formio( + `/project/${project._id}/form/${form._id}/submission/${submission._id}`, + ); + return formio.deleteSubmission().then((response) => { + assert.equal( + response, + 'OK', + 'Submission should be deleted.', + ); + }); + }, + mock() { + return { + url: `${Formio.getBaseUrl()}/project/${ + project._id + }/form/${form._id}/submission/${submission._id}`, + method: 'DELETE', + response: { + status: 200, + body: 'OK', + headers: { + 'Content-Type': 'text/plain; charset=utf-8', + 'x-jwt-token': userToken, + }, + }, + }; + }, }, - ]; - }, - }, - { - name: "userPermissions method should give all permissions for admin role", - test() { - const user = { - roles: ["test_admin_role"], - }; - const formio = new Formio(`${Formio.getBaseUrl()}/testform`); - return formio.userPermissions(user).then((permissions) => { - assert.equal(permissions.create, true); - assert.equal(permissions.read, true); - assert.equal(permissions.edit, true); - assert.equal(permissions.delete, true); - }); - }, - mock() { - return [ { - url: `${Formio.getBaseUrl()}/testform`, - method: "GET", - response() { - return { - status: 200, - body: { - submissionAccess: [], - }, - }; - }, + name: 'Delete Form', + test() { + const formio = new Formio( + `/project/${project._id}/form/${form._id}`, + ); + return formio.deleteForm().then((response) => { + assert.equal( + response, + 'OK', + 'Submission should be deleted.', + ); + }); + }, + mock() { + return { + url: `${Formio.getBaseUrl()}/project/${ + project._id + }/form/${form._id}`, + method: 'DELETE', + response: { + status: 200, + body: 'OK', + headers: { + 'Content-Type': 'text/plain; charset=utf-8', + 'x-jwt-token': userToken, + }, + }, + }; + }, }, { - url: `${Formio.getBaseUrl()}/access`, - method: "GET", - response() { - return { - status: 200, - body: { - roles: [ - { - _id: "test_admin_role", - admin: true, - }, - ], - }, - }; - }, - }, - ]; - }, - }, - { - name: "userPermissions method should give only group read permission for `read` level", - test() { - const user = { - roles: ["test_group_id"], - }; - const submission = { - data: { - groupField: { - _id: "test_group_id", - }, + name: 'Delete Project', + test() { + const formio = new Formio(`/project/${project._id}`); + return formio.deleteProject().then((response) => { + assert.equal( + response, + 'OK', + 'Submission should be deleted.', + ); + }); + }, + mock() { + return { + url: `${Formio.getBaseUrl()}/project/${project._id}`, + method: 'DELETE', + response: { + status: 200, + body: 'OK', + headers: { + 'Content-Type': 'text/plain; charset=utf-8', + 'x-jwt-token': userToken, + }, + }, + }; + }, }, - }; - const formio = new Formio(`${Formio.getBaseUrl()}/testform`); - return formio - .userPermissions(user, undefined, submission) - .then((permissions) => { - assert.equal(permissions.create, false); - assert.equal(permissions.read, true); - assert.equal(permissions.edit, false); - assert.equal(permissions.delete, false); - }); - }, - mock() { - return [ { - url: `${Formio.getBaseUrl()}/testform`, - method: "GET", - response() { - return { - status: 200, - body: { - submissionAccess: [], - components: [ - { - defaultPermission: "read", - key: "groupField", - }, - ], - }, - }; - }, + name: 'Getting Projects', + test() { + return Formio.loadProjects().then((projects) => { + assert.equal( + projects.length, + 0, + 'Should return no projects.', + ); + assert.equal( + projects.skip, + undefined, + 'skip should be undefined.', + ); + assert.equal( + projects.limit, + undefined, + 'limit should be undefined.', + ); + assert.equal( + projects.serverCount, + 0, + 'serverCount should be 0.', + ); + }); + }, + mock() { + return { + url: `${Formio.getBaseUrl()}/project`, + method: 'GET', + response() { + return { + headers: { + 'Content-Type': 'application/json', + 'Content-Range': '*/0', + 'Range-Unit': 'items', + 'x-jwt-token': userToken, + }, + body: [], + }; + }, + }; + }, }, { - url: `${Formio.getBaseUrl()}/access`, - method: "GET", - response() { - return { - status: 200, - body: { - roles: [], - }, - }; - }, - }, - ]; - }, - }, - { - name: "userPermissions method should give group read and create permissions for `create` level", - test() { - const user = { - roles: ["test_group_id"], - }; - const submission = { - data: { - groupField: { - _id: "test_group_id", - }, + name: 'Temporary Token', + test() { + const formio = new Formio(`/project/${project._id}`); + return formio + .getTempToken(200, 'GET:/current') + .then((tempToken) => { + assert.equal(tempToken, userToken); + }); + }, + mock() { + return { + url: `${Formio.getBaseUrl()}/project/${ + project._id + }/token`, + method: 'GET', + response() { + return { + status: 200, + body: userToken, + headers: { + 'Content-Type': 'text/plain; charset=utf-8', + 'x-jwt-token': userToken, + }, + }; + }, + }; + }, }, - }; - const formio = new Formio(`${Formio.getBaseUrl()}/testform`); - return formio - .userPermissions(user, undefined, submission) - .then((permissions) => { - assert.equal(permissions.create, true); - assert.equal(permissions.read, true); - assert.equal(permissions.edit, false); - assert.equal(permissions.delete, false); - }); - }, - mock() { - return [ { - url: `${Formio.getBaseUrl()}/testform`, - method: "GET", - response() { - return { - status: 200, - body: { - submissionAccess: [], - components: [ - { - defaultPermission: "create", - key: "groupField", - }, - ], - }, - }; - }, + name: 'Logging Out', + test() { + return Formio.logout().then(() => { + assert.equal(Formio.getToken(), '', 'Logged out'); + }); + }, + mock() { + return { + url: `${Formio.getBaseUrl()}/logout`, + method: 'GET', + response() { + userToken = null; + return { + status: 200, + body: 'OK', + headers: { + 'Content-Type': 'text/plain; charset=utf-8', + 'x-jwt-token': '', + }, + }; + }, + }; + }, }, { - url: `${Formio.getBaseUrl()}/access`, - method: "GET", - response() { - return { - status: 200, - body: { - roles: [], - }, - }; - }, - }, - ]; - }, - }, - { - name: "userPermissions method should give group read, create and edit permissions for `write` level", - test() { - const user = { - roles: ["test_group_id"], - }; - const submission = { - data: { - groupField: { - _id: "test_group_id", - }, + name: 'userPermissions method should give create_all permission', + test() { + const user = { + _id: 'test_user_id', + roles: ['test_role_id'], + }; + + const formio = new Formio( + `${Formio.getBaseUrl()}/testform`, + ); + return formio.userPermissions(user).then((permissions) => { + assert.equal(permissions.create, true); + assert.equal(permissions.edit, false); + assert.equal(permissions.delete, false); + assert.equal(permissions.read, false); + }); + }, + mock() { + return [ + { + url: `${Formio.getBaseUrl()}/testform`, + method: 'GET', + response() { + return { + status: 200, + body: { + submissionAccess: [ + { + type: 'create_all', + roles: ['test_role_id'], + }, + ], + }, + }; + }, + }, + { + url: `${Formio.getBaseUrl()}/access`, + method: 'GET', + response() { + return { + status: 200, + body: { + roles: [], + }, + }; + }, + }, + ]; + }, }, - }; - const formio = new Formio(`${Formio.getBaseUrl()}/testform`); - return formio - .userPermissions(user, undefined, submission) - .then((permissions) => { - assert.equal(permissions.create, true); - assert.equal(permissions.read, true); - assert.equal(permissions.edit, true); - assert.equal(permissions.delete, false); - }); - }, - mock() { - return [ { - url: `${Formio.getBaseUrl()}/testform`, - method: "GET", - response() { - return { - status: 200, - body: { - submissionAccess: [], - components: [ - { - defaultPermission: "write", - key: "groupField", - }, - ], - }, - }; - }, + name: 'userPermissions method should give create_own permission', + test() { + const userId = 'test_user_id'; + const user = { + _id: userId, + roles: ['test_role_id'], + }; + const submission = { + owner: userId, + }; + const formio = new Formio( + `${Formio.getBaseUrl()}/testform`, + ); + return formio + .userPermissions(user, undefined, submission) + .then((permissions) => { + assert.equal(permissions.create, true); + assert.equal(permissions.edit, false); + assert.equal(permissions.read, false); + assert.equal(permissions.delete, false); + }); + }, + mock() { + return [ + { + url: `${Formio.getBaseUrl()}/testform`, + method: 'GET', + response() { + return { + status: 200, + body: { + submissionAccess: [ + { + type: 'create_own', + roles: ['test_role_id'], + }, + ], + }, + }; + }, + }, + { + url: `${Formio.getBaseUrl()}/access`, + method: 'GET', + response() { + return { + status: 200, + body: { + roles: [], + }, + }; + }, + }, + ]; + }, }, { - url: `${Formio.getBaseUrl()}/access`, - method: "GET", - response() { - return { - status: 200, - body: { - roles: [], - }, - }; - }, + name: 'userPermissions method should give permissions for Anonymous role', + test() { + const user = { + _id: false, + roles: [], + }; + const formio = new Formio( + `${Formio.getBaseUrl()}/testform`, + ); + return formio.userPermissions(user).then((permissions) => { + assert.equal(permissions.create, true); + assert.equal(permissions.edit, false); + assert.equal(permissions.read, false); + assert.equal(permissions.delete, false); + }); + }, + mock() { + return [ + { + url: `${Formio.getBaseUrl()}/testform`, + method: 'GET', + response() { + return { + status: 200, + body: { + submissionAccess: [ + { + type: 'create_all', + roles: [ + 'test_anonymous_role_id', + ], + }, + ], + }, + }; + }, + }, + { + url: `${Formio.getBaseUrl()}/access`, + method: 'GET', + response() { + return { + status: 200, + body: { + roles: [ + { + _id: 'test_anonymous_role_id', + default: true, + }, + ], + }, + }; + }, + }, + ]; + }, }, - ]; - }, - }, - { - name: "userPermissions method should give all group permissions for `admin` level", - test() { - const user = { - roles: ["test_group_id"], - }; - const submission = { - data: { - groupField: { - _id: "test_group_id", - }, + { + name: 'userPermissions method should give all permissions for admin role', + test() { + const user = { + roles: ['test_admin_role'], + }; + const formio = new Formio( + `${Formio.getBaseUrl()}/testform`, + ); + return formio.userPermissions(user).then((permissions) => { + assert.equal(permissions.create, true); + assert.equal(permissions.read, true); + assert.equal(permissions.edit, true); + assert.equal(permissions.delete, true); + }); + }, + mock() { + return [ + { + url: `${Formio.getBaseUrl()}/testform`, + method: 'GET', + response() { + return { + status: 200, + body: { + submissionAccess: [], + }, + }; + }, + }, + { + url: `${Formio.getBaseUrl()}/access`, + method: 'GET', + response() { + return { + status: 200, + body: { + roles: [ + { + _id: 'test_admin_role', + admin: true, + }, + ], + }, + }; + }, + }, + ]; + }, }, - }; - const formio = new Formio(`${Formio.getBaseUrl()}/testform`); - return formio - .userPermissions(user, undefined, submission) - .then((permissions) => { - assert.equal(permissions.create, true); - assert.equal(permissions.read, true); - assert.equal(permissions.edit, true); - assert.equal(permissions.delete, true); - }); - }, - mock() { - return [ { - url: `${Formio.getBaseUrl()}/testform`, - method: "GET", - response() { - return { - status: 200, - body: { - submissionAccess: [], - components: [ - { - defaultPermission: "admin", - key: "groupField", - }, - ], - }, - }; - }, + name: 'userPermissions method should give only group read permission for `read` level', + test() { + const user = { + roles: ['test_group_id'], + }; + const submission = { + data: { + groupField: { + _id: 'test_group_id', + }, + }, + }; + const formio = new Formio( + `${Formio.getBaseUrl()}/testform`, + ); + return formio + .userPermissions(user, undefined, submission) + .then((permissions) => { + assert.equal(permissions.create, false); + assert.equal(permissions.read, true); + assert.equal(permissions.edit, false); + assert.equal(permissions.delete, false); + }); + }, + mock() { + return [ + { + url: `${Formio.getBaseUrl()}/testform`, + method: 'GET', + response() { + return { + status: 200, + body: { + submissionAccess: [], + components: [ + { + defaultPermission: 'read', + key: 'groupField', + }, + ], + }, + }; + }, + }, + { + url: `${Formio.getBaseUrl()}/access`, + method: 'GET', + response() { + return { + status: 200, + body: { + roles: [], + }, + }; + }, + }, + ]; + }, }, { - url: `${Formio.getBaseUrl()}/access`, - method: "GET", - response() { - return { - status: 200, - body: { - roles: [], - }, - }; - }, + name: 'userPermissions method should give group read and create permissions for `create` level', + test() { + const user = { + roles: ['test_group_id'], + }; + const submission = { + data: { + groupField: { + _id: 'test_group_id', + }, + }, + }; + const formio = new Formio( + `${Formio.getBaseUrl()}/testform`, + ); + return formio + .userPermissions(user, undefined, submission) + .then((permissions) => { + assert.equal(permissions.create, true); + assert.equal(permissions.read, true); + assert.equal(permissions.edit, false); + assert.equal(permissions.delete, false); + }); + }, + mock() { + return [ + { + url: `${Formio.getBaseUrl()}/testform`, + method: 'GET', + response() { + return { + status: 200, + body: { + submissionAccess: [], + components: [ + { + defaultPermission: 'create', + key: 'groupField', + }, + ], + }, + }; + }, + }, + { + url: `${Formio.getBaseUrl()}/access`, + method: 'GET', + response() { + return { + status: 200, + body: { + roles: [], + }, + }; + }, + }, + ]; + }, }, - ]; - }, - }, - { - name: "userPermissions method should handle submission with multiple groups", - test() { - const user1 = { - roles: ["test_group_id1"], - }; - const user2 = { - roles: ["test_group_id2"], - }; - const submission = { - data: { - groupField: [ - { - _id: "test_group_id1", + { + name: 'userPermissions method should give group read, create and edit permissions for `write` level', + test() { + const user = { + roles: ['test_group_id'], + }; + const submission = { + data: { + groupField: { + _id: 'test_group_id', + }, + }, + }; + const formio = new Formio( + `${Formio.getBaseUrl()}/testform`, + ); + return formio + .userPermissions(user, undefined, submission) + .then((permissions) => { + assert.equal(permissions.create, true); + assert.equal(permissions.read, true); + assert.equal(permissions.edit, true); + assert.equal(permissions.delete, false); + }); }, - { - _id: "test_group_id2", + mock() { + return [ + { + url: `${Formio.getBaseUrl()}/testform`, + method: 'GET', + response() { + return { + status: 200, + body: { + submissionAccess: [], + components: [ + { + defaultPermission: 'write', + key: 'groupField', + }, + ], + }, + }; + }, + }, + { + url: `${Formio.getBaseUrl()}/access`, + method: 'GET', + response() { + return { + status: 200, + body: { + roles: [], + }, + }; + }, + }, + ]; }, - ], }, - }; - const formio = new Formio(`${Formio.getBaseUrl()}/testform`); - return Promise.all([ - formio - .userPermissions(user1, undefined, submission) - .then((permissions) => { - assert.equal(permissions.create, false); - assert.equal(permissions.read, true); - assert.equal(permissions.edit, false); - assert.equal(permissions.delete, false); - }), - formio - .userPermissions(user2, undefined, submission) - .then((permissions) => { - assert.equal(permissions.create, false); - assert.equal(permissions.read, true); - assert.equal(permissions.edit, false); - assert.equal(permissions.delete, false); - }), - ]); - }, - mock() { - return [ { - url: `${Formio.getBaseUrl()}/testform`, - method: "GET", - response() { - return { - status: 200, - body: { - submissionAccess: [], - components: [ - { - defaultPermission: "read", - key: "groupField", - }, - ], - }, - }; - }, + name: 'userPermissions method should give all group permissions for `admin` level', + test() { + const user = { + roles: ['test_group_id'], + }; + const submission = { + data: { + groupField: { + _id: 'test_group_id', + }, + }, + }; + const formio = new Formio( + `${Formio.getBaseUrl()}/testform`, + ); + return formio + .userPermissions(user, undefined, submission) + .then((permissions) => { + assert.equal(permissions.create, true); + assert.equal(permissions.read, true); + assert.equal(permissions.edit, true); + assert.equal(permissions.delete, true); + }); + }, + mock() { + return [ + { + url: `${Formio.getBaseUrl()}/testform`, + method: 'GET', + response() { + return { + status: 200, + body: { + submissionAccess: [], + components: [ + { + defaultPermission: 'admin', + key: 'groupField', + }, + ], + }, + }; + }, + }, + { + url: `${Formio.getBaseUrl()}/access`, + method: 'GET', + response() { + return { + status: 200, + body: { + roles: [], + }, + }; + }, + }, + ]; + }, }, { - url: `${Formio.getBaseUrl()}/access`, - method: "GET", - response() { - return { - status: 200, - body: { - roles: [], - }, - }; - }, + name: 'userPermissions method should handle submission with multiple groups', + test() { + const user1 = { + roles: ['test_group_id1'], + }; + const user2 = { + roles: ['test_group_id2'], + }; + const submission = { + data: { + groupField: [ + { + _id: 'test_group_id1', + }, + { + _id: 'test_group_id2', + }, + ], + }, + }; + const formio = new Formio( + `${Formio.getBaseUrl()}/testform`, + ); + return Promise.all([ + formio + .userPermissions(user1, undefined, submission) + .then((permissions) => { + assert.equal(permissions.create, false); + assert.equal(permissions.read, true); + assert.equal(permissions.edit, false); + assert.equal(permissions.delete, false); + }), + formio + .userPermissions(user2, undefined, submission) + .then((permissions) => { + assert.equal(permissions.create, false); + assert.equal(permissions.read, true); + assert.equal(permissions.edit, false); + assert.equal(permissions.delete, false); + }), + ]); + }, + mock() { + return [ + { + url: `${Formio.getBaseUrl()}/testform`, + method: 'GET', + response() { + return { + status: 200, + body: { + submissionAccess: [], + components: [ + { + defaultPermission: 'read', + key: 'groupField', + }, + ], + }, + }; + }, + }, + { + url: `${Formio.getBaseUrl()}/access`, + method: 'GET', + response() { + return { + status: 200, + body: { + roles: [], + }, + }; + }, + }, + ]; + }, }, - ]; - }, - }, - ]; - - tests.forEach(testCapability); - }); + ]; - describe("Formio.currentUser", function () { - let plugin = null; - - beforeEach(function () { - plugin = { - wrapStaticRequestPromise: sinon.spy((promise) => promise), - staticRequest: sinon.spy(() => { - // Return dummy user - const userId = generateID(); - return Promise.resolve({ - _id: userId, - created: new Date().toISOString(), - modified: new Date().toISOString(), - data: { - email: "user@place.com", - name: "user", - }, - externalIds: [], - externalTokens: [], - form: generateID(), - owner: userId, - }); - }), - }; - Formio.registerPlugin(plugin, "currentUserTestPlugin"); + tests.forEach(testCapability); }); - afterEach(function () { - Formio.deregisterPlugin(plugin); - }); + describe('Formio.currentUser', function () { + let plugin = null; - it("Initial currentUser() should make static request", function (done) { - // Force token - Formio.token = chance.string({ length: 30 }); - Formio.currentUser().then(() => { - assert.ok( - plugin.staticRequest.calledOnce, - "staticRequest should be called once", - ); - done(); - }); - assert.ok( - plugin.wrapStaticRequestPromise.calledOnce, - "wrapStaticRequestPromise should be called once", - ); - }); + beforeEach(function () { + plugin = { + wrapStaticRequestPromise: sinon.spy((promise) => promise), + staticRequest: sinon.spy(() => { + // Return dummy user + const userId = generateID(); + return Promise.resolve({ + _id: userId, + created: new Date().toISOString(), + modified: new Date().toISOString(), + data: { + email: 'user@place.com', + name: 'user', + }, + externalIds: [], + externalTokens: [], + form: generateID(), + owner: userId, + }); + }), + }; + Formio.registerPlugin(plugin, 'currentUserTestPlugin'); + }); - it("Next currentUser() should return cached value", function (done) { - // Clear token - Formio.currentUser().then(() => { - assert.ok( - !plugin.staticRequest.called, - "staticRequest should not be called", - ); - done(); - }); - assert.ok( - plugin.wrapStaticRequestPromise.calledOnce, - "wrapStaticRequestPromise should be called once", - ); - }); + afterEach(function () { + Formio.deregisterPlugin(plugin); + }); - it("Should render after form submission if renderMode = 'html'", function (done) { - const formJson = { - components: [ - { - label: "Text Field", - tableView: true, - key: "textField", - type: "textfield", - input: true, - }, - { - label: "Phone Number", - tableView: true, - key: "phoneNumber", - type: "phoneNumber", - input: true, - }, - ], - }; - const element = document.createElement("div"); - Formio.createForm(element, formJson, { renderMode: "html" }) - .then((form) => { - const textField = form.getComponent("textField"); - const phoneNumber = form.getComponent("phoneNumber"); - assert.equal( - textField.element.querySelector("[ref=value]").innerHTML, - "-", - ); - assert.equal( - phoneNumber.element.querySelector("[ref=value]").innerHTML, - "-", - ); - form.submission = { - data: { - textField: "textField", - phoneNumber: "88005553535", - }, - }; - setTimeout(() => { - assert.equal( - textField.element.querySelector("[ref=value]").innerHTML, - "textField", - ); - assert.equal( - phoneNumber.element.querySelector("[ref=value]").innerHTML, - "88005553535", + it('Initial currentUser() should make static request', function (done) { + // Force token + Formio.token = chance.string({ length: 30 }); + Formio.currentUser().then(() => { + assert.ok( + plugin.staticRequest.calledOnce, + 'staticRequest should be called once', + ); + done(); + }); + assert.ok( + plugin.wrapStaticRequestPromise.calledOnce, + 'wrapStaticRequestPromise should be called once', ); - done(); - }, 300); - }) - .catch(done); - }); + }); - it("Should render after form submission if renderMode = 'html' with Nested Form", function (done) { - const formJson = { - components: [ - { - label: "Form", - key: "form", - type: "form", - input: true, - components: [ - { - label: "Text Field", - key: "textField", - type: "textfield", - input: true, - }, - { - label: "Password", - key: "password", - type: "password", - input: true, - }, - ], - }, - { - label: "Checkbox", - type: "checkbox", - input: true, - }, - ], - }; - const element = document.createElement("div"); - Formio.createForm(element, formJson, { renderMode: "html" }) - .then((form) => { - assert.equal( - form.getComponent("textField").element.querySelector("[ref=value]") - .innerHTML, - "-", - ); - assert.equal( - form.getComponent("password").element.querySelector("[ref=value]") - .innerHTML, - "-", - ); - form.submission = { - data: { - form: { - data: { - textField: "textField", - password: "password", - }, - }, - }, - }; - setTimeout(() => { - assert.equal( - form - .getComponent("textField") - .element.querySelector("[ref=value]").innerHTML, - "textField", - ); - assert.equal( - form.getComponent("password").element.querySelector("[ref=value]") - .innerHTML, - "password", + it('Next currentUser() should return cached value', function (done) { + // Clear token + Formio.currentUser().then(() => { + assert.ok( + !plugin.staticRequest.called, + 'staticRequest should not be called', + ); + done(); + }); + assert.ok( + plugin.wrapStaticRequestPromise.calledOnce, + 'wrapStaticRequestPromise should be called once', ); - done(); - }, 300); - }) - .catch(done); + }); + + it("Should render after form submission if renderMode = 'html'", function (done) { + const formJson = { + components: [ + { + label: 'Text Field', + tableView: true, + key: 'textField', + type: 'textfield', + input: true, + }, + { + label: 'Phone Number', + tableView: true, + key: 'phoneNumber', + type: 'phoneNumber', + input: true, + }, + ], + }; + const element = document.createElement('div'); + Formio.createForm(element, formJson, { renderMode: 'html' }) + .then((form) => { + const textField = form.getComponent('textField'); + const phoneNumber = form.getComponent('phoneNumber'); + assert.equal( + textField.element.querySelector('[ref=value]') + .innerHTML, + '-', + ); + assert.equal( + phoneNumber.element.querySelector('[ref=value]') + .innerHTML, + '-', + ); + form.submission = { + data: { + textField: 'textField', + phoneNumber: '88005553535', + }, + }; + setTimeout(() => { + assert.equal( + textField.element.querySelector('[ref=value]') + .innerHTML, + 'textField', + ); + assert.equal( + phoneNumber.element.querySelector('[ref=value]') + .innerHTML, + '88005553535', + ); + done(); + }, 300); + }) + .catch(done); + }); + + it("Should render after form submission if renderMode = 'html' with Nested Form", function (done) { + const formJson = { + components: [ + { + label: 'Form', + key: 'form', + type: 'form', + input: true, + components: [ + { + label: 'Text Field', + key: 'textField', + type: 'textfield', + input: true, + }, + { + label: 'Password', + key: 'password', + type: 'password', + input: true, + }, + ], + }, + { + label: 'Checkbox', + type: 'checkbox', + input: true, + }, + ], + }; + const element = document.createElement('div'); + Formio.createForm(element, formJson, { renderMode: 'html' }) + .then((form) => { + assert.equal( + form + .getComponent('textField') + .element.querySelector('[ref=value]').innerHTML, + '-', + ); + assert.equal( + form + .getComponent('password') + .element.querySelector('[ref=value]').innerHTML, + '-', + ); + form.submission = { + data: { + form: { + data: { + textField: 'textField', + password: 'password', + }, + }, + }, + }; + setTimeout(() => { + assert.equal( + form + .getComponent('textField') + .element.querySelector('[ref=value]').innerHTML, + 'textField', + ); + assert.equal( + form + .getComponent('password') + .element.querySelector('[ref=value]').innerHTML, + 'password', + ); + done(); + }, 300); + }) + .catch(done); + }); }); - }); }); diff --git a/src/PDF.js b/src/PDF.js index aa3cd4469f..b47a90b679 100644 --- a/src/PDF.js +++ b/src/PDF.js @@ -3,337 +3,380 @@ import Webform from './Webform'; import { fastCloneDeep, eachComponent } from './utils/utils'; export default class PDF extends Webform { - constructor(element, options) { - options.display = 'pdf'; - super(element, options); - this.components = []; - } - - init() { - super.init(); - - // Handle an iframe submission. - this.on('iframe-submission', (submission) => this.setValue(submission, { - fromIframe: true - }), true); - - this.on('iframe-change', (submission) => this.setValue(submission, { - fromIframe: true - }), true); - - this.on('iframe-getIframePositions', (query) => { - const iframe = document.getElementById(`iframe-${query.formId}`); - if (iframe) { - const iframeBoundingClientRect = iframe.getBoundingClientRect(); - this.postMessage({ - name: 'iframePositions', - data: { - formId: query.formId, - iframe: { - top: iframeBoundingClientRect.top - }, - scrollY: window.scrollY || window.pageYOffset - } + constructor(element, options) { + options.display = 'pdf'; + super(element, options); + this.components = []; + } + + init() { + super.init(); + + // Handle an iframe submission. + this.on( + 'iframe-submission', + (submission) => + this.setValue(submission, { + fromIframe: true, + }), + true, + ); + + this.on( + 'iframe-change', + (submission) => + this.setValue(submission, { + fromIframe: true, + }), + true, + ); + + this.on('iframe-getIframePositions', (query) => { + const iframe = document.getElementById(`iframe-${query.formId}`); + if (iframe) { + const iframeBoundingClientRect = iframe.getBoundingClientRect(); + this.postMessage({ + name: 'iframePositions', + data: { + formId: query.formId, + iframe: { + top: iframeBoundingClientRect.top, + }, + scrollY: window.scrollY || window.pageYOffset, + }, + }); + } }); - } - }); - // Trigger when this form is ready. - this.on('iframe-ready', () => this.iframeReadyResolve(), true); - } - - render() { - this.submitButton = this.addComponent({ - disabled: this.form.disableWizardSubmit, - input: true, - type: 'button', - action: 'submit', - internal: true, - label: 'Submit', - key: 'submit', - ref: 'button', - hidden: this.isSubmitButtonHidden() - }); + // Trigger when this form is ready. + this.on('iframe-ready', () => this.iframeReadyResolve(), true); + } - return this.renderTemplate('pdf', { - submitButton: this.submitButton.render(), - classes: 'formio-form-pdf', - children: this.renderComponents() - }); - } + render() { + this.submitButton = this.addComponent({ + disabled: this.form.disableWizardSubmit, + input: true, + type: 'button', + action: 'submit', + internal: true, + label: 'Submit', + key: 'submit', + ref: 'button', + hidden: this.isSubmitButtonHidden(), + }); - redraw() { - this.postMessage({ name: 'redraw' }); - return this.builderMode ? Promise.resolve() : super.redraw(); - } + return this.renderTemplate('pdf', { + submitButton: this.submitButton.render(), + classes: 'formio-form-pdf', + children: this.renderComponents(), + }); + } - destroy(all = false) { - if (this.iframeElement) { - delete this.iframeElement.formioComponent; - this.iframeElement.formioComponent = null; + redraw() { + this.postMessage({ name: 'redraw' }); + return this.builderMode ? Promise.resolve() : super.redraw(); } - super.destroy(all); - } - - rebuild() { - if (this.attached && this.builderMode && this.component.components) { - this.destroyComponents(); - this.addComponents(); - return Promise.resolve(); + + destroy(all = false) { + if (this.iframeElement) { + delete this.iframeElement.formioComponent; + this.iframeElement.formioComponent = null; + } + super.destroy(all); } - this.postMessage({ name: 'redraw' }); - return super.rebuild(); - } - - // Do not attach nested components for pdf. - attachComponents(element, components, container) { - components = components || this.components; - container = container || this.component.components; - element = this.hook('attachComponents', element, components, container, this); - return Promise.resolve(); - } - - attach(element) { - return super.attach(element).then(() => { - this.loadRefs(element, { - button: 'single', - buttonMessageContainer: 'single', - buttonMessage: 'single', - zoomIn: 'single', - zoomOut: 'single', - iframeContainer: 'single' - }); - this.submitButton.refs = { ...this.refs }; - this.submitButton.attachButton(); - - // Reset the iframeReady promise. - this.iframeReady = new Promise((resolve, reject) => { - this.iframeReadyResolve = resolve; - this.iframeReadyReject = reject; - }); - - // iframes cannot be in the template so manually create it - this.iframeElement = this.ce('iframe', { - src: this.getSrc(), - id: `iframe-${this.id}`, - seamless: true, - class: 'formio-iframe' - }); - - this.iframeElement.formioContainer = this.component.components; - this.iframeElement.formioComponent = this; - - // Append the iframe to the iframeContainer in the template - this.empty(this.refs.iframeContainer); - this.appendChild(this.refs.iframeContainer, this.iframeElement); - - // Post the form to the iframe - this.form.base = Formio.getBaseUrl(); - this.form.projectUrl = Formio.getProjectUrl(); - this.postMessage({ name: 'form', data: this.form }); - - // Hide the submit button if the associated component is hidden - const submitButton = this.components.find(c => c.element === this.refs.button); - if (submitButton) { - this.refs.button.classList.toggle('hidden', !submitButton.visible); - } - - this.addEventListener(this.refs.zoomIn, 'click', (event) => { - event.preventDefault(); - this.postMessage({ name: 'zoomIn' }); - }); - - this.addEventListener(this.refs.zoomOut, 'click', (event) => { - event.preventDefault(); - this.postMessage({ name: 'zoomOut' }); - }); - - const form = fastCloneDeep(this.form); - if (this.formio) { - form.projectUrl = this.formio.projectUrl; - form.url = this.formio.formUrl; - form.base = this.formio.base; - this.postMessage({ name: 'token', data: this.formio.getToken() }); - } - - this.emit('attach'); - }); - } - - /** - * Get the submission from the iframe. - * - * @return {Promise} - */ - getSubmission() { - return new Promise((resolve) => { - this.once('iframe-submission', resolve); - this.postMessage({ name: 'getSubmission' }); - }); - } - - /** - * Ensure we have the submission from the iframe before we submit the form. - * - * @param options - * @return {*} - */ - submitForm(options = {}) { - this.postMessage({ name: 'getErrors' }); - return this.getSubmission().then(() => super.submitForm(options)); - } - - getSrc() { - if (!this._form || !this._form.settings || !this._form.settings.pdf) { - return ''; + + rebuild() { + if (this.attached && this.builderMode && this.component.components) { + this.destroyComponents(); + this.addComponents(); + return Promise.resolve(); + } + this.postMessage({ name: 'redraw' }); + return super.rebuild(); } - let iframeSrc = `${this._form.settings.pdf.src}.html`; - const params = [`id=${this.id}`]; + // Do not attach nested components for pdf. + attachComponents(element, components, container) { + components = components || this.components; + container = container || this.component.components; + element = this.hook( + 'attachComponents', + element, + components, + container, + this, + ); + return Promise.resolve(); + } - if (this.options.showCheckboxBackground || this._form.settings.showCheckboxBackground) { - params.push('checkboxbackground=1'); + attach(element) { + return super.attach(element).then(() => { + this.loadRefs(element, { + button: 'single', + buttonMessageContainer: 'single', + buttonMessage: 'single', + zoomIn: 'single', + zoomOut: 'single', + iframeContainer: 'single', + }); + this.submitButton.refs = { ...this.refs }; + this.submitButton.attachButton(); + + // Reset the iframeReady promise. + this.iframeReady = new Promise((resolve, reject) => { + this.iframeReadyResolve = resolve; + this.iframeReadyReject = reject; + }); + + // iframes cannot be in the template so manually create it + this.iframeElement = this.ce('iframe', { + src: this.getSrc(), + id: `iframe-${this.id}`, + seamless: true, + class: 'formio-iframe', + }); + + this.iframeElement.formioContainer = this.component.components; + this.iframeElement.formioComponent = this; + + // Append the iframe to the iframeContainer in the template + this.empty(this.refs.iframeContainer); + this.appendChild(this.refs.iframeContainer, this.iframeElement); + + // Post the form to the iframe + this.form.base = Formio.getBaseUrl(); + this.form.projectUrl = Formio.getProjectUrl(); + this.postMessage({ name: 'form', data: this.form }); + + // Hide the submit button if the associated component is hidden + const submitButton = this.components.find( + (c) => c.element === this.refs.button, + ); + if (submitButton) { + this.refs.button.classList.toggle( + 'hidden', + !submitButton.visible, + ); + } + + this.addEventListener(this.refs.zoomIn, 'click', (event) => { + event.preventDefault(); + this.postMessage({ name: 'zoomIn' }); + }); + + this.addEventListener(this.refs.zoomOut, 'click', (event) => { + event.preventDefault(); + this.postMessage({ name: 'zoomOut' }); + }); + + const form = fastCloneDeep(this.form); + if (this.formio) { + form.projectUrl = this.formio.projectUrl; + form.url = this.formio.formUrl; + form.base = this.formio.base; + this.postMessage({ + name: 'token', + data: this.formio.getToken(), + }); + } + + this.emit('attach'); + }); } - if (this.options.readOnly) { - params.push('readonly=1'); + /** + * Get the submission from the iframe. + * + * @return {Promise} + */ + getSubmission() { + return new Promise((resolve) => { + this.once('iframe-submission', resolve); + this.postMessage({ name: 'getSubmission' }); + }); } - if (this.options.zoom) { - params.push(`zoom=${this.options.zoom}`); + /** + * Ensure we have the submission from the iframe before we submit the form. + * + * @param options + * @return {*} + */ + submitForm(options = {}) { + this.postMessage({ name: 'getErrors' }); + return this.getSubmission().then(() => super.submitForm(options)); } - if (this.builderMode) { - params.push('builder=1'); + getSrc() { + if (!this._form || !this._form.settings || !this._form.settings.pdf) { + return ''; + } + + let iframeSrc = `${this._form.settings.pdf.src}.html`; + const params = [`id=${this.id}`]; + + if ( + this.options.showCheckboxBackground || + this._form.settings.showCheckboxBackground + ) { + params.push('checkboxbackground=1'); + } + + if (this.options.readOnly) { + params.push('readonly=1'); + } + + if (this.options.zoom) { + params.push(`zoom=${this.options.zoom}`); + } + + if (this.builderMode) { + params.push('builder=1'); + } + + if (params.length) { + iframeSrc += `?${params.join('&')}`; + } + + return iframeSrc; } - if (params.length) { - iframeSrc += `?${params.join('&')}`; + setForm(form, flags = {}) { + return super.setForm(form, flags).then(() => { + if (this.formio) { + form.projectUrl = this.formio.projectUrl; + form.url = this.formio.formUrl; + form.base = this.formio.base; + this.postMessage({ + name: 'token', + data: this.formio.getToken(), + }); + } + this.postMessage({ name: 'form', data: this.form }); + }); } - return iframeSrc; - } - - setForm(form, flags = {}) { - return super.setForm(form, flags).then(() => { - if (this.formio) { - form.projectUrl = this.formio.projectUrl; - form.url = this.formio.formUrl; - form.base = this.formio.base; - this.postMessage({ name: 'token', data: this.formio.getToken() }); - } - this.postMessage({ name: 'form', data: this.form }); - }); - } - - /** - * Set's the value of this form component. - * - * @param submission - * @param flags - */ - setValue(submission, flags = {}) { - const changed = super.setValue(submission, flags); - if (!flags || !flags.fromIframe) { - this.once('iframe-ready', () => { - if (changed) { - this.postMessage({ name: 'submission', data: submission }); + /** + * Set's the value of this form component. + * + * @param submission + * @param flags + */ + setValue(submission, flags = {}) { + const changed = super.setValue(submission, flags); + if (!flags || !flags.fromIframe) { + this.once('iframe-ready', () => { + if (changed) { + this.postMessage({ name: 'submission', data: submission }); + } + }); } - }); + return changed; } - return changed; - } - postMessage(message) { - // If we get here before the iframeReady promise is set up, it's via the superclass constructor - if (!this.iframeReady) { - return; - } + postMessage(message) { + // If we get here before the iframeReady promise is set up, it's via the superclass constructor + if (!this.iframeReady) { + return; + } + + if (!message.type) { + message.type = 'iframe-data'; + } - if (!message.type) { - message.type = 'iframe-data'; + this.iframeReady.then(() => { + if ( + this.iframeElement && + this.iframeElement.contentWindow && + !(message.name === 'form' && this.iframeFormSetUp) + ) { + this.iframeElement.contentWindow.postMessage( + JSON.stringify(message), + '*', + ); + this.iframeFormSetUp = message.name === 'form'; + } + }); } - this.iframeReady.then(() => { - if (this.iframeElement && this.iframeElement.contentWindow && !(message.name === 'form' && this.iframeFormSetUp)) { - this.iframeElement.contentWindow.postMessage(JSON.stringify(message), '*'); - this.iframeFormSetUp = message.name === 'form'; - } - }); - } + focusOnComponent(key) { + this.postMessage({ + name: 'focusErroredField', + data: key, + }); + } - focusOnComponent(key) { - this.postMessage({ - name: 'focusErroredField', - data: key, - }); - } + // Do not clear the iframe. + clear() {} - // Do not clear the iframe. - clear() {} + showErrors(error, triggerEvent) { + const helpBlock = document.getElementById('submit-error'); + const submitError = this.t('submitError'); + const isSubmitErrorShown = + this.refs.buttonMessage?.textContent.trim() === submitError; - showErrors(error, triggerEvent) { - const helpBlock = document.getElementById('submit-error'); - const submitError = this.t('submitError'); - const isSubmitErrorShown = this.refs.buttonMessage?.textContent.trim() === submitError; + if (!helpBlock && this.errors.length && !isSubmitErrorShown) { + const p = this.ce('p', { class: 'help-block' }); - if (!helpBlock && this.errors.length && !isSubmitErrorShown) { - const p = this.ce('p', { class: 'help-block' }); + this.setContent(p, submitError); + p.addEventListener('click', () => { + window.scrollTo(0, 0); + }); - this.setContent(p, submitError); - p.addEventListener('click', () => { - window.scrollTo(0, 0); - }); + const div = this.ce('div', { + id: 'submit-error', + class: 'has-error', + }); - const div = this.ce('div', { id: 'submit-error', class: 'has-error' }); + this.appendTo(p, div); + this.appendTo(div, this.element); + } - this.appendTo(p, div); - this.appendTo(div, this.element); - } + if (!this.errors.length && helpBlock) { + helpBlock.remove(); + } - if (!this.errors.length && helpBlock) { - helpBlock.remove(); + super.showErrors(error, triggerEvent); } - super.showErrors(error, triggerEvent); - } - - isSubmitButtonHidden() { - let hidden = false; - eachComponent(this.component.components, (component) => { - if ( - (component.type === 'button') && - ((component.action === 'submit') || !component.action) - ) { - hidden = component.hidden || false; - } - }); + isSubmitButtonHidden() { + let hidden = false; + eachComponent(this.component.components, (component) => { + if ( + component.type === 'button' && + (component.action === 'submit' || !component.action) + ) { + hidden = component.hidden || false; + } + }); - return hidden; - } + return hidden; + } } /** * Listen for window messages. */ if (typeof window !== 'undefined') { - window.addEventListener('message', (event) => { - let eventData = null; - try { - eventData = JSON.parse(event.data); - } - catch (err) { - eventData = null; - } + window.addEventListener('message', (event) => { + let eventData = null; + try { + eventData = JSON.parse(event.data); + } catch (err) { + eventData = null; + } - // If this form exists, then emit the event within this form. - if ( - eventData && - eventData.name && - eventData.formId && - Object.prototype.hasOwnProperty.call(Formio.forms, eventData.formId) - ) { - Formio.forms[eventData.formId].emit(`iframe-${eventData.name}`, eventData.data); - } - }); + // If this form exists, then emit the event within this form. + if ( + eventData && + eventData.name && + eventData.formId && + Object.prototype.hasOwnProperty.call(Formio.forms, eventData.formId) + ) { + Formio.forms[eventData.formId].emit( + `iframe-${eventData.name}`, + eventData.data, + ); + } + }); } diff --git a/src/PDFBuilder.js b/src/PDFBuilder.js index 8ec65323eb..4602cd8f58 100644 --- a/src/PDFBuilder.js +++ b/src/PDFBuilder.js @@ -2,530 +2,655 @@ import _ from 'lodash'; import { Formio } from './Formio'; import WebformBuilder from './WebformBuilder'; -import { fastCloneDeep, getElementRect , getBrowserInfo } from './utils/utils'; +import { fastCloneDeep, getElementRect, getBrowserInfo } from './utils/utils'; import { eachComponent } from './utils/formUtils'; import BuilderUtils from './utils/builder'; import PDF from './PDF'; export default class PDFBuilder extends WebformBuilder { - constructor() { - let element, options; - if (arguments[0] instanceof HTMLElement || arguments[1]) { - element = arguments[0]; - options = arguments[1]; - } - else { - options = arguments[0]; + constructor() { + let element, options; + if (arguments[0] instanceof HTMLElement || arguments[1]) { + element = arguments[0]; + options = arguments[1]; + } else { + options = arguments[0]; + } + + // Force superclass to skip the automatic init; we'll trigger it manually + options.skipInit = true; + options.display = 'pdf'; + + if (element) { + super(element, options); + } else { + super(options); + } + + this.dragDropEnabled = false; } - // Force superclass to skip the automatic init; we'll trigger it manually - options.skipInit = true; - options.display = 'pdf'; + get defaultGroups() { + return { + pdf: { + title: 'PDF Fields', + weight: 0, + default: true, + components: { + textfield: true, + number: true, + password: true, + email: true, + phoneNumber: true, + currency: true, + checkbox: true, + signature: true, + select: true, + textarea: true, + datetime: true, + file: true, + htmlelement: true, + signrequestsignature: true, + }, + }, + basic: false, + advanced: false, + layout: false, + data: false, + premium: false, + resource: false, + }; + } - if (element) { - super(element, options); + get hasPDF() { + return _.has(this.webform.form, 'settings.pdf'); } - else { - super(options); + + get projectUrl() { + return this.options.projectUrl || Formio.getProjectUrl(); } - this.dragDropEnabled = false; - } - - get defaultGroups() { - return { - pdf: { - title: 'PDF Fields', - weight: 0, - default: true, - components: { - textfield: true, - number: true, - password: true, - email: true, - phoneNumber: true, - currency: true, - checkbox: true, - signature: true, - select: true, - textarea: true, - datetime: true, - file: true, - htmlelement: true, - signrequestsignature: true - } - }, - basic: false, - advanced: false, - layout: false, - data: false, - premium: false, - resource: false - }; - } - - get hasPDF() { - return _.has(this.webform.form, 'settings.pdf'); - } - - get projectUrl() { - return this.options.projectUrl || Formio.getProjectUrl(); - } - - init() { - this.options.attachMode = 'builder'; - this.webform = this.webform || this.createForm(this.options); - this.webform.init(); - } - - render() { - const result = this.renderTemplate('pdfBuilder', { - sidebar: this.renderTemplate('builderSidebar', { - scrollEnabled: this.sideBarScroll, - groupOrder: this.groupOrder, - groupId: `builder-sidebar-${this.id}`, - groups: this.groupOrder.map((groupKey) => this.renderTemplate('builderSidebarGroup', { - group: this.groups[groupKey], - groupKey, - groupId: `builder-sidebar-${this.id}`, - subgroups: this.groups[groupKey].subgroups.map((group) => this.renderTemplate('builderSidebarGroup', { - group, - groupKey: group.key, - groupId: `group-container-${groupKey}`, - subgroups: [] - })), - })), - }), - form: this.hasPDF ? - this.webform.render() : - this.renderTemplate('pdfBuilderUpload', {}) - }); - - return result; - } - - attach(element) { - // PDF Upload - if (!this.hasPDF) { - this.loadRefs(element, { - 'fileDrop': 'single', - 'fileBrowse': 'single', - 'hiddenFileInputElement': 'single', - 'uploadError': 'single', - 'uploadProgress': 'single', - 'uploadProgressWrapper': 'single', - 'dragDropText': 'single' - }); - this.addEventListener(this.refs['pdf-upload-button'], 'click', (event) => { - event.preventDefault(); - }); - - // Init the upload error. - if (!this.projectUrl) { - this.setUploadError('Form options.projectUrl not set. Please set the "projectUrl" property of the options for this form or use Formio.setProjectUrl(). This setting is necessary to upload a pdf background.'); - } - else { - this.setUploadError(); - } - - if (this.refs.fileDrop) { - const element = this; - this.addEventListener(this.refs.fileDrop, 'dragover', function(event) { - this.className = 'fileSelector fileDragOver'; - event.preventDefault(); - }); - this.addEventListener(this.refs.fileDrop, 'dragleave', function(event) { - this.className = 'fileSelector'; - event.preventDefault(); - }); - this.addEventListener(this.refs.fileDrop, 'drop', function(event) { - this.className = 'fileSelector'; - event.preventDefault(); - element.upload(event.dataTransfer.files[0]); - return false; - }); - } - - if (this.refs.fileBrowse && this.refs.hiddenFileInputElement) { - this.addEventListener(this.refs.fileBrowse, 'click', (event) => { - event.preventDefault(); - // There is no direct way to trigger a file dialog. To work around this, create an input of type file and trigger - // a click event on it. - if (typeof this.refs.hiddenFileInputElement.trigger === 'function') { - this.refs.hiddenFileInputElement.trigger('click'); - } - else { - this.refs.hiddenFileInputElement.click(); - } - }); - this.addEventListener(this.refs.hiddenFileInputElement, 'change', () => { - if (!this.refs.hiddenFileInputElement.value) { - return; - } + init() { + this.options.attachMode = 'builder'; + this.webform = this.webform || this.createForm(this.options); + this.webform.init(); + } - this.upload(this.refs.hiddenFileInputElement.files[0]); - this.refs.hiddenFileInputElement.value = ''; + render() { + const result = this.renderTemplate('pdfBuilder', { + sidebar: this.renderTemplate('builderSidebar', { + scrollEnabled: this.sideBarScroll, + groupOrder: this.groupOrder, + groupId: `builder-sidebar-${this.id}`, + groups: this.groupOrder.map((groupKey) => + this.renderTemplate('builderSidebarGroup', { + group: this.groups[groupKey], + groupKey, + groupId: `builder-sidebar-${this.id}`, + subgroups: this.groups[groupKey].subgroups.map( + (group) => + this.renderTemplate('builderSidebarGroup', { + group, + groupKey: group.key, + groupId: `group-container-${groupKey}`, + subgroups: [], + }), + ), + }), + ), + }), + form: this.hasPDF + ? this.webform.render() + : this.renderTemplate('pdfBuilderUpload', {}), }); - } - return Promise.resolve(); + return result; } - // Normal PDF Builder - return super.attach(element).then(() => { - this.loadRefs(this.element, { - iframeDropzone: 'single', - 'sidebar-container': 'multiple', - 'sidebar': 'single', - }); - - this.afterAttach(); - return this.element; - }); - } - - afterAttach() { - this.on('saveComponent', (component) => { - this.webform.postMessage({ name: 'updateElement', data: component }); - }); - this.on('removeComponent', (component) => { - this.webform.postMessage({ name: 'removeElement', data: component }); - }); - this.initIframeEvents(); - this.updateDropzoneDimensions(); - - const sidebar = this.refs.sidebar; - if (sidebar) { - this.addClass(sidebar, 'disabled'); - this.webform.on('iframe-ready', () => { - this.pdfLoaded = true; - this.updateDragAndDrop(); - this.removeClass(sidebar, 'disabled'); - }, true); - } - } + attach(element) { + // PDF Upload + if (!this.hasPDF) { + this.loadRefs(element, { + fileDrop: 'single', + fileBrowse: 'single', + hiddenFileInputElement: 'single', + uploadError: 'single', + uploadProgress: 'single', + uploadProgressWrapper: 'single', + dragDropText: 'single', + }); + this.addEventListener( + this.refs['pdf-upload-button'], + 'click', + (event) => { + event.preventDefault(); + }, + ); + + // Init the upload error. + if (!this.projectUrl) { + this.setUploadError( + 'Form options.projectUrl not set. Please set the "projectUrl" property of the options for this form or use Formio.setProjectUrl(). This setting is necessary to upload a pdf background.', + ); + } else { + this.setUploadError(); + } + + if (this.refs.fileDrop) { + const element = this; + this.addEventListener( + this.refs.fileDrop, + 'dragover', + function (event) { + this.className = 'fileSelector fileDragOver'; + event.preventDefault(); + }, + ); + this.addEventListener( + this.refs.fileDrop, + 'dragleave', + function (event) { + this.className = 'fileSelector'; + event.preventDefault(); + }, + ); + this.addEventListener( + this.refs.fileDrop, + 'drop', + function (event) { + this.className = 'fileSelector'; + event.preventDefault(); + element.upload(event.dataTransfer.files[0]); + return false; + }, + ); + } + + if (this.refs.fileBrowse && this.refs.hiddenFileInputElement) { + this.addEventListener( + this.refs.fileBrowse, + 'click', + (event) => { + event.preventDefault(); + // There is no direct way to trigger a file dialog. To work around this, create an input of type file and trigger + // a click event on it. + if ( + typeof this.refs.hiddenFileInputElement.trigger === + 'function' + ) { + this.refs.hiddenFileInputElement.trigger('click'); + } else { + this.refs.hiddenFileInputElement.click(); + } + }, + ); + this.addEventListener( + this.refs.hiddenFileInputElement, + 'change', + () => { + if (!this.refs.hiddenFileInputElement.value) { + return; + } + + this.upload(this.refs.hiddenFileInputElement.files[0]); + this.refs.hiddenFileInputElement.value = ''; + }, + ); + } + + return Promise.resolve(); + } - upload(file) { - const formio = new Formio(this.projectUrl); - if (this.refs.dragDropText) { - this.refs.dragDropText.style.display = 'none'; - } - if (this.refs.uploadProgressWrapper) { - this.refs.uploadProgressWrapper.style.display = 'inherit'; + // Normal PDF Builder + return super.attach(element).then(() => { + this.loadRefs(this.element, { + iframeDropzone: 'single', + 'sidebar-container': 'multiple', + sidebar: 'single', + }); + + this.afterAttach(); + return this.element; + }); } - formio.uploadFile('url', file, file, '', (event) => { - if (this.refs.uploadProgress) { - const progress = Math.floor((event.loaded / event.total) * 100); - this.refs.uploadProgress.style.width = `${progress}%`; - if (progress > 98) { - this.refs.uploadProgress.innerHTML = this.t('Converting PDF. Please wait.'); - } - else { - this.refs.uploadProgress.innerHTML = `${this.t('Uploading')} ${progress}%`; - } - } - }, `${this.projectUrl}/upload`, {}, 'file') - .then((result) => { - let autoConversionComponentsAssigned = false; - - if (result.data.formfields?.components && result.data.formfields.components.length) { - const formInitState = this.webform.form.components[0]?.key === 'submit'; - const wizardInitState = this.webform.form.components[0]?.key === 'page1' && - this.webform.form.components[0]?.components.length === 0; - const emptyFormState = this.webform.form.components.length === 0; - - if (formInitState || wizardInitState || emptyFormState) { - autoConversionComponentsAssigned = true; - this.webform.form.components = result.data.formfields.components; - } + + afterAttach() { + this.on('saveComponent', (component) => { + this.webform.postMessage({ + name: 'updateElement', + data: component, + }); + }); + this.on('removeComponent', (component) => { + this.webform.postMessage({ + name: 'removeElement', + data: component, + }); + }); + this.initIframeEvents(); + this.updateDropzoneDimensions(); + + const sidebar = this.refs.sidebar; + if (sidebar) { + this.addClass(sidebar, 'disabled'); + this.webform.on( + 'iframe-ready', + () => { + this.pdfLoaded = true; + this.updateDragAndDrop(); + this.removeClass(sidebar, 'disabled'); + }, + true, + ); } + } + + upload(file) { + const formio = new Formio(this.projectUrl); if (this.refs.dragDropText) { - this.refs.dragDropText.style.display = 'inherit'; + this.refs.dragDropText.style.display = 'none'; } if (this.refs.uploadProgressWrapper) { - this.refs.uploadProgressWrapper.style.display = 'none'; + this.refs.uploadProgressWrapper.style.display = 'inherit'; } + formio + .uploadFile( + 'url', + file, + file, + '', + (event) => { + if (this.refs.uploadProgress) { + const progress = Math.floor( + (event.loaded / event.total) * 100, + ); + this.refs.uploadProgress.style.width = `${progress}%`; + if (progress > 98) { + this.refs.uploadProgress.innerHTML = this.t( + 'Converting PDF. Please wait.', + ); + } else { + this.refs.uploadProgress.innerHTML = `${this.t( + 'Uploading', + )} ${progress}%`; + } + } + }, + `${this.projectUrl}/upload`, + {}, + 'file', + ) + .then((result) => { + let autoConversionComponentsAssigned = false; + + if ( + result.data.formfields?.components && + result.data.formfields.components.length + ) { + const formInitState = + this.webform.form.components[0]?.key === 'submit'; + const wizardInitState = + this.webform.form.components[0]?.key === 'page1' && + this.webform.form.components[0]?.components.length === + 0; + const emptyFormState = + this.webform.form.components.length === 0; + + if (formInitState || wizardInitState || emptyFormState) { + autoConversionComponentsAssigned = true; + this.webform.form.components = + result.data.formfields.components; + } + } + if (this.refs.dragDropText) { + this.refs.dragDropText.style.display = 'inherit'; + } + if (this.refs.uploadProgressWrapper) { + this.refs.uploadProgressWrapper.style.display = 'none'; + } + + _.set(this.webform.form, 'settings.pdf', { + id: result.data.file, + src: result.data.filesServer + ? `${result.data.filesServer}${result.data.path}` + : `${new URL(this.projectUrl).origin}/pdf-proxy${ + result.data.path + }`, + nonFillableConversionUsed: + autoConversionComponentsAssigned && + result.data.formfields.nonFillableConversionUsed, + }); + + this.emit('pdfUploaded', result.data); + this.redraw(); + }) + .catch((err) => this.setUploadError(err)); + } - _.set(this.webform.form, 'settings.pdf', { - id: result.data.file, - src: result.data.filesServer ? `${result.data.filesServer}${result.data.path}` : `${new URL(this.projectUrl).origin}/pdf-proxy${result.data.path}`, - nonFillableConversionUsed: autoConversionComponentsAssigned && result.data.formfields.nonFillableConversionUsed - }); + setUploadError(message) { + if (!this.refs.uploadError) { + return; + } + this.refs.uploadError.style.display = message ? '' : 'none'; + this.refs.uploadError.innerHTML = message; + } - this.emit('pdfUploaded', result.data); - this.redraw(); - }) - .catch((err) => this.setUploadError(err)); - } + createForm(options) { + // Instantiate the webform from the PDF class instead of Webform + options.skipInit = false; + options.hideLoader = true; + this.webform = new PDF(this.element, options); + this.webform.on('attach', () => { + // If the dropzone exists but has been removed in a PDF rebuild, reinstate it + if ( + this.refs.iframeDropzone && + ![...this.refs.form.children].includes(this.refs.iframeDropzone) + ) { + this.prependTo(this.refs.iframeDropzone, this.refs.form); + } + }); + return this.webform; + } - setUploadError(message) { - if (!this.refs.uploadError) { - return; + destroy(all = false) { + super.destroy(all); + this.webform.destroy(all); } - this.refs.uploadError.style.display = message ? '' : 'none'; - this.refs.uploadError.innerHTML = message; - } - - createForm(options) { - // Instantiate the webform from the PDF class instead of Webform - options.skipInit = false; - options.hideLoader = true; - this.webform = new PDF(this.element, options); - this.webform.on('attach', () => { - // If the dropzone exists but has been removed in a PDF rebuild, reinstate it - if (this.refs.iframeDropzone && ![...this.refs.form.children].includes(this.refs.iframeDropzone)) { - this.prependTo(this.refs.iframeDropzone, this.refs.form); - } - }); - return this.webform; - } - - destroy(all = false) { - super.destroy(all); - this.webform.destroy(all); - } - - // d8b 8888888888 888 - // Y8P 888 888 - // 888 888 - // 888 8888888 888d888 8888b. 88888b.d88b. .d88b. .d88b. 888 888 .d88b. 88888b. 888888 .d8888b - // 888 888 888P" "88b 888 "888 "88b d8P Y8b d8P Y8b 888 888 d8P Y8b 888 "88b 888 88K - // 888 888 888 .d888888 888 888 888 88888888 88888888 Y88 88P 88888888 888 888 888 "Y8888b. - // 888 888 888 888 888 888 888 888 Y8b. Y8b. Y8bd8P Y8b. 888 888 Y88b. X88 - // 888 888 888 "Y888888 888 888 888 "Y8888 "Y8888 Y88P "Y8888 888 888 "Y888 88888P' - getParentContainer(component) { - let container = []; - let originalComponent = null; - eachComponent(this.webform._form.components, (comp, path, components) => { - if (comp.id === component.component.id) { - container = components; - originalComponent = comp; - return true; - } - }, true); - return { - formioComponent: component.parent, - formioContainer: container, - originalComponent - }; - } - - initIframeEvents() { - this.webform.off('iframe-elementUpdate'); - this.webform.off('iframe-componentUpdate'); - this.webform.off('iframe-componentClick'); - this.webform.on('iframe-elementUpdate', schema => { - const component = this.webform.getComponentById(schema.id); - if (component && component.component) { - const isNew = true; - component.component.overlay = { - page: schema.page, - left: schema.left, - top: schema.top, - height: schema.height, - width: schema.width - }; - if (!this.options.noNewEdit && !component.component.noNewEdit) { - this.editComponent(component.component, this.getParentContainer(component), isNew); - } - this.emit('updateComponent', component.component); - } - return component; - }); - - this.webform.on('iframe-componentUpdate', schema => { - const component = this.webform.getComponentById(schema.id); - if (component && component.component) { - component.component.overlay = { - page: schema.overlay.page, - left: schema.overlay.left, - top: schema.overlay.top, - height: schema.overlay.height, - width: schema.overlay.width + // d8b 8888888888 888 + // Y8P 888 888 + // 888 888 + // 888 8888888 888d888 8888b. 88888b.d88b. .d88b. .d88b. 888 888 .d88b. 88888b. 888888 .d8888b + // 888 888 888P" "88b 888 "888 "88b d8P Y8b d8P Y8b 888 888 d8P Y8b 888 "88b 888 88K + // 888 888 888 .d888888 888 888 888 88888888 88888888 Y88 88P 88888888 888 888 888 "Y8888b. + // 888 888 888 888 888 888 888 888 Y8b. Y8b. Y8bd8P Y8b. 888 888 Y88b. X88 + // 888 888 888 "Y888888 888 888 888 "Y8888 "Y8888 Y88P "Y8888 888 888 "Y888 88888P' + getParentContainer(component) { + let container = []; + let originalComponent = null; + eachComponent( + this.webform._form.components, + (comp, path, components) => { + if (comp.id === component.component.id) { + container = components; + originalComponent = comp; + return true; + } + }, + true, + ); + return { + formioComponent: component.parent, + formioContainer: container, + originalComponent, }; - this.emit('updateComponent', component.component); - this.emit('change', this.form); - } - return component; - }); - - this.webform.on('iframe-componentClick', schema => { - const component = this.webform.getComponentById(schema.id); - if (component) { - this.editComponent(component.component, this.getParentContainer(component)); - } - }, true); - } - - // 8888888b. 888 d8b - // 888 "Y88b 888 Y8P - // 888 888 888 - // 888 888 888d888 .d88b. 88888b. 88888888 .d88b. 88888b. .d88b. 888 .d88b. .d88b. 888 .d8888b - // 888 888 888P" d88""88b 888 "88b d88P d88""88b 888 "88b d8P Y8b 888 d88""88b d88P"88b 888 d88P" - // 888 888 888 888 888 888 888 d88P 888 888 888 888 88888888 888 888 888 888 888 888 888 - // 888 .d88P 888 Y88..88P 888 d88P d88P Y88..88P 888 888 Y8b. 888 Y88..88P Y88b 888 888 Y88b. - // 8888888P" 888 "Y88P" 88888P" 88888888 "Y88P" 888 888 "Y8888 888 "Y88P" "Y88888 888 "Y8888P - // 888 888 - // 888 Y8b d88P - // 888 "Y88P" - - initDropzoneEvents() { - if (!this.refs.iframeDropzone) { - return; - } - // This is required per HTML spec in order for the drop event to fire - this.removeEventListener(this.refs.iframeDropzone, 'dragover'); - this.removeEventListener(this.refs.iframeDropzone, 'drop'); - this.addEventListener(this.refs.iframeDropzone, 'dragover', (e) => { - e.preventDefault(); - return false; - }); - - this.addEventListener(this.refs.iframeDropzone, 'drop', this.onDropzoneDrop.bind(this)); - } - - updateDragAndDrop() { - if (!this.pdfLoaded) { - return; } - this.initDropzoneEvents(); - this.prepSidebarComponentsForDrag(); - } - prepSidebarComponentsForDrag() { - if (!this.refs['sidebar-container']) { - return; - } - this.refs['sidebar-container'].forEach(container => { - [...container.children].forEach(el => { - el.draggable = true; - el.setAttribute('draggable', true); - this.removeEventListener(el, 'dragstart'); - this.removeEventListener(el, 'dragend'); - this.addEventListener(el, 'dragstart', this.onDragStart.bind(this), true); - this.addEventListener(el, 'dragend', this.onDragEnd.bind(this), true); - this.addEventListener(el, 'drag', (e) => { - e.target.style.cursor = 'none'; + initIframeEvents() { + this.webform.off('iframe-elementUpdate'); + this.webform.off('iframe-componentUpdate'); + this.webform.off('iframe-componentClick'); + this.webform.on('iframe-elementUpdate', (schema) => { + const component = this.webform.getComponentById(schema.id); + if (component && component.component) { + const isNew = true; + component.component.overlay = { + page: schema.page, + left: schema.left, + top: schema.top, + height: schema.height, + width: schema.width, + }; + + if (!this.options.noNewEdit && !component.component.noNewEdit) { + this.editComponent( + component.component, + this.getParentContainer(component), + isNew, + ); + } + this.emit('updateComponent', component.component); + } + return component; }); - }); - }); - } - updateDropzoneDimensions() { - if (!this.refs.iframeDropzone) { - return; + this.webform.on('iframe-componentUpdate', (schema) => { + const component = this.webform.getComponentById(schema.id); + if (component && component.component) { + component.component.overlay = { + page: schema.overlay.page, + left: schema.overlay.left, + top: schema.overlay.top, + height: schema.overlay.height, + width: schema.overlay.width, + }; + this.emit('updateComponent', component.component); + this.emit('change', this.form); + } + return component; + }); + + this.webform.on( + 'iframe-componentClick', + (schema) => { + const component = this.webform.getComponentById(schema.id); + if (component) { + this.editComponent( + component.component, + this.getParentContainer(component), + ); + } + }, + true, + ); } - const iframeRect = getElementRect(this.webform.refs.iframeContainer); - this.refs.iframeDropzone.style.height = iframeRect && iframeRect.height ? `${iframeRect.height}px` : '1000px'; - this.refs.iframeDropzone.style.width = iframeRect && iframeRect.width ? `${iframeRect.width}px` : '100%'; - } - - onDragStart(e) { - // Taking the current offset of a dragged item relative to the cursor - const { offsetX = 0, offsetY = 0 } = e; - this.itemOffsetX = offsetX; - this.itemOffsetY = offsetY; - - e.dataTransfer.setData('text', ''); - this.updateDropzoneDimensions(); - this.addClass(this.refs.iframeDropzone, 'enabled'); - this.dropEmitted = false; - } - - onDropzoneDrop(e) { - this.dropEmitted = true; - this.dropEvent = e; - e.preventDefault(); - return false; - } - - onDragEnd(e) { - // IMPORTANT - must retrieve offsets BEFORE disabling the dropzone - offsets will - // reflect absolute positioning if accessed after the target element is hidden - const iframeRect = this.webform.refs.iframeContainer.getBoundingClientRect(); - const layerX = this.dropEvent ? this.dropEvent.layerX : null; - const layerY = this.dropEvent ? this.dropEvent.layerY : null; - const WIDTH = 100; - const HEIGHT = 20; - // Always disable the dropzone on drag end - this.removeClass(this.refs.iframeDropzone, 'enabled'); - - // If there hasn't been a drop event on the dropzone, we're done - if (!this.dropEvent) { - // a 'drop' event may not be emited in the chrome browser when using a Mac, therefore an additional check has been added - // eslint-disable-next-line no-undef - if (!this.dropEmitted && (getBrowserInfo().chrome || getBrowserInfo().edge) && globalThis.navigator.userAgentData.platform === 'macOS' && iframeRect.left < e.clientX && iframeRect.top < e.clientY ) { - this.dropEvent = e; - this.dropEvent.dataTransfer.effectAllowed = 'all'; - this.dropEmitted = true; - } - else { - return; - } + // 8888888b. 888 d8b + // 888 "Y88b 888 Y8P + // 888 888 888 + // 888 888 888d888 .d88b. 88888b. 88888888 .d88b. 88888b. .d88b. 888 .d88b. .d88b. 888 .d8888b + // 888 888 888P" d88""88b 888 "88b d88P d88""88b 888 "88b d8P Y8b 888 d88""88b d88P"88b 888 d88P" + // 888 888 888 888 888 888 888 d88P 888 888 888 888 88888888 888 888 888 888 888 888 888 + // 888 .d88P 888 Y88..88P 888 d88P d88P Y88..88P 888 888 Y8b. 888 Y88..88P Y88b 888 888 Y88b. + // 8888888P" 888 "Y88P" 88888P" 88888888 "Y88P" 888 888 "Y8888 888 "Y88P" "Y88888 888 "Y8888P + // 888 888 + // 888 Y8b d88P + // 888 "Y88P" + + initDropzoneEvents() { + if (!this.refs.iframeDropzone) { + return; + } + // This is required per HTML spec in order for the drop event to fire + this.removeEventListener(this.refs.iframeDropzone, 'dragover'); + this.removeEventListener(this.refs.iframeDropzone, 'drop'); + this.addEventListener(this.refs.iframeDropzone, 'dragover', (e) => { + e.preventDefault(); + return false; + }); + + this.addEventListener( + this.refs.iframeDropzone, + 'drop', + this.onDropzoneDrop.bind(this), + ); } - const element = e.target; - const type = element.getAttribute('data-type'); - const key = element.getAttribute('data-key'); - const group = element.getAttribute('data-group'); - const schema = fastCloneDeep(this.schemas[type]); + updateDragAndDrop() { + if (!this.pdfLoaded) { + return; + } + this.initDropzoneEvents(); + this.prepSidebarComponentsForDrag(); + } - if (key && group) { - const info = this.getComponentInfo(key, group); - _.merge(schema, info); + prepSidebarComponentsForDrag() { + if (!this.refs['sidebar-container']) { + return; + } + this.refs['sidebar-container'].forEach((container) => { + [...container.children].forEach((el) => { + el.draggable = true; + el.setAttribute('draggable', true); + this.removeEventListener(el, 'dragstart'); + this.removeEventListener(el, 'dragend'); + this.addEventListener( + el, + 'dragstart', + this.onDragStart.bind(this), + true, + ); + this.addEventListener( + el, + 'dragend', + this.onDragEnd.bind(this), + true, + ); + this.addEventListener(el, 'drag', (e) => { + e.target.style.cursor = 'none'; + }); + }); + }); } - // Set a unique key for this component. - BuilderUtils.uniquify([this.webform._form], schema); - this.webform._form.components.push(schema); + updateDropzoneDimensions() { + if (!this.refs.iframeDropzone) { + return; + } - schema.overlay = { - top: layerY ? (layerY - this.itemOffsetY + HEIGHT) : (e.clientY - iframeRect.top - (this.itemOffsetY - HEIGHT )*2), - left: layerX ? (layerX - this.itemOffsetX) : (e.clientX - iframeRect.left - this.itemOffsetX*2), - width: WIDTH, - height: HEIGHT - }; + const iframeRect = getElementRect(this.webform.refs.iframeContainer); + this.refs.iframeDropzone.style.height = + iframeRect && iframeRect.height + ? `${iframeRect.height}px` + : '1000px'; + this.refs.iframeDropzone.style.width = + iframeRect && iframeRect.width ? `${iframeRect.width}px` : '100%'; + } - this.webform.addComponent(schema, {}, null, true); - this.webform.postMessage({ name: 'addElement', data: schema }); + onDragStart(e) { + // Taking the current offset of a dragged item relative to the cursor + const { offsetX = 0, offsetY = 0 } = e; + this.itemOffsetX = offsetX; + this.itemOffsetY = offsetY; - this.emit('addComponent', schema, this.webform, schema.key, this.webform.component.components.length, !this.options.noNewEdit && !schema.noNewEdit); + e.dataTransfer.setData('text', ''); + this.updateDropzoneDimensions(); + this.addClass(this.refs.iframeDropzone, 'enabled'); + this.dropEmitted = false; + } - // Delete the stored drop event now that it's been handled - this.dropEvent = null; - e.target.style.cursor = 'default'; - } + onDropzoneDrop(e) { + this.dropEmitted = true; + this.dropEvent = e; + e.preventDefault(); + return false; + } + + onDragEnd(e) { + // IMPORTANT - must retrieve offsets BEFORE disabling the dropzone - offsets will + // reflect absolute positioning if accessed after the target element is hidden + const iframeRect = + this.webform.refs.iframeContainer.getBoundingClientRect(); + const layerX = this.dropEvent ? this.dropEvent.layerX : null; + const layerY = this.dropEvent ? this.dropEvent.layerY : null; + const WIDTH = 100; + const HEIGHT = 20; + // Always disable the dropzone on drag end + this.removeClass(this.refs.iframeDropzone, 'enabled'); + + // If there hasn't been a drop event on the dropzone, we're done + if (!this.dropEvent) { + // a 'drop' event may not be emited in the chrome browser when using a Mac, therefore an additional check has been added + // eslint-disable-next-line no-undef + if ( + !this.dropEmitted && + (getBrowserInfo().chrome || getBrowserInfo().edge) && + globalThis.navigator.userAgentData.platform === 'macOS' && + iframeRect.left < e.clientX && + iframeRect.top < e.clientY + ) { + this.dropEvent = e; + this.dropEvent.dataTransfer.effectAllowed = 'all'; + this.dropEmitted = true; + } else { + return; + } + } - highlightInvalidComponents() { - const repeatablePaths = this.findRepeatablePaths(); + const element = e.target; + const type = element.getAttribute('data-type'); + const key = element.getAttribute('data-key'); + const group = element.getAttribute('data-group'); + const schema = fastCloneDeep(this.schemas[type]); - // update elements which path was duplicated if any pathes have been changed - if (!_.isEqual(this.repeatablePaths, repeatablePaths)) { - eachComponent(this.webform.getComponents(), (comp, path) => { - if (this.repeatablePaths.includes(path)) { - this.webform.postMessage({ name: 'updateElement', data: comp.component }); + if (key && group) { + const info = this.getComponentInfo(key, group); + _.merge(schema, info); } - }); - this.repeatablePaths = repeatablePaths; - } + // Set a unique key for this component. + BuilderUtils.uniquify([this.webform._form], schema); + this.webform._form.components.push(schema); + + schema.overlay = { + top: layerY + ? layerY - this.itemOffsetY + HEIGHT + : e.clientY - iframeRect.top - (this.itemOffsetY - HEIGHT) * 2, + left: layerX + ? layerX - this.itemOffsetX + : e.clientX - iframeRect.left - this.itemOffsetX * 2, + width: WIDTH, + height: HEIGHT, + }; - if (!repeatablePaths.length) { - return; + this.webform.addComponent(schema, {}, null, true); + this.webform.postMessage({ name: 'addElement', data: schema }); + + this.emit( + 'addComponent', + schema, + this.webform, + schema.key, + this.webform.component.components.length, + !this.options.noNewEdit && !schema.noNewEdit, + ); + + // Delete the stored drop event now that it's been handled + this.dropEvent = null; + e.target.style.cursor = 'default'; } - eachComponent(this.webform.getComponents(), (comp, path) => { - if (this.repeatablePaths.includes(path)) { - this.webform.postMessage({ - name: 'showBuilderErrors', - data: { - compId: comp.component.id, - errorMessage: `API Key is not unique: ${comp.key}`, - } + highlightInvalidComponents() { + const repeatablePaths = this.findRepeatablePaths(); + + // update elements which path was duplicated if any pathes have been changed + if (!_.isEqual(this.repeatablePaths, repeatablePaths)) { + eachComponent(this.webform.getComponents(), (comp, path) => { + if (this.repeatablePaths.includes(path)) { + this.webform.postMessage({ + name: 'updateElement', + data: comp.component, + }); + } + }); + + this.repeatablePaths = repeatablePaths; + } + + if (!repeatablePaths.length) { + return; + } + + eachComponent(this.webform.getComponents(), (comp, path) => { + if (this.repeatablePaths.includes(path)) { + this.webform.postMessage({ + name: 'showBuilderErrors', + data: { + compId: comp.component.id, + errorMessage: `API Key is not unique: ${comp.key}`, + }, + }); + } }); - } - }); - } + } } diff --git a/src/PDFBuilder.spec.js b/src/PDFBuilder.spec.js index 8dfa4e7272..7567cc21b6 100644 --- a/src/PDFBuilder.spec.js +++ b/src/PDFBuilder.spec.js @@ -1,62 +1,69 @@ import FormBuilder from './FormBuilder'; -describe('Formio PDF Form Builder tests', function() { - it('Should emit change event when component position changed', function(done) { - const form = { - 'type': 'form', - 'components': [ - { - 'input': true, - 'label': 'Text', - 'key': 'text', - 'type': 'textfield', - 'overlay': { - 'top': 135, - 'left': 211.516, - 'height': 20, - 'width': 100, - 'page': 1 - } - }, - { - 'input': true, - 'label': 'Submit', - 'key': 'submit', - 'action': 'submit', - 'type': 'button' - } - ], - 'display': 'pdf', - 'name': 'testPdfForm' - }; - const element = document.createElement('div'); - document.body.appendChild(element); - const builder = new FormBuilder(element, form, {}); - builder.ready - .then(function(builder) { - let isPfdFormInitilized = false; - builder.on('change', function() { - if (isPfdFormInitilized) { //ignore any change events fired before initialized event - //remove builder elements from DOM - builder.destroy(); - try { - document.body.removeChild(element); - } - catch (err) { - console.warn(err); - } - done(); - } - else { - done(); - } +describe('Formio PDF Form Builder tests', function () { + it('Should emit change event when component position changed', function (done) { + const form = { + type: 'form', + components: [ + { + input: true, + label: 'Text', + key: 'text', + type: 'textfield', + overlay: { + top: 135, + left: 211.516, + height: 20, + width: 100, + page: 1, + }, + }, + { + input: true, + label: 'Submit', + key: 'submit', + action: 'submit', + type: 'button', + }, + ], + display: 'pdf', + name: 'testPdfForm', + }; + const element = document.createElement('div'); + document.body.appendChild(element); + const builder = new FormBuilder(element, form, {}); + builder.ready.then(function (builder) { + let isPfdFormInitilized = false; + builder.on('change', function () { + if (isPfdFormInitilized) { + //ignore any change events fired before initialized event + //remove builder elements from DOM + builder.destroy(); + try { + document.body.removeChild(element); + } catch (err) { + console.warn(err); + } + done(); + } else { + done(); + } + }); + builder.pdfForm.on('initialized', () => { + isPfdFormInitilized = true; + const component = Object.assign( + {}, + builder.pdfForm.getComponent('text').component, + ); + component.overlay = { + top: 225, + left: 405.516, + height: 20, + width: 100, + page: 1, + }; + builder.pdfForm.emit('iframe-componentUpdate', component); + }); }); - builder.pdfForm.on('initialized', () => { - isPfdFormInitilized = true; - const component = Object.assign({}, builder.pdfForm.getComponent('text').component); - component.overlay = { 'top': 225, 'left': 405.516, 'height': 20, 'width': 100, 'page': 1 }; - builder.pdfForm.emit('iframe-componentUpdate', component); - }); - }); - }); + }); }); diff --git a/src/PDFBuilder.unit.js b/src/PDFBuilder.unit.js index 407c7be3d8..369f879b76 100644 --- a/src/PDFBuilder.unit.js +++ b/src/PDFBuilder.unit.js @@ -4,189 +4,221 @@ import { expect } from 'chai'; import { Formio } from './Formio'; import FormBuilder from './FormBuilder'; -describe('PDF Builder tests', function() { - describe('PDF Auto Conversion', function() { - const originalUploadFile = Formio.prototype.uploadFile; - const originalLoadProject = Formio.prototype.loadProject; - - after(function() { - Formio.prototype.uploadFile = originalUploadFile; - Formio.prototype.loadProject = originalLoadProject; - }); +describe('PDF Builder tests', function () { + describe('PDF Auto Conversion', function () { + const originalUploadFile = Formio.prototype.uploadFile; + const originalLoadProject = Formio.prototype.loadProject; + + after(function () { + Formio.prototype.uploadFile = originalUploadFile; + Formio.prototype.loadProject = originalLoadProject; + }); - beforeEach(function() { - Formio.prototype.loadProject = fake.resolves(null); - }); + beforeEach(function () { + Formio.prototype.loadProject = fake.resolves(null); + }); - const getUploadResponseMock = (withNonFillable) => ({ - data: { - file: 'fileId', - filesServer: 'http://fakeserver.test', - path: '/path/to/pdf/file', - formfields: { - components: [ - { - input: true, - label: 'Test', - key: 'testTextField', - type: 'textfield', - overlay: { - top: 135, - left: 211.516, - height: 20, - width: 100, - page: 1, - }, + const getUploadResponseMock = (withNonFillable) => ({ + data: { + file: 'fileId', + filesServer: 'http://fakeserver.test', + path: '/path/to/pdf/file', + formfields: { + components: [ + { + input: true, + label: 'Test', + key: 'testTextField', + type: 'textfield', + overlay: { + top: 135, + left: 211.516, + height: 20, + width: 100, + page: 1, + }, + }, + ], + ...(withNonFillable && { nonFillableConversionUsed: true }), + }, }, - ], - ...(withNonFillable && { nonFillableConversionUsed: true }) - }, - }, - }); - - it('Should assign fields from PDF auto conversion to the empty form', function(done) { - const uploadResponseMock = getUploadResponseMock(); - - Formio.prototype.uploadFile = fake.resolves(uploadResponseMock); - - const form = { - type: 'form', - components: [], - display: 'pdf', - name: 'testPdfForm', - }; - const builder = new FormBuilder(document.createElement('div'), form, {}); - - builder.ready - .then(function(builder) { - builder.on('pdfUploaded', (result) => { - expect(result).to.be.deep.equal(uploadResponseMock.data); - expect(builder.webform.form.components).to.be.deep.equal(uploadResponseMock.data.formfields.components); - expect(builder.webform.form.settings.pdf.nonFillableConversionUsed).to.be.undefined; - - done(); - }); + }); - builder.upload(); + it('Should assign fields from PDF auto conversion to the empty form', function (done) { + const uploadResponseMock = getUploadResponseMock(); + + Formio.prototype.uploadFile = fake.resolves(uploadResponseMock); + + const form = { + type: 'form', + components: [], + display: 'pdf', + name: 'testPdfForm', + }; + const builder = new FormBuilder( + document.createElement('div'), + form, + {}, + ); + + builder.ready.then(function (builder) { + builder.on('pdfUploaded', (result) => { + expect(result).to.be.deep.equal(uploadResponseMock.data); + expect(builder.webform.form.components).to.be.deep.equal( + uploadResponseMock.data.formfields.components, + ); + expect( + builder.webform.form.settings.pdf + .nonFillableConversionUsed, + ).to.be.undefined; + + done(); + }); + + builder.upload(); + }); }); - }); - it('Should assign fields from PDF auto conversion to the initial form', function(done) { - const uploadResponseMock = getUploadResponseMock(); - - Formio.prototype.uploadFile = fake.resolves(uploadResponseMock); - - const form = { - type: 'form', - components: [ - { - input: true, - label: 'Submit', - key: 'submit', - action: 'submit', - type: 'button', - }, - ], - display: 'pdf', - name: 'testPdfForm', - }; - const builder = new FormBuilder(document.createElement('div'), form, {}); - - builder.ready - .then(function(builder) { - builder.on('pdfUploaded', (result) => { - expect(result).to.be.deep.equal(uploadResponseMock.data); - expect(builder.webform.form.components).to.be.deep.equal(uploadResponseMock.data.formfields.components); - expect(builder.webform.form.settings.pdf.nonFillableConversionUsed).to.be.undefined; - - done(); - }); - - builder.upload(); + it('Should assign fields from PDF auto conversion to the initial form', function (done) { + const uploadResponseMock = getUploadResponseMock(); + + Formio.prototype.uploadFile = fake.resolves(uploadResponseMock); + + const form = { + type: 'form', + components: [ + { + input: true, + label: 'Submit', + key: 'submit', + action: 'submit', + type: 'button', + }, + ], + display: 'pdf', + name: 'testPdfForm', + }; + const builder = new FormBuilder( + document.createElement('div'), + form, + {}, + ); + + builder.ready.then(function (builder) { + builder.on('pdfUploaded', (result) => { + expect(result).to.be.deep.equal(uploadResponseMock.data); + expect(builder.webform.form.components).to.be.deep.equal( + uploadResponseMock.data.formfields.components, + ); + expect( + builder.webform.form.settings.pdf + .nonFillableConversionUsed, + ).to.be.undefined; + + done(); + }); + + builder.upload(); + }); }); - }); - it('Should assign fields from PDF non fillable conversion to the initial form', function(done) { - const uploadResponseMock = getUploadResponseMock(true); - - Formio.prototype.uploadFile = fake.resolves(uploadResponseMock); - - const form = { - type: 'form', - components: [ - { - input: true, - label: 'Submit', - key: 'submit', - action: 'submit', - type: 'button', - }, - ], - display: 'pdf', - name: 'testPdfForm', - }; - const builder = new FormBuilder(document.createElement('div'), form, {}); - - builder.ready - .then(function(builder) { - builder.on('pdfUploaded', (result) => { - expect(result).to.be.deep.equal(uploadResponseMock.data); - expect(builder.webform.form.components).to.be.deep.equal(uploadResponseMock.data.formfields.components); - expect(builder.webform.form.settings.pdf.nonFillableConversionUsed).to.be.true; - - done(); - }); - - builder.upload(); + it('Should assign fields from PDF non fillable conversion to the initial form', function (done) { + const uploadResponseMock = getUploadResponseMock(true); + + Formio.prototype.uploadFile = fake.resolves(uploadResponseMock); + + const form = { + type: 'form', + components: [ + { + input: true, + label: 'Submit', + key: 'submit', + action: 'submit', + type: 'button', + }, + ], + display: 'pdf', + name: 'testPdfForm', + }; + const builder = new FormBuilder( + document.createElement('div'), + form, + {}, + ); + + builder.ready.then(function (builder) { + builder.on('pdfUploaded', (result) => { + expect(result).to.be.deep.equal(uploadResponseMock.data); + expect(builder.webform.form.components).to.be.deep.equal( + uploadResponseMock.data.formfields.components, + ); + expect( + builder.webform.form.settings.pdf + .nonFillableConversionUsed, + ).to.be.true; + + done(); + }); + + builder.upload(); + }); }); - }); - it('Should not assign fields from PDF auto conversion to non pristine form', function(done) { - const uploadResponseMock = getUploadResponseMock(true); - - Formio.prototype.uploadFile = fake.resolves(uploadResponseMock); - - const form = { - type: 'form', - components: [ - { - input: true, - label: 'Text Field', - key: 'textField', - type: 'textfield', - overlay: { - top: 135, - left: 211.516, - height: 20, - width: 100, - page: 1, - }, - }, - { - input: true, - label: 'Submit', - key: 'submit', - action: 'submit', - type: 'button', - }, - ], - display: 'pdf', - name: 'testPdfForm', - }; - const builder = new FormBuilder(document.createElement('div'), form, {}); - - builder.ready - .then(function(builder) { - builder.on('pdfUploaded', (result) => { - expect(result).to.be.deep.equal(uploadResponseMock.data); - expect(builder.webform.form.components).to.be.deep.equal(form.components); - expect(builder.webform.form.settings.pdf.nonFillableConversionUsed).to.be.false; - - done(); - }); - - builder.upload(); + it('Should not assign fields from PDF auto conversion to non pristine form', function (done) { + const uploadResponseMock = getUploadResponseMock(true); + + Formio.prototype.uploadFile = fake.resolves(uploadResponseMock); + + const form = { + type: 'form', + components: [ + { + input: true, + label: 'Text Field', + key: 'textField', + type: 'textfield', + overlay: { + top: 135, + left: 211.516, + height: 20, + width: 100, + page: 1, + }, + }, + { + input: true, + label: 'Submit', + key: 'submit', + action: 'submit', + type: 'button', + }, + ], + display: 'pdf', + name: 'testPdfForm', + }; + const builder = new FormBuilder( + document.createElement('div'), + form, + {}, + ); + + builder.ready.then(function (builder) { + builder.on('pdfUploaded', (result) => { + expect(result).to.be.deep.equal(uploadResponseMock.data); + expect(builder.webform.form.components).to.be.deep.equal( + form.components, + ); + expect( + builder.webform.form.settings.pdf + .nonFillableConversionUsed, + ).to.be.false; + + done(); + }); + + builder.upload(); + }); }); }); - }); }); diff --git a/src/Webform.js b/src/Webform.js index e9541876bc..bdb671e566 100644 --- a/src/Webform.js +++ b/src/Webform.js @@ -7,13 +7,13 @@ import { Formio } from './Formio'; import Components from './components/Components'; import NestedDataComponent from './components/_classes/nesteddata/NestedDataComponent'; import { - fastCloneDeep, - currentTimezone, - unescapeHTML, - getStringFromComponentPath, - searchComponents, - convertStringToHTMLElement, - getArrayFromComponentPath + fastCloneDeep, + currentTimezone, + unescapeHTML, + getStringFromComponentPath, + searchComponents, + convertStringToHTMLElement, + getArrayFromComponentPath, } from './utils/utils'; import { eachComponent } from './utils/formUtils'; @@ -24,1634 +24,1781 @@ Formio.forms = {}; Formio.registerComponent = Components.setComponent; function getIconSet(icons) { - if (icons === 'fontawesome') { - return 'fa'; - } - return icons || ''; + if (icons === 'fontawesome') { + return 'fa'; + } + return icons || ''; } function getOptions(options) { - options = _.defaults(options, { - submitOnEnter: false, - iconset: getIconSet((options && options.icons) ? options.icons : Formio.icons), - i18next: null, - saveDraft: false, - alwaysDirty: false, - saveDraftThrottle: 5000, - display: 'form', - cdnUrl: Formio.cdn.baseUrl - }); - if (!options.events) { - options.events = new EventEmitter(); - } - return options; + options = _.defaults(options, { + submitOnEnter: false, + iconset: getIconSet( + options && options.icons ? options.icons : Formio.icons, + ), + i18next: null, + saveDraft: false, + alwaysDirty: false, + saveDraftThrottle: 5000, + display: 'form', + cdnUrl: Formio.cdn.baseUrl, + }); + if (!options.events) { + options.events = new EventEmitter(); + } + return options; } /** * Renders a Form.io form within the webpage. */ export default class Webform extends NestedDataComponent { - /** - * Creates a new Form instance. - * - * @param {Object} options - The options to create a new form instance. - * @param {boolean} options.saveDraft - Set this if you would like to enable the save draft feature. - * @param {boolean} options.saveDraftThrottle - The throttle for the save draft feature. - * @param {boolean} options.readOnly - Set this form to readOnly - * @param {boolean} options.noAlerts - Set to true to disable the alerts dialog. - * @param {boolean} options.i18n - The translation file for this rendering. @see https://github.com/formio/formio.js/blob/master/i18n.js - * @param {boolean} options.template - Provides a way to inject custom logic into the creation of every element rendered within the form. - */ - /* eslint-disable max-statements */ - constructor() { - let element, options; - if (arguments[0] instanceof HTMLElement || arguments[1]) { - element = arguments[0]; - options = arguments[1]; - } - else { - options = arguments[0]; - } - super(null, getOptions(options)); - - this.setElement(element); - - // Keep track of all available forms globally. - Formio.forms[this.id] = this; - - // Set the base url. - if (this.options.baseUrl) { - Formio.setBaseUrl(this.options.baseUrl); + /** + * Creates a new Form instance. + * + * @param {Object} options - The options to create a new form instance. + * @param {boolean} options.saveDraft - Set this if you would like to enable the save draft feature. + * @param {boolean} options.saveDraftThrottle - The throttle for the save draft feature. + * @param {boolean} options.readOnly - Set this form to readOnly + * @param {boolean} options.noAlerts - Set to true to disable the alerts dialog. + * @param {boolean} options.i18n - The translation file for this rendering. @see https://github.com/formio/formio.js/blob/master/i18n.js + * @param {boolean} options.template - Provides a way to inject custom logic into the creation of every element rendered within the form. + */ + /* eslint-disable max-statements */ + constructor() { + let element, options; + if (arguments[0] instanceof HTMLElement || arguments[1]) { + element = arguments[0]; + options = arguments[1]; + } else { + options = arguments[0]; + } + super(null, getOptions(options)); + + this.setElement(element); + + // Keep track of all available forms globally. + Formio.forms[this.id] = this; + + // Set the base url. + if (this.options.baseUrl) { + Formio.setBaseUrl(this.options.baseUrl); + } + + /** + * The type of this element. + * @type {string} + */ + this.type = 'form'; + this._src = ''; + this._loading = false; + this._form = {}; + this.draftEnabled = false; + this.savingDraft = true; + if (this.options.saveDraftThrottle) { + this.triggerSaveDraft = _.throttle( + this.saveDraft.bind(this), + this.options.saveDraftThrottle, + ); + } else { + this.triggerSaveDraft = this.saveDraft.bind(this); + } + + this.customErrors = []; + + /** + * Determines if this form should submit the API on submit. + * @type {boolean} + */ + this.nosubmit = false; + + /** + * Determines if the form has tried to be submitted, error or not. + * + * @type {boolean} + */ + this.submitted = false; + + /** + * Determines if the form is being submitted at the moment. + * + * @type {boolean} + */ + this.submitting = false; + + /** + * The Formio instance for this form. + * @type {Formio} + */ + this.formio = null; + + /** + * The loader HTML element. + * @type {HTMLElement} + */ + this.loader = null; + + /** + * The alert HTML element + * @type {HTMLElement} + */ + this.alert = null; + + /** + * Promise that is triggered when the submission is done loading. + * @type {Promise} + */ + this.onSubmission = null; + + /** + * Determines if this submission is explicitly set. + * @type {boolean} + */ + this.submissionSet = false; + + /** + * Promise that executes when the form is ready and rendered. + * @type {Promise} + * + * @example + * import Webform from '@formio/js/Webform'; + * let form = new Webform(document.getElementById('formio')); + * form.formReady.then(() => { + * console.log('The form is ready!'); + * }); + * form.src = 'https://examples.form.io/example'; + */ + this.formReady = new Promise((resolve, reject) => { + /** + * Called when the formReady state of this form has been resolved. + * + * @type {function} + */ + this.formReadyResolve = resolve; + + /** + * Called when this form could not load and is rejected. + * + * @type {function} + */ + this.formReadyReject = reject; + }); + + /** + * Promise that executes when the submission is ready and rendered. + * @type {Promise} + * + * @example + * import Webform from '@formio/js/Webform'; + * let form = new Webform(document.getElementById('formio')); + * form.submissionReady.then(() => { + * console.log('The submission is ready!'); + * }); + * form.src = 'https://examples.form.io/example/submission/234234234234234243'; + */ + this.submissionReady = new Promise((resolve, reject) => { + /** + * Called when the formReady state of this form has been resolved. + * + * @type {function} + */ + this.submissionReadyResolve = resolve; + + /** + * Called when this form could not load and is rejected. + * + * @type {function} + */ + this.submissionReadyReject = reject; + }); + + this.shortcuts = []; + + // Set language after everything is established. + this.language = this.i18next.language; + + // See if we need to restore the draft from a user. + if (this.options.saveDraft && !this.options.skipDraftRestore) { + this.formReady.then(() => { + const user = Formio.getUser(); + // Only restore a draft if the submission isn't explicitly set. + if (user && !this.submissionSet) { + this.restoreDraft(user._id); + } + }); + } + + this.component.clearOnHide = false; + + // Ensure the root is set to this component. + this.root = this; + this.localRoot = this; + } + /* eslint-enable max-statements */ + + get language() { + return this.options.language; + } + + get emptyValue() { + return null; + } + + componentContext() { + return this._data; } /** - * The type of this element. - * @type {string} + * Sets the language for this form. + * + * @param lang + * @return {Promise} */ - this.type = 'form'; - this._src = ''; - this._loading = false; - this._form = {}; - this.draftEnabled = false; - this.savingDraft = true; - if (this.options.saveDraftThrottle) { - this.triggerSaveDraft = _.throttle(this.saveDraft.bind(this), this.options.saveDraftThrottle); + set language(lang) { + if (!this.i18next) { + return; + } + this.options.language = lang; + if (this.i18next.language === lang) { + return; + } + this.i18next.changeLanguage(lang, (err) => { + if (err) { + return; + } + this.rebuild(); + this.emit('languageChanged'); + }); } - else { - this.triggerSaveDraft = this.saveDraft.bind(this); + + get componentComponents() { + return this.form.components; } - this.customErrors = []; + get shadowRoot() { + return this.options.shadowRoot; + } /** - * Determines if this form should submit the API on submit. - * @type {boolean} + * Add a language for translations + * + * @param code + * @param lang + * @param active + * @return {*} */ - this.nosubmit = false; + addLanguage(code, lang, active = false) { + if (this.i18next) { + var translations = _.assign( + fastCloneDeep(i18nDefaults.resources.en.translation), + lang, + ); + this.i18next.addResourceBundle( + code, + 'translation', + translations, + true, + true, + ); + if (active) { + this.language = code; + } + } + } + + keyboardCatchableElement(element) { + if (element.nodeName === 'TEXTAREA') { + return false; + } + + if (element.nodeName === 'INPUT') { + return ['text', 'email', 'password'].indexOf(element.type) === -1; + } + + return true; + } + + executeShortcuts = (event) => { + const { target } = event; + if (!this.keyboardCatchableElement(target)) { + return; + } + + const ctrl = event.ctrlKey || event.metaKey; + const keyCode = event.keyCode; + let char = ''; + + if (65 <= keyCode && keyCode <= 90) { + char = String.fromCharCode(keyCode); + } else if (keyCode === 13) { + char = 'Enter'; + } else if (keyCode === 27) { + char = 'Esc'; + } + + _.each(this.shortcuts, (shortcut) => { + if (shortcut.ctrl && !ctrl) { + return; + } + + if (shortcut.shortcut === char) { + shortcut.element.click(); + event.preventDefault(); + } + }); + }; + + addShortcut(element, shortcut) { + if (!shortcut || !/^([A-Z]|Enter|Esc)$/i.test(shortcut)) { + return; + } + + shortcut = _.capitalize(shortcut); + + if (shortcut === 'Enter' || shortcut === 'Esc') { + // Restrict Enter and Esc only for buttons + if (element.tagName !== 'BUTTON') { + return; + } + + this.shortcuts.push({ + shortcut, + element, + }); + } else { + this.shortcuts.push({ + ctrl: true, + shortcut, + element, + }); + } + } + + removeShortcut(element, shortcut) { + if (!shortcut || !/^([A-Z]|Enter|Esc)$/i.test(shortcut)) { + return; + } + + _.remove(this.shortcuts, { + shortcut, + element, + }); + } + + /** + * Get the embed source of the form. + * + * @returns {string} + */ + get src() { + return this._src; + } + + /** + * Loads the submission if applicable. + */ + loadSubmission() { + this.loadingSubmission = true; + if (this.formio.submissionId) { + this.onSubmission = this.formio + .loadSubmission() + .then( + (submission) => this.setSubmission(submission), + (err) => this.submissionReadyReject(err), + ) + .catch((err) => this.submissionReadyReject(err)); + } else { + this.submissionReadyResolve(); + } + return this.submissionReady; + } /** - * Determines if the form has tried to be submitted, error or not. + * Set the src of the form renderer. * - * @type {boolean} + * @param value + * @param options */ - this.submitted = false; + setSrc(value, options) { + if (this.setUrl(value, options)) { + this.nosubmit = false; + return this.formio + .loadForm({ params: { live: 1 } }) + .then((form) => { + const setForm = this.setForm(form); + this.loadSubmission(); + return setForm; + }) + .catch((err) => { + console.warn(err); + this.formReadyReject(err); + }); + } + return Promise.resolve(); + } /** - * Determines if the form is being submitted at the moment. + * Set the Form source, which is typically the Form.io embed URL. * - * @type {boolean} + * @param {string} value - The value of the form embed url. + * + * @example + * import Webform from '@formio/js/Webform'; + * let form = new Webform(document.getElementById('formio')); + * form.formReady.then(() => { + * console.log('The form is formReady!'); + * }); + * form.src = 'https://examples.form.io/example'; */ - this.submitting = false; + set src(value) { + this.setSrc(value); + } /** - * The Formio instance for this form. - * @type {Formio} + * Get the embed source of the form. + * + * @returns {string} */ - this.formio = null; + get url() { + return this._src; + } /** - * The loader HTML element. - * @type {HTMLElement} + * Sets the url of the form renderer. + * + * @param value + * @param options */ - this.loader = null; + setUrl(value, options) { + if (!value || typeof value !== 'string' || value === this._src) { + return false; + } + this._src = value; + this.nosubmit = true; + this.formio = this.options.formio = new Formio(value, options); + + if (this.type === 'form') { + // Set the options source so this can be passed to other components. + this.options.src = value; + } + return true; + } /** - * The alert HTML element - * @type {HTMLElement} + * Set the form source but don't initialize the form and submission from the url. + * + * @param {string} value - The value of the form embed url. */ - this.alert = null; + set url(value) { + this.setUrl(value); + } /** - * Promise that is triggered when the submission is done loading. - * @type {Promise} + * Called when both the form and submission have been loaded. + * + * @returns {Promise} - The promise to trigger when both form and submission have loaded. */ - this.onSubmission = null; + get ready() { + return this.formReady.then(() => { + return super.ready.then(() => { + return this.loadingSubmission ? this.submissionReady : true; + }); + }); + } /** - * Determines if this submission is explicitly set. - * @type {boolean} + * Returns if this form is loading. + * + * @returns {boolean} - TRUE means the form is loading, FALSE otherwise. */ - this.submissionSet = false; + get loading() { + return this._loading; + } /** - * Promise that executes when the form is ready and rendered. - * @type {Promise} + * Set the loading state for this form, and also show the loader spinner. * - * @example - * import Webform from '@formio/js/Webform'; - * let form = new Webform(document.getElementById('formio')); - * form.formReady.then(() => { - * console.log('The form is ready!'); - * }); - * form.src = 'https://examples.form.io/example'; + * @param {boolean} loading - If this form should be "loading" or not. */ - this.formReady = new Promise((resolve, reject) => { - /** - * Called when the formReady state of this form has been resolved. - * - * @type {function} - */ - this.formReadyResolve = resolve; - - /** - * Called when this form could not load and is rejected. - * - * @type {function} - */ - this.formReadyReject = reject; - }); + set loading(loading) { + if (this._loading !== loading) { + this._loading = loading; + if (!this.loader && loading) { + this.loader = this.ce('div', { + class: 'loader-wrapper', + }); + const spinner = this.ce('div', { + class: 'loader text-center', + }); + this.loader.appendChild(spinner); + } + /* eslint-disable max-depth */ + if (this.loader) { + try { + if (loading) { + this.prependTo(this.loader, this.wrapper); + } else { + this.removeChildFrom(this.loader, this.wrapper); + } + } catch (err) { + // ingore + } + } + /* eslint-enable max-depth */ + } + } /** - * Promise that executes when the submission is ready and rendered. - * @type {Promise} + * Sets the JSON schema for the form to be rendered. * * @example * import Webform from '@formio/js/Webform'; * let form = new Webform(document.getElementById('formio')); - * form.submissionReady.then(() => { - * console.log('The submission is ready!'); + * form.setForm({ + * components: [ + * { + * type: 'textfield', + * key: 'firstName', + * label: 'First Name', + * placeholder: 'Enter your first name.', + * input: true + * }, + * { + * type: 'textfield', + * key: 'lastName', + * label: 'Last Name', + * placeholder: 'Enter your last name', + * input: true + * }, + * { + * type: 'button', + * action: 'submit', + * label: 'Submit', + * theme: 'primary' + * } + * ] * }); - * form.src = 'https://examples.form.io/example/submission/234234234234234243'; + * + * @param {Object} form - The JSON schema of the form @see https://examples.form.io/example for an example JSON schema. + * @param flags + * @returns {*} */ - this.submissionReady = new Promise((resolve, reject) => { - /** - * Called when the formReady state of this form has been resolved. - * - * @type {function} - */ - this.submissionReadyResolve = resolve; - - /** - * Called when this form could not load and is rejected. - * - * @type {function} - */ - this.submissionReadyReject = reject; - }); + setForm(form, flags) { + const isFormAlreadySet = this._form && this._form.components?.length; + try { + // Do not set the form again if it has been already set + if ( + isFormAlreadySet && + JSON.stringify(this._form) === JSON.stringify(form) + ) { + return Promise.resolve(); + } - this.shortcuts = []; - - // Set language after everything is established. - this.language = this.i18next.language; - - // See if we need to restore the draft from a user. - if (this.options.saveDraft && !this.options.skipDraftRestore) { - this.formReady.then(()=> { - const user = Formio.getUser(); - // Only restore a draft if the submission isn't explicitly set. - if (user && !this.submissionSet) { - this.restoreDraft(user._id); - } - }); - } - - this.component.clearOnHide = false; - - // Ensure the root is set to this component. - this.root = this; - this.localRoot = this; - } - /* eslint-enable max-statements */ - - get language() { - return this.options.language; - } - - get emptyValue() { - return null; - } - - componentContext() { - return this._data; - } - - /** - * Sets the language for this form. - * - * @param lang - * @return {Promise} - */ - set language(lang) { - if (!this.i18next) { - return; - } - this.options.language = lang; - if (this.i18next.language === lang) { - return; - } - this.i18next.changeLanguage(lang, (err) => { - if (err) { - return; - } - this.rebuild(); - this.emit('languageChanged'); - }); - } + // Create the form. + this._form = flags?.keepAsReference ? form : _.cloneDeep(form); - get componentComponents() { - return this.form.components; - } + if (this.onSetForm) { + this.onSetForm(_.cloneDeep(this._form), form); + } - get shadowRoot() { - return this.options.shadowRoot; - } + if (this.parent?.component?.modalEdit) { + return Promise.resolve(); + } + } catch (err) { + console.warn(err); + // If provided form is not a valid JSON object, do not set it too + return Promise.resolve(); + } - /** - * Add a language for translations - * - * @param code - * @param lang - * @param active - * @return {*} - */ - addLanguage(code, lang, active = false) { - if (this.i18next) { - var translations = _.assign(fastCloneDeep(i18nDefaults.resources.en.translation), lang); - this.i18next.addResourceBundle(code, 'translation', translations, true, true); - if (active) { - this.language = code; - } - } - } + // Allow the form to provide component overrides. + if (form && form.settings && form.settings.components) { + this.options.components = form.settings.components; + } - keyboardCatchableElement(element) { - if (element.nodeName === 'TEXTAREA') { - return false; - } + if (form && form.properties) { + this.options.properties = form.properties; + } + // Use the sanitize config from the form settings or the global sanitize config if it is not provided in the options + if (!this.options.sanitizeConfig && !this.builderMode) { + this.options.sanitizeConfig = + _.get(form, 'settings.sanitizeConfig') || + _.get(form, 'globalSettings.sanitizeConfig'); + } - if (element.nodeName === 'INPUT') { - return [ - 'text', - 'email', - 'password' - ].indexOf(element.type) === -1; + if ('schema' in form && compareVersions(form.schema, '1.x') > 0) { + this.ready.then(() => { + this.setAlert( + 'alert alert-danger', + 'Form schema is for a newer version, please upgrade your renderer. Some functionality may not work.', + ); + }); + } + + // See if they pass a module, and evaluate it if so. + if (form && form.module) { + let formModule = null; + if (typeof form.module === 'string') { + try { + formModule = this.evaluate(`return ${form.module}`); + } catch (err) { + console.warn(err); + } + } else { + formModule = form.module; + } + if (formModule) { + Formio.use(formModule); + + // Since we got here after instantiation, we need to manually apply form options. + if (formModule.options && formModule.options.form) { + this.options = Object.assign( + this.options, + formModule.options.form, + ); + } + } + } + + this.initialized = false; + const rebuild = this.rebuild() || Promise.resolve(); + return rebuild.then(() => { + this.emit('formLoad', form); + this.triggerRecaptcha(); + // Make sure to trigger onChange after a render event occurs to speed up form rendering. + setTimeout(() => { + this.onChange(flags); + this.formReadyResolve(); + }, 0); + + return this.formReady; + }); } - return true; - } + /** + * Gets the form object. + * + * @returns {Object} - The form JSON schema. + */ + get form() { + if (!this._form) { + this._form = { + components: [], + }; + } + return this._form; + } - executeShortcuts = (event) => { - const { target } = event; - if (!this.keyboardCatchableElement(target)) { - return; + /** + * Sets the form value. + * + * @alias setForm + * @param {Object} form - The form schema object. + */ + set form(form) { + this.setForm(form); } - const ctrl = event.ctrlKey || event.metaKey; - const keyCode = event.keyCode; - let char = ''; + /** + * Returns the submission object that was set within this form. + * + * @returns {Object} + */ + get submission() { + return this.getValue(); + } - if (65 <= keyCode && keyCode <= 90) { - char = String.fromCharCode(keyCode); + /** + * Sets the submission of a form. + * + * @example + * import Webform from '@formio/js/Webform'; + * let form = new Webform(document.getElementById('formio')); + * form.src = 'https://examples.form.io/example'; + * form.submission = {data: { + * firstName: 'Joe', + * lastName: 'Smith', + * email: 'joe@example.com' + * }}; + * + * @param {Object} submission - The Form.io submission object. + */ + set submission(submission) { + this.setSubmission(submission); } - else if (keyCode === 13) { - char = 'Enter'; + + /** + * Sets a submission and returns the promise when it is ready. + * @param submission + * @param flags + * @return {Promise.} + */ + setSubmission(submission, flags = {}) { + flags = { + ...flags, + fromSubmission: _.has(flags, 'fromSubmission') + ? flags.fromSubmission + : true, + }; + return (this.onSubmission = this.formReady + .then( + (resolveFlags) => { + if (resolveFlags) { + flags = { + ...flags, + ...resolveFlags, + }; + } + this.submissionSet = true; + this.triggerChange(flags); + this.emit('beforeSetSubmission', submission); + this.setValue(submission, flags); + return this.submissionReadyResolve(submission); + }, + (err) => this.submissionReadyReject(err), + ) + .catch((err) => this.submissionReadyReject(err))); + } + + handleDraftError(errName, errDetails, restoreDraft) { + const errorMessage = _.trim(`${this.t(errName)} ${errDetails || ''}`); + console.warn(errorMessage); + this.emit( + restoreDraft ? 'restoreDraftError' : 'saveDraftError', + errDetails || errorMessage, + ); } - else if (keyCode === 27) { - char = 'Esc'; + + /** + * Saves a submission draft. + */ + saveDraft() { + if (!this.draftEnabled) { + return; + } + if (!this.formio) { + this.handleDraftError('saveDraftInstanceError'); + return; + } + if (!Formio.getUser()) { + this.handleDraftError('saveDraftAuthError'); + return; + } + const draft = fastCloneDeep(this.submission); + draft.state = 'draft'; + + if (!this.savingDraft) { + this.emit('saveDraftBegin'); + this.savingDraft = true; + this.formio + .saveSubmission(draft) + .then((sub) => { + // Set id to submission to avoid creating new draft submission + this.submission._id = sub._id; + this.savingDraft = false; + this.emit('saveDraft', sub); + }) + .catch((err) => { + this.savingDraft = false; + this.handleDraftError('saveDraftError', err); + }); + } } - _.each(this.shortcuts, (shortcut) => { - if (shortcut.ctrl && !ctrl) { - return; - } + /** + * Restores a draft submission based on the user who is authenticated. + * + * @param {userId} - The user id where we need to restore the draft from. + */ + restoreDraft(userId) { + if (!this.formio) { + this.handleDraftError('restoreDraftInstanceError', null, true); + return; + } + this.savingDraft = true; + this.formio + .loadSubmissions({ + params: { + state: 'draft', + owner: userId, + }, + }) + .then((submissions) => { + if (submissions.length > 0 && !this.options.skipDraftRestore) { + const draft = fastCloneDeep(submissions[0]); + return this.setSubmission(draft).then(() => { + this.draftEnabled = true; + this.savingDraft = false; + this.emit('restoreDraft', draft); + }); + } + // Enable drafts so that we can keep track of changes. + this.draftEnabled = true; + this.savingDraft = false; + this.emit('restoreDraft', null); + }) + .catch((err) => { + this.draftEnabled = true; + this.savingDraft = false; + this.handleDraftError('restoreDraftError', err, true); + }); + } - if (shortcut.shortcut === char) { - shortcut.element.click(); - event.preventDefault(); - } - }); - }; + get schema() { + const schema = fastCloneDeep(_.omit(this._form, ['components'])); + schema.components = []; + this.eachComponent((component) => + schema.components.push(component.schema), + ); + return schema; + } - addShortcut(element, shortcut) { - if (!shortcut || !/^([A-Z]|Enter|Esc)$/i.test(shortcut)) { - return; + mergeData(_this, _that) { + _.mergeWith(_this, _that, (thisValue, thatValue) => { + if ( + Array.isArray(thisValue) && + Array.isArray(thatValue) && + thisValue.length !== thatValue.length + ) { + return thatValue; + } + }); } - shortcut = _.capitalize(shortcut); + setValue(submission, flags = {}) { + if (!submission || !submission.data) { + submission = { + data: {}, + metadata: submission.metadata, + }; + } + // Metadata needs to be available before setValue + this._submission.metadata = submission.metadata || {}; + this.editing = !!submission._id; + + // Set the timezone in the options if available. + if ( + !this.options.submissionTimezone && + submission.metadata && + submission.metadata.timezone + ) { + this.options.submissionTimezone = submission.metadata.timezone; + } - if (shortcut === 'Enter' || shortcut === 'Esc') { - // Restrict Enter and Esc only for buttons - if (element.tagName !== 'BUTTON') { - return; - } + const changed = super.setValue(submission.data, flags); + if (!flags.sanitize) { + this.mergeData(this.data, submission.data); + } - this.shortcuts.push({ - shortcut, - element - }); - } - else { - this.shortcuts.push({ - ctrl: true, - shortcut, - element - }); + submission.data = this.data; + this._submission = submission; + return changed; } - } - removeShortcut(element, shortcut) { - if (!shortcut || !/^([A-Z]|Enter|Esc)$/i.test(shortcut)) { - return; + getValue() { + if (!this._submission.data) { + this._submission.data = {}; + } + if (this.viewOnly) { + return this._submission; + } + const submission = this._submission; + submission.data = this.data; + return this._submission; } - _.remove(this.shortcuts, { - shortcut, - element - }); - } - - /** - * Get the embed source of the form. - * - * @returns {string} - */ - get src() { - return this._src; - } - - /** - * Loads the submission if applicable. - */ - loadSubmission() { - this.loadingSubmission = true; - if (this.formio.submissionId) { - this.onSubmission = this.formio.loadSubmission().then( - (submission) => this.setSubmission(submission), - (err) => this.submissionReadyReject(err) - ).catch( - (err) => this.submissionReadyReject(err) - ); - } - else { - this.submissionReadyResolve(); - } - return this.submissionReady; - } - - /** - * Set the src of the form renderer. - * - * @param value - * @param options - */ - setSrc(value, options) { - if (this.setUrl(value, options)) { - this.nosubmit = false; - return this.formio.loadForm({ params: { live: 1 } }).then( - (form) => { - const setForm = this.setForm(form); - this.loadSubmission(); - return setForm; - }).catch((err) => { - console.warn(err); - this.formReadyReject(err); - }); - } - return Promise.resolve(); - } - - /** - * Set the Form source, which is typically the Form.io embed URL. - * - * @param {string} value - The value of the form embed url. - * - * @example - * import Webform from '@formio/js/Webform'; - * let form = new Webform(document.getElementById('formio')); - * form.formReady.then(() => { - * console.log('The form is formReady!'); - * }); - * form.src = 'https://examples.form.io/example'; - */ - set src(value) { - this.setSrc(value); - } - - /** - * Get the embed source of the form. - * - * @returns {string} - */ - get url() { - return this._src; - } - - /** - * Sets the url of the form renderer. - * - * @param value - * @param options - */ - setUrl(value, options) { - if ( - !value || - (typeof value !== 'string') || - (value === this._src) - ) { - return false; - } - this._src = value; - this.nosubmit = true; - this.formio = this.options.formio = new Formio(value, options); - - if (this.type === 'form') { - // Set the options source so this can be passed to other components. - this.options.src = value; - } - return true; - } - - /** - * Set the form source but don't initialize the form and submission from the url. - * - * @param {string} value - The value of the form embed url. - */ - set url(value) { - this.setUrl(value); - } - - /** - * Called when both the form and submission have been loaded. - * - * @returns {Promise} - The promise to trigger when both form and submission have loaded. - */ - get ready() { - return this.formReady.then(() => { - return super.ready.then(() => { - return this.loadingSubmission ? this.submissionReady : true; - }); - }); - } - - /** - * Returns if this form is loading. - * - * @returns {boolean} - TRUE means the form is loading, FALSE otherwise. - */ - get loading() { - return this._loading; - } - - /** - * Set the loading state for this form, and also show the loader spinner. - * - * @param {boolean} loading - If this form should be "loading" or not. - */ - set loading(loading) { - if (this._loading !== loading) { - this._loading = loading; - if (!this.loader && loading) { - this.loader = this.ce('div', { - class: 'loader-wrapper' - }); - const spinner = this.ce('div', { - class: 'loader text-center' - }); - this.loader.appendChild(spinner); - } - /* eslint-disable max-depth */ - if (this.loader) { - try { - if (loading) { - this.prependTo(this.loader, this.wrapper); - } - else { - this.removeChildFrom(this.loader, this.wrapper); - } - } - catch (err) { - // ingore - } - } - /* eslint-enable max-depth */ - } - } - - /** - * Sets the JSON schema for the form to be rendered. - * - * @example - * import Webform from '@formio/js/Webform'; - * let form = new Webform(document.getElementById('formio')); - * form.setForm({ - * components: [ - * { - * type: 'textfield', - * key: 'firstName', - * label: 'First Name', - * placeholder: 'Enter your first name.', - * input: true - * }, - * { - * type: 'textfield', - * key: 'lastName', - * label: 'Last Name', - * placeholder: 'Enter your last name', - * input: true - * }, - * { - * type: 'button', - * action: 'submit', - * label: 'Submit', - * theme: 'primary' - * } - * ] - * }); - * - * @param {Object} form - The JSON schema of the form @see https://examples.form.io/example for an example JSON schema. - * @param flags - * @returns {*} - */ - setForm(form, flags) { - const isFormAlreadySet = this._form && this._form.components?.length; - try { - // Do not set the form again if it has been already set - if (isFormAlreadySet && JSON.stringify(this._form) === JSON.stringify(form)) { - return Promise.resolve(); - } + /** + * Build the form. + */ + init() { + if (this.options.submission) { + const submission = _.extend({}, this.options.submission); + this._submission = submission; + this._data = submission.data; + } else { + this._submission = this._submission || { data: {} }; + } - // Create the form. - this._form = flags?.keepAsReference ? form : _.cloneDeep(form); + // Remove any existing components. + if (this.components && this.components.length) { + this.destroyComponents(); + this.components = []; + } - if (this.onSetForm) { - this.onSetForm(_.cloneDeep(this._form), form); - } + if (this.component) { + this.component.components = this.form ? this.form.components : []; + } else { + this.component = this.form; + } + this.component.type = 'form'; + this.component.input = false; + + this.addComponents(); + this.on( + 'submitButton', + (options) => { + this.submit(false, options).catch((e) => { + options.instance.loading = false; + return e !== false && console.log(e); + }); + }, + true, + ); + + this.on( + 'checkValidity', + (data) => this.checkValidity(data, true, data), + true, + ); + this.on( + 'requestUrl', + (args) => this.submitUrl(args.url, args.headers), + true, + ); + this.on('resetForm', () => this.resetValue(), true); + this.on('deleteSubmission', () => this.deleteSubmission(), true); + this.on('refreshData', () => this.updateValue(), true); + + this.executeFormController(); + + return this.formReady; + } + + executeFormController() { + // If no controller value or + // hidden and set to clearOnHide (Don't calculate a value for a hidden field set to clear when hidden) + if ( + !this.form || + !this.form.controller || + ((!this.visible || this.component.hidden) && + this.component.clearOnHide && + !this.rootPristine) + ) { + return false; + } - if (this.parent?.component?.modalEdit) { - return Promise.resolve(); - } - } - catch (err) { - console.warn(err); - // If provided form is not a valid JSON object, do not set it too - return Promise.resolve(); + this.formReady.then(() => { + this.evaluate(this.form.controller, { + components: this.components, + instance: this, + }); + }); } - // Allow the form to provide component overrides. - if (form && form.settings && form.settings.components) { - this.options.components = form.settings.components; + teardown() { + this.emit('formDelete', this.id); + delete Formio.forms[this.id]; + delete this.executeShortcuts; + delete this.triggerSaveDraft; + super.teardown(); } - if (form && form.properties) { - this.options.properties = form.properties; - } - // Use the sanitize config from the form settings or the global sanitize config if it is not provided in the options - if (!this.options.sanitizeConfig && !this.builderMode) { - this.options.sanitizeConfig = _.get(form, 'settings.sanitizeConfig') || _.get(form, 'globalSettings.sanitizeConfig'); - } + destroy(all = false) { + this.off('submitButton'); + this.off('checkValidity'); + this.off('requestUrl'); + this.off('resetForm'); + this.off('deleteSubmission'); + this.off('refreshData'); - if ('schema' in form && compareVersions(form.schema, '1.x') > 0) { - this.ready.then(() => { - this.setAlert('alert alert-danger', 'Form schema is for a newer version, please upgrade your renderer. Some functionality may not work.'); - }); + return super.destroy(all); } - // See if they pass a module, and evaluate it if so. - if (form && form.module) { - let formModule = null; - if (typeof form.module === 'string') { - try { - formModule = this.evaluate(`return ${form.module}`); - } - catch (err) { - console.warn(err); + build(element) { + if (element || this.element) { + return this.ready.then(() => { + element = element || this.element; + super.build(element); + }); } - } - else { - formModule = form.module; - } - if (formModule) { - Formio.use(formModule); + return this.ready; + } - // Since we got here after instantiation, we need to manually apply form options. - if (formModule.options && formModule.options.form) { - this.options = Object.assign(this.options, formModule.options.form); + getClassName() { + let classes = 'formio-form'; + if (this.options.readOnly) { + classes += ' formio-read-only'; } - } + return classes; } - this.initialized = false; - const rebuild = this.rebuild() || Promise.resolve(); - return rebuild.then(() => { - this.emit('formLoad', form); - this.triggerRecaptcha(); - // Make sure to trigger onChange after a render event occurs to speed up form rendering. - setTimeout(() => { - this.onChange(flags); - this.formReadyResolve(); - }, 0); + render() { + return super.render( + this.renderTemplate('webform', { + classes: this.getClassName(), + children: this.renderComponents(), + }), + this.builderMode ? 'builder' : 'form', + true, + ); + } - return this.formReady; - }); - } - - /** - * Gets the form object. - * - * @returns {Object} - The form JSON schema. - */ - get form() { - if (!this._form) { - this._form = { - components: [] - }; - } - return this._form; - } - - /** - * Sets the form value. - * - * @alias setForm - * @param {Object} form - The form schema object. - */ - set form(form) { - this.setForm(form); - } - - /** - * Returns the submission object that was set within this form. - * - * @returns {Object} - */ - get submission() { - return this.getValue(); - } - - /** - * Sets the submission of a form. - * - * @example - * import Webform from '@formio/js/Webform'; - * let form = new Webform(document.getElementById('formio')); - * form.src = 'https://examples.form.io/example'; - * form.submission = {data: { - * firstName: 'Joe', - * lastName: 'Smith', - * email: 'joe@example.com' - * }}; - * - * @param {Object} submission - The Form.io submission object. - */ - set submission(submission) { - this.setSubmission(submission); - } - - /** - * Sets a submission and returns the promise when it is ready. - * @param submission - * @param flags - * @return {Promise.} - */ - setSubmission(submission, flags = {}) { - flags = { - ...flags, - fromSubmission: _.has(flags, 'fromSubmission') ? flags.fromSubmission : true, - }; - return this.onSubmission = this.formReady.then( - (resolveFlags) => { - if (resolveFlags) { - flags = { - ...flags, - ...resolveFlags - }; - } - this.submissionSet = true; - this.triggerChange(flags); - this.emit('beforeSetSubmission', submission); - this.setValue(submission, flags); - return this.submissionReadyResolve(submission); - }, - (err) => this.submissionReadyReject(err) - ).catch( - (err) => this.submissionReadyReject(err) - ); - } - - handleDraftError(errName, errDetails, restoreDraft) { - const errorMessage = _.trim(`${this.t(errName)} ${errDetails || ''}`); - console.warn(errorMessage); - this.emit(restoreDraft ? 'restoreDraftError' : 'saveDraftError', errDetails || errorMessage); - } - - /** - * Saves a submission draft. - */ - saveDraft() { - if (!this.draftEnabled) { - return; - } - if (!this.formio) { - this.handleDraftError('saveDraftInstanceError'); - return; - } - if (!Formio.getUser()) { - this.handleDraftError('saveDraftAuthError'); - return; - } - const draft = fastCloneDeep(this.submission); - draft.state = 'draft'; - - if (!this.savingDraft) { - this.emit('saveDraftBegin'); - this.savingDraft = true; - this.formio.saveSubmission(draft).then((sub) => { - // Set id to submission to avoid creating new draft submission - this.submission._id = sub._id; - this.savingDraft = false; - this.emit('saveDraft', sub); - }) - .catch(err => { - this.savingDraft = false; - this.handleDraftError('saveDraftError', err); - }); - } - } - - /** - * Restores a draft submission based on the user who is authenticated. - * - * @param {userId} - The user id where we need to restore the draft from. - */ - restoreDraft(userId) { - if (!this.formio) { - this.handleDraftError('restoreDraftInstanceError', null, true); - return; - } - this.savingDraft = true; - this.formio.loadSubmissions({ - params: { - state: 'draft', - owner: userId - } - }).then(submissions => { - if (submissions.length > 0 && !this.options.skipDraftRestore) { - const draft = fastCloneDeep(submissions[0]); - return this.setSubmission(draft).then(() => { - this.draftEnabled = true; - this.savingDraft = false; - this.emit('restoreDraft', draft); + redraw() { + // Don't bother if we have not built yet. + if (!this.element) { + return Promise.resolve(); + } + this.clear(); + this.setContent(this.element, this.render()); + return this.attach(this.element); + } + + attach(element) { + this.setElement(element); + this.loadRefs(element, { webform: 'single' }); + const childPromise = this.attachComponents(this.refs.webform); + this.addEventListener(document, 'keydown', this.executeShortcuts); + this.currentForm = this; + this.hook('attachWebform', element, this); + return childPromise.then(() => { + this.emit('render', this.element); + + return this.setValue(this._submission, { + noUpdateEvent: true, + }); }); - } - // Enable drafts so that we can keep track of changes. - this.draftEnabled = true; - this.savingDraft = false; - this.emit('restoreDraft', null); - }) - .catch(err => { - this.draftEnabled = true; - this.savingDraft = false; - this.handleDraftError('restoreDraftError', err, true); - }); - } - - get schema() { - const schema = fastCloneDeep(_.omit(this._form, ['components'])); - schema.components = []; - this.eachComponent((component) => schema.components.push(component.schema)); - return schema; - } - - mergeData(_this, _that) { - _.mergeWith(_this, _that, (thisValue, thatValue) => { - if (Array.isArray(thisValue) && Array.isArray(thatValue) && thisValue.length !== thatValue.length) { - return thatValue; - } - }); - } + } - setValue(submission, flags = {}) { - if (!submission || !submission.data) { - submission = { - data: {}, - metadata: submission.metadata, - }; - } - // Metadata needs to be available before setValue - this._submission.metadata = submission.metadata || {}; - this.editing = !!submission._id; - - // Set the timezone in the options if available. - if ( - !this.options.submissionTimezone && - submission.metadata && - submission.metadata.timezone - ) { - this.options.submissionTimezone = submission.metadata.timezone; - } - - const changed = super.setValue(submission.data, flags); - if (!flags.sanitize) { - this.mergeData(this.data, submission.data); - } - - submission.data = this.data; - this._submission = submission; - return changed; - } - - getValue() { - if (!this._submission.data) { - this._submission.data = {}; - } - if (this.viewOnly) { - return this._submission; - } - const submission = this._submission; - submission.data = this.data; - return this._submission; - } - - /** - * Build the form. - */ - init() { - if (this.options.submission) { - const submission = _.extend({}, this.options.submission); - this._submission = submission; - this._data = submission.data; - } - else { - this._submission = this._submission || { data: {} }; - } - - // Remove any existing components. - if (this.components && this.components.length) { - this.destroyComponents(); - this.components = []; - } - - if (this.component) { - this.component.components = this.form ? this.form.components : []; - } - else { - this.component = this.form; - } - this.component.type = 'form'; - this.component.input = false; - - this.addComponents(); - this.on('submitButton', options => { - this.submit(false, options).catch(e => { - options.instance.loading = false; - return e !== false && console.log(e); - }); - }, true); - - this.on('checkValidity', (data) => this.checkValidity(data, true, data), true); - this.on('requestUrl', (args) => (this.submitUrl(args.url,args.headers)), true); - this.on('resetForm', () => this.resetValue(), true); - this.on('deleteSubmission', () => this.deleteSubmission(), true); - this.on('refreshData', () => this.updateValue(), true); - - this.executeFormController(); - - return this.formReady; - } - - executeFormController() { - // If no controller value or - // hidden and set to clearOnHide (Don't calculate a value for a hidden field set to clear when hidden) - if ( - !this.form || !this.form.controller - || ((!this.visible || this.component.hidden) && this.component.clearOnHide && !this.rootPristine) - ) { - return false; - } - - this.formReady.then(() => { - this.evaluate(this.form.controller, { - components: this.components, - instance: this, - }); - }); - } - - teardown() { - this.emit('formDelete', this.id); - delete Formio.forms[this.id]; - delete this.executeShortcuts; - delete this.triggerSaveDraft; - super.teardown(); - } - - destroy(all = false) { - this.off('submitButton'); - this.off('checkValidity'); - this.off('requestUrl'); - this.off('resetForm'); - this.off('deleteSubmission'); - this.off('refreshData'); - - return super.destroy(all); - } - - build(element) { - if (element || this.element) { - return this.ready.then(() => { - element = element || this.element; - super.build(element); - }); - } - return this.ready; - } - - getClassName() { - let classes = 'formio-form'; - if (this.options.readOnly) { - classes += ' formio-read-only'; - } - return classes; - } - - render() { - return super.render(this.renderTemplate('webform', { - classes: this.getClassName(), - children: this.renderComponents(), - }), this.builderMode ? 'builder' : 'form', true); - } - - redraw() { - // Don't bother if we have not built yet. - if (!this.element) { - return Promise.resolve(); - } - this.clear(); - this.setContent(this.element, this.render()); - return this.attach(this.element); - } - - attach(element) { - this.setElement(element); - this.loadRefs(element, { webform: 'single' }); - const childPromise = this.attachComponents(this.refs.webform); - this.addEventListener(document, 'keydown', this.executeShortcuts); - this.currentForm = this; - this.hook('attachWebform', element, this); - return childPromise.then(() => { - this.emit('render', this.element); - - return this.setValue(this._submission, { - noUpdateEvent: true, - }); - }); - } + hasRequiredFields() { + let result = false; - hasRequiredFields() { - let result = false; + eachComponent( + this.form.components, + (component) => { + if (component.validate.required) { + result = true; + return true; + } + }, + true, + ); - eachComponent(this.form.components, (component) => { - if (component.validate.required) { - result = true; - return true; - } - }, true); - - return result; - } - - resetValue() { - _.each(this.getComponents(), (comp) => (comp.resetValue())); - this.setPristine(true); - this.onChange({ resetValue: true }); - } - - /** - * Sets a new alert to display in the error dialog of the form. - * - * @param {string} type - The type of alert to display. "danger", "success", "warning", etc. - * @param {string} message - The message to show in the alert. - * @param {Object} options - */ - setAlert(type, message, options) { - if (!type && this.submitted) { - if (this.alert) { - if (this.refs.errorRef && this.refs.errorRef.length) { - this.refs.errorRef.forEach(el => { - this.removeEventListener(el, 'click'); - this.removeEventListener(el, 'keypress'); - }); - } - this.removeChild(this.alert); - this.alert = null; - } - return; + return result; } - if (this.options.noAlerts) { - if (!message) { - this.emit('error', false); - } - return; + + resetValue() { + _.each(this.getComponents(), (comp) => comp.resetValue()); + this.setPristine(true); + this.onChange({ resetValue: true }); } - if (this.alert) { - try { - if (this.refs.errorRef && this.refs.errorRef.length) { - this.refs.errorRef.forEach(el => { - this.removeEventListener(el, 'click'); - this.removeEventListener(el, 'keypress'); - }); + + /** + * Sets a new alert to display in the error dialog of the form. + * + * @param {string} type - The type of alert to display. "danger", "success", "warning", etc. + * @param {string} message - The message to show in the alert. + * @param {Object} options + */ + setAlert(type, message, options) { + if (!type && this.submitted) { + if (this.alert) { + if (this.refs.errorRef && this.refs.errorRef.length) { + this.refs.errorRef.forEach((el) => { + this.removeEventListener(el, 'click'); + this.removeEventListener(el, 'keypress'); + }); + } + this.removeChild(this.alert); + this.alert = null; + } + return; + } + if (this.options.noAlerts) { + if (!message) { + this.emit('error', false); + } + return; + } + if (this.alert) { + try { + if (this.refs.errorRef && this.refs.errorRef.length) { + this.refs.errorRef.forEach((el) => { + this.removeEventListener(el, 'click'); + this.removeEventListener(el, 'keypress'); + }); + } + this.removeChild(this.alert); + this.alert = null; + } catch (err) { + // ignore + } + } + if (message) { + const attrs = { + class: (options && options.classes) || `alert alert-${type}`, + id: `error-list-${this.id}`, + }; + + const templateOptions = { + message: + message instanceof HTMLElement + ? message.outerHTML + : message, + attrs: attrs, + type, + }; + + this.alert = convertStringToHTMLElement( + this.renderTemplate('alert', templateOptions), + `#${attrs.id}`, + ); + } + if (!this.alert) { + return; } - this.removeChild(this.alert); - this.alert = null; - } - catch (err) { - // ignore - } - } - if (message) { - const attrs = { - class: (options && options.classes) || `alert alert-${type}`, - id: `error-list-${this.id}`, - }; - const templateOptions = { - message: message instanceof HTMLElement ? message.outerHTML : message, - attrs: attrs, - type - }; + this.loadRefs(this.alert, { errorRef: 'multiple' }); - this.alert = convertStringToHTMLElement(this.renderTemplate('alert', templateOptions),`#${attrs.id}`); + if (this.refs.errorRef && this.refs.errorRef.length) { + this.refs.errorRef.forEach((el) => { + this.addEventListener(el, 'click', (e) => { + const key = e.currentTarget.dataset.componentKey; + this.focusOnComponent(key); + }); + this.addEventListener(el, 'keydown', (e) => { + if (e.keyCode === 13) { + e.preventDefault(); + const key = e.currentTarget.dataset.componentKey; + this.focusOnComponent(key); + } + }); + }); + } + this.prepend(this.alert); } - if (!this.alert) { - return; + + /** + * Focus on selected component. + * + * @param {string} key - The key of selected component. + * @returns {*} + */ + focusOnComponent(key) { + if (key) { + const component = this.getComponent(key); + if (component) { + component.focus(); + } + } } - this.loadRefs(this.alert, { errorRef: 'multiple' }); + /** + * Show the errors of this form within the alert dialog. + * + * @param {Object} error - An optional additional error to display along with the component errors. + * @returns {*} + */ + /* eslint-disable no-unused-vars */ + showErrors(error, triggerEvent, onChange) { + this.loading = false; + let errors = this.errors; + if (error) { + if (Array.isArray(error)) { + errors = errors.concat(error); + } else { + errors.push(error); + } + } else { + errors = super.errors; + } - if (this.refs.errorRef && this.refs.errorRef.length) { - this.refs.errorRef.forEach(el => { - this.addEventListener(el, 'click', (e) => { - const key = e.currentTarget.dataset.componentKey; - this.focusOnComponent(key); - }); - this.addEventListener(el, 'keydown', (e) => { - if (e.keyCode === 13) { - e.preventDefault(); - const key = e.currentTarget.dataset.componentKey; - this.focusOnComponent(key); - } - }); - }); - } - this.prepend(this.alert); - } - - /** - * Focus on selected component. - * - * @param {string} key - The key of selected component. - * @returns {*} - */ - focusOnComponent(key) { - if (key) { - const component = this.getComponent(key); - if (component) { - component.focus(); - } - } - } - - /** - * Show the errors of this form within the alert dialog. - * - * @param {Object} error - An optional additional error to display along with the component errors. - * @returns {*} - */ - /* eslint-disable no-unused-vars */ - showErrors(error, triggerEvent, onChange) { - this.loading = false; - let errors = this.errors; - if (error) { - if (Array.isArray(error)) { - errors = errors.concat(error); - } - else { - errors.push(error); - } - } - else { - errors = super.errors; - } - - errors = errors.concat(this.customErrors); - - if (!errors.length) { - this.setAlert(false); - return; - } - - // Mark any components as invalid if in a custom message. - errors.forEach((err) => { - const { components = [] } = err; - - if (err.component) { - components.push(err.component); - } - - if (err.path) { - components.push(err.path); - } - - components.forEach((path) => { - const originalPath = this._parentPath + getStringFromComponentPath(path); - const component = this.getComponent(path, _.identity, originalPath); - - if (err.fromServer) { - if (component.serverErrors) { - component.serverErrors.push(err); - } - else { - component.serverErrors = [err]; - } - } - const components = _.compact(Array.isArray(component) ? component : [component]); - - components.forEach((component) => component.setCustomValidity(err.message, true)); - }); - }); + errors = errors.concat(this.customErrors); - const displayedErrors = []; + if (!errors.length) { + this.setAlert(false); + return; + } - errors.forEach(err => { - if (err) { - const createListItem = (message, index) => { - const messageFromIndex = !_.isUndefined(index) && err.messages && err.messages[index]; - const keyOrPath = (messageFromIndex && messageFromIndex.formattedKeyOrPath || messageFromIndex.path) || (err.component && err.component.key) || err.fromServer && err.path; + // Mark any components as invalid if in a custom message. + errors.forEach((err) => { + const { components = [] } = err; - let formattedKeyOrPath = keyOrPath ? getStringFromComponentPath(keyOrPath) : ''; - formattedKeyOrPath = this._parentPath + formattedKeyOrPath; - if (typeof err !== 'string' && !err.formattedKeyOrPath) { - err.formattedKeyOrPath = formattedKeyOrPath; - } + if (err.component) { + components.push(err.component); + } - return { - message: unescapeHTML(message), - keyOrPath: formattedKeyOrPath - }; - }; + if (err.path) { + components.push(err.path); + } - err.messages = _.uniqBy(err.messages, message => message.message); - - if (err.messages && err.messages.length) { - const { component } = err; - err.messages.forEach(({ message, context, fromServer }, index) => { - const text = context?.hasLabel || fromServer - ? this.t('alertMessage', { message: this.t(message) }) - : this.t('alertMessageWithLabel', { - label: this.t(component.label), - message: this.t(message), - }); - displayedErrors.push(createListItem(text, index)); - }); - } - else if (err) { - const message = _.isObject(err) - ? this.t('alertMessage', { message: this.t(err.message || '') }) - : this.t('alertMessage', { message: this.t(err) }); - displayedErrors.push(createListItem(message)); - } - } - }); + components.forEach((path) => { + const originalPath = + this._parentPath + getStringFromComponentPath(path); + const component = this.getComponent( + path, + _.identity, + originalPath, + ); + + if (err.fromServer) { + if (component.serverErrors) { + component.serverErrors.push(err); + } else { + component.serverErrors = [err]; + } + } + const components = _.compact( + Array.isArray(component) ? component : [component], + ); + + components.forEach((component) => + component.setCustomValidity(err.message, true), + ); + }); + }); - const errorsList = this.renderTemplate('errorsList', { errors: displayedErrors }); - this.root.setAlert('danger', errorsList); - - if (triggerEvent) { - this.emit('error', errors); - } - - return errors; - } - /* eslint-enable no-unused-vars */ - - /** - * Called when the submission has completed, or if the submission needs to be sent to an external library. - * - * @param {Object} submission - The submission object. - * @param {boolean} saved - Whether or not this submission was saved to the server. - * @returns {object} - The submission object. - */ - onSubmit(submission, saved) { - this.loading = false; - this.submitting = false; - this.setPristine(true); - // We want to return the submitted submission and setValue will mutate the submission so cloneDeep it here. - this.setValue(fastCloneDeep(submission), { - noValidate: true, - noCheck: true - }); - this.setAlert('success', `

${this.t('complete')}

`); - // Cancel triggered saveDraft to prevent overriding the submitted state - if (this.draftEnabled && this.triggerSaveDraft?.cancel) { - this.triggerSaveDraft.cancel(); + const displayedErrors = []; + + errors.forEach((err) => { + if (err) { + const createListItem = (message, index) => { + const messageFromIndex = + !_.isUndefined(index) && + err.messages && + err.messages[index]; + const keyOrPath = + (messageFromIndex && + messageFromIndex.formattedKeyOrPath) || + messageFromIndex.path || + (err.component && err.component.key) || + (err.fromServer && err.path); + + let formattedKeyOrPath = keyOrPath + ? getStringFromComponentPath(keyOrPath) + : ''; + formattedKeyOrPath = this._parentPath + formattedKeyOrPath; + if (typeof err !== 'string' && !err.formattedKeyOrPath) { + err.formattedKeyOrPath = formattedKeyOrPath; + } + + return { + message: unescapeHTML(message), + keyOrPath: formattedKeyOrPath, + }; + }; + + err.messages = _.uniqBy( + err.messages, + (message) => message.message, + ); + + if (err.messages && err.messages.length) { + const { component } = err; + err.messages.forEach( + ({ message, context, fromServer }, index) => { + const text = + context?.hasLabel || fromServer + ? this.t('alertMessage', { + message: this.t(message), + }) + : this.t('alertMessageWithLabel', { + label: this.t(component.label), + message: this.t(message), + }); + displayedErrors.push(createListItem(text, index)); + }, + ); + } else if (err) { + const message = _.isObject(err) + ? this.t('alertMessage', { + message: this.t(err.message || ''), + }) + : this.t('alertMessage', { message: this.t(err) }); + displayedErrors.push(createListItem(message)); + } + } + }); + + const errorsList = this.renderTemplate('errorsList', { + errors: displayedErrors, + }); + this.root.setAlert('danger', errorsList); + + if (triggerEvent) { + this.emit('error', errors); + } + + return errors; } - this.emit('submit', submission, saved); - if (saved) { - this.emit('submitDone', submission); + /* eslint-enable no-unused-vars */ + + /** + * Called when the submission has completed, or if the submission needs to be sent to an external library. + * + * @param {Object} submission - The submission object. + * @param {boolean} saved - Whether or not this submission was saved to the server. + * @returns {object} - The submission object. + */ + onSubmit(submission, saved) { + this.loading = false; + this.submitting = false; + this.setPristine(true); + // We want to return the submitted submission and setValue will mutate the submission so cloneDeep it here. + this.setValue(fastCloneDeep(submission), { + noValidate: true, + noCheck: true, + }); + this.setAlert('success', `

${this.t('complete')}

`); + // Cancel triggered saveDraft to prevent overriding the submitted state + if (this.draftEnabled && this.triggerSaveDraft?.cancel) { + this.triggerSaveDraft.cancel(); + } + this.emit('submit', submission, saved); + if (saved) { + this.emit('submitDone', submission); + } + return submission; } - return submission; - } - normalizeError(error) { - if (error) { - if (typeof error === 'object' && 'details' in error) { - error = error.details; - } + normalizeError(error) { + if (error) { + if (typeof error === 'object' && 'details' in error) { + error = error.details; + } + + if (typeof error === 'string') { + error = { message: error }; + } + } - if (typeof error === 'string') { - error = { message: error }; - } + return error; } - return error; - } - - /** - * Called when an error occurs during the submission. - * - * @param {Object} error - The error that occured. - */ - onSubmissionError(error) { - error = this.normalizeError(error); - - this.submitting = false; - this.setPristine(false); - this.emit('submitError', error || this.errors); - - // Allow for silent cancellations (no error message, no submit button error state) - if (error && error.silent) { - this.emit('change', { isValid: true }, { silent: true }); - return false; - } - - const errors = this.showErrors(error, true); - - if (this.root && this.root.alert) { - this.scrollIntoView(this.root.alert); - } - return errors; - } - - /** - * Trigger the change event for this form. - * - * @param changed - * @param flags - */ - onChange(flags, changed, modified, changes) { - flags = flags || {}; - let isChangeEventEmitted = false; - // For any change events, clear any custom errors for that component. - if (changed && changed.component) { - this.customErrors = this.customErrors.filter(err => err.component && err.component !== changed.component.key); - } - - super.onChange(flags, true); - const value = _.clone(this.submission); - flags.changed = value.changed = changed; - flags.changes = changes; - - if (modified && this.pristine) { - this.pristine = false; - } - - value.isValid = this.checkData(value.data, flags); - this.loading = false; - if (this.submitted) { - // show server errors while they are not cleaned/fixed - const nonComponentServerErrors = _.filter(this.serverErrors || [], err => !err.component && !err.path); - this.showErrors(nonComponentServerErrors.length ? nonComponentServerErrors : null); - } - - // See if we need to save the draft of the form. - if (modified && this.options.saveDraft) { - this.triggerSaveDraft(); - } - - if (!flags || !flags.noEmit) { - this.emit('change', value, flags, modified); - isChangeEventEmitted = true; - } - - // The form is initialized after the first change event occurs. - if (isChangeEventEmitted && !this.initialized) { - this.emit('initialized'); - this.initialized = true; - } - } - - checkData(data, flags = {}) { - const valid = super.checkData(data, flags); - if ((_.isEmpty(flags) || flags.noValidate) && this.submitted) { - this.showErrors(); - } - return valid; - } - - /** - * Send a delete request to the server. - */ - deleteSubmission() { - return this.formio.deleteSubmission() - .then(() => { - this.emit('submissionDeleted', this.submission); - this.resetValue(); - }); - } - - /** - * Cancels the submission. - * - * @alias reset - */ - cancel(noconfirm) { - const shouldReset = this.hook('beforeCancel', true); - if (shouldReset && (noconfirm || confirm(this.t('confirmCancel')))) { - this.resetValue(); - return true; - } - else { - this.emit('cancelSubmit'); - return false; - } - } - - setMetadata(submission) { - // Add in metadata about client submitting the form - submission.metadata = submission.metadata || {}; - _.defaults(submission.metadata, { - timezone: _.get(this, '_submission.metadata.timezone', currentTimezone()), - offset: parseInt(_.get(this, '_submission.metadata.offset', moment().utcOffset()), 10), - origin: document.location.origin, - referrer: document.referrer, - browserName: navigator.appName, - userAgent: navigator.userAgent, - pathName: window.location.pathname, - onLine: navigator.onLine - }); - } + /** + * Called when an error occurs during the submission. + * + * @param {Object} error - The error that occured. + */ + onSubmissionError(error) { + error = this.normalizeError(error); - submitForm(options = {}) { - this.clearServerErrors(); + this.submitting = false; + this.setPristine(false); + this.emit('submitError', error || this.errors); - return new Promise((resolve, reject) => { - // Read-only forms should never submit. - if (this.options.readOnly) { - return resolve({ - submission: this.submission, - saved: false - }); - } + // Allow for silent cancellations (no error message, no submit button error state) + if (error && error.silent) { + this.emit('change', { isValid: true }, { silent: true }); + return false; + } + + const errors = this.showErrors(error, true); - const submission = fastCloneDeep(this.submission || {}); + if (this.root && this.root.alert) { + this.scrollIntoView(this.root.alert); + } + return errors; + } + + /** + * Trigger the change event for this form. + * + * @param changed + * @param flags + */ + onChange(flags, changed, modified, changes) { + flags = flags || {}; + let isChangeEventEmitted = false; + // For any change events, clear any custom errors for that component. + if (changed && changed.component) { + this.customErrors = this.customErrors.filter( + (err) => + err.component && err.component !== changed.component.key, + ); + } - this.setMetadata(submission); + super.onChange(flags, true); + const value = _.clone(this.submission); + flags.changed = value.changed = changed; + flags.changes = changes; - submission.state = options.state || 'submitted'; + if (modified && this.pristine) { + this.pristine = false; + } - const isDraft = (submission.state === 'draft'); - this.hook('beforeSubmit', { ...submission, component: options.component }, (err , data) => { - if (err) { - return reject(err); + value.isValid = this.checkData(value.data, flags); + this.loading = false; + if (this.submitted) { + // show server errors while they are not cleaned/fixed + const nonComponentServerErrors = _.filter( + this.serverErrors || [], + (err) => !err.component && !err.path, + ); + this.showErrors( + nonComponentServerErrors.length + ? nonComponentServerErrors + : null, + ); } - submission._vnote = data && data._vnote ? data._vnote : ''; + // See if we need to save the draft of the form. + if (modified && this.options.saveDraft) { + this.triggerSaveDraft(); + } - if (!isDraft && !submission.data) { - return reject('Invalid Submission'); + if (!flags || !flags.noEmit) { + this.emit('change', value, flags, modified); + isChangeEventEmitted = true; } - if (!isDraft && !this.checkValidity(submission.data, true)) { - return reject(); + // The form is initialized after the first change event occurs. + if (isChangeEventEmitted && !this.initialized) { + this.emit('initialized'); + this.initialized = true; } + } - this.everyComponent((comp) => { - if (submission._vnote && comp.type === 'form' && comp.component.reference) { - _.get(submission.data, comp.path, {})._vnote = submission._vnote; - } - const { persistent } = comp.component; - if (persistent === 'client-only') { - _.unset(submission.data, comp.path); - } + checkData(data, flags = {}) { + const valid = super.checkData(data, flags); + if ((_.isEmpty(flags) || flags.noValidate) && this.submitted) { + this.showErrors(); + } + return valid; + } + + /** + * Send a delete request to the server. + */ + deleteSubmission() { + return this.formio.deleteSubmission().then(() => { + this.emit('submissionDeleted', this.submission); + this.resetValue(); }); + } - this.hook('customValidation', { ...submission, component: options.component }, (err) => { - if (err) { - // If string is returned, cast to object. - if (typeof err === 'string') { - err = { - message: err - }; + /** + * Cancels the submission. + * + * @alias reset + */ + cancel(noconfirm) { + const shouldReset = this.hook('beforeCancel', true); + if (shouldReset && (noconfirm || confirm(this.t('confirmCancel')))) { + this.resetValue(); + return true; + } else { + this.emit('cancelSubmit'); + return false; + } + } + + setMetadata(submission) { + // Add in metadata about client submitting the form + submission.metadata = submission.metadata || {}; + _.defaults(submission.metadata, { + timezone: _.get( + this, + '_submission.metadata.timezone', + currentTimezone(), + ), + offset: parseInt( + _.get( + this, + '_submission.metadata.offset', + moment().utcOffset(), + ), + 10, + ), + origin: document.location.origin, + referrer: document.referrer, + browserName: navigator.appName, + userAgent: navigator.userAgent, + pathName: window.location.pathname, + onLine: navigator.onLine, + }); + } + + submitForm(options = {}) { + this.clearServerErrors(); + + return new Promise((resolve, reject) => { + // Read-only forms should never submit. + if (this.options.readOnly) { + return resolve({ + submission: this.submission, + saved: false, + }); } - // Ensure err is an array. - err = Array.isArray(err) ? err : [err]; - - // Set as custom errors. - this.customErrors = err; - - return reject(); - } - - this.loading = true; - - // Use the form action to submit the form if available. - if (this._form && this._form.action) { - const method = (submission.data._id && this._form.action.includes(submission.data._id)) ? 'PUT' : 'POST'; - return Formio.makeStaticRequest(this._form.action, method, submission, this.formio ? this.formio.options : {}) - .then((result) => resolve({ - submission: result, - saved: true, - })) - .catch((error) => { - this.setServerErrors(error); - - return reject(error); - }); - } - - const submitFormio = this.formio; - if (this.nosubmit || !submitFormio) { - return resolve({ - submission, - saved: false, - }); - } - // If this is an actionUrl, then make sure to save the action and not the submission. - const submitMethod = submitFormio.actionUrl ? 'saveAction' : 'saveSubmission'; - submitFormio[submitMethod](submission) - .then((result) => resolve({ - submission: result, - saved: true, - })) - .catch((error) => { - this.setServerErrors(error); - - return reject(error); - }); + const submission = fastCloneDeep(this.submission || {}); + + this.setMetadata(submission); + + submission.state = options.state || 'submitted'; + + const isDraft = submission.state === 'draft'; + this.hook( + 'beforeSubmit', + { ...submission, component: options.component }, + (err, data) => { + if (err) { + return reject(err); + } + + submission._vnote = data && data._vnote ? data._vnote : ''; + + if (!isDraft && !submission.data) { + return reject('Invalid Submission'); + } + + if ( + !isDraft && + !this.checkValidity(submission.data, true) + ) { + return reject(); + } + + this.everyComponent((comp) => { + if ( + submission._vnote && + comp.type === 'form' && + comp.component.reference + ) { + _.get(submission.data, comp.path, {})._vnote = + submission._vnote; + } + const { persistent } = comp.component; + if (persistent === 'client-only') { + _.unset(submission.data, comp.path); + } + }); + + this.hook( + 'customValidation', + { ...submission, component: options.component }, + (err) => { + if (err) { + // If string is returned, cast to object. + if (typeof err === 'string') { + err = { + message: err, + }; + } + + // Ensure err is an array. + err = Array.isArray(err) ? err : [err]; + + // Set as custom errors. + this.customErrors = err; + + return reject(); + } + + this.loading = true; + + // Use the form action to submit the form if available. + if (this._form && this._form.action) { + const method = + submission.data._id && + this._form.action.includes( + submission.data._id, + ) + ? 'PUT' + : 'POST'; + return Formio.makeStaticRequest( + this._form.action, + method, + submission, + this.formio ? this.formio.options : {}, + ) + .then((result) => + resolve({ + submission: result, + saved: true, + }), + ) + .catch((error) => { + this.setServerErrors(error); + + return reject(error); + }); + } + + const submitFormio = this.formio; + if (this.nosubmit || !submitFormio) { + return resolve({ + submission, + saved: false, + }); + } + // If this is an actionUrl, then make sure to save the action and not the submission. + const submitMethod = submitFormio.actionUrl + ? 'saveAction' + : 'saveSubmission'; + submitFormio[submitMethod](submission) + .then((result) => + resolve({ + submission: result, + saved: true, + }), + ) + .catch((error) => { + this.setServerErrors(error); + + return reject(error); + }); + }, + ); + }, + ); }); - }); - }); - } - - setServerErrors(error) { - if (error.details) { - this.serverErrors = error.details.filter((err) => err.level ? err.level === 'error' : err).map((err) => { - err.fromServer = true; - return err; - }); - } - else if (typeof error === 'string') { - this.serverErrors = [{ fromServer: true, level: 'error', message: error }]; - } - } - - executeSubmit(options) { - this.submitted = true; - this.submitting = true; - return this.submitForm(options) - .then(({ submission, saved }) => this.onSubmit(submission, saved)) - .then((results) => { - this.submissionInProcess = false; - return results; - }) - .catch((err) => { - this.submissionInProcess = false; - return Promise.reject(this.onSubmissionError(err)); - }); - } - - clearServerErrors() { - this.serverErrors?.forEach((error) => { - if (error.path) { - const pathArray = getArrayFromComponentPath(error.path); - const component = this.getComponent(pathArray, _.identity, error.formattedKeyOrPath); - - if (component) { - component.serverErrors = []; - } - } - }); - this.serverErrors = []; - } - - /** - * Submits the form. - * - * @example - * import Webform from '@formio/js/Webform'; - * let form = new Webform(document.getElementById('formio')); - * form.src = 'https://examples.form.io/example'; - * form.submission = {data: { - * firstName: 'Joe', - * lastName: 'Smith', - * email: 'joe@example.com' - * }}; - * form.submit().then((submission) => { - * console.log(submission); - * }); - * - * @param {boolean} before - If this submission occured from the before handlers. - * - * @returns {Promise} - A promise when the form is done submitting. - */ - submit(before, options) { - this.submissionInProcess = true; - if (!before) { - return this.beforeSubmit(options).then(() => this.executeSubmit(options)); - } - else { - return this.executeSubmit(options); - } - } - - submitUrl(URL, headers) { - if (!URL) { - return console.warn('Missing URL argument'); - } - - const submission = this.submission || {}; - const API_URL = URL; - const settings = { - method: 'POST', - headers: {} - }; + } + + setServerErrors(error) { + if (error.details) { + this.serverErrors = error.details + .filter((err) => (err.level ? err.level === 'error' : err)) + .map((err) => { + err.fromServer = true; + return err; + }); + } else if (typeof error === 'string') { + this.serverErrors = [ + { fromServer: true, level: 'error', message: error }, + ]; + } + } - if (headers && headers.length > 0) { - headers.map((e) => { - if (e.header !== '' && e.value !== '') { - settings.headers[e.header] = this.interpolate(e.value, submission); - } - }); - } - if (API_URL && settings) { - Formio.makeStaticRequest(API_URL,settings.method,submission, { headers: settings.headers }).then(() => { - this.emit('requestDone'); - this.setAlert('success', '

Success

'); - }).catch((e) => { - const message = `${e.statusText ? e.statusText : ''} ${e.status ? e.status : e}`; - this.emit('error', message); - console.error(message); - this.setAlert('danger', `

${message}

`); - return Promise.reject(this.onSubmissionError(e)); + executeSubmit(options) { + this.submitted = true; + this.submitting = true; + return this.submitForm(options) + .then(({ submission, saved }) => this.onSubmit(submission, saved)) + .then((results) => { + this.submissionInProcess = false; + return results; + }) + .catch((err) => { + this.submissionInProcess = false; + return Promise.reject(this.onSubmissionError(err)); + }); + } + + clearServerErrors() { + this.serverErrors?.forEach((error) => { + if (error.path) { + const pathArray = getArrayFromComponentPath(error.path); + const component = this.getComponent( + pathArray, + _.identity, + error.formattedKeyOrPath, + ); + + if (component) { + component.serverErrors = []; + } + } }); + this.serverErrors = []; } - else { - this.emit('error', 'You should add a URL to this button.'); - this.setAlert('warning', 'You should add a URL to this button.'); - return console.warn('You should add a URL to this button.'); + + /** + * Submits the form. + * + * @example + * import Webform from '@formio/js/Webform'; + * let form = new Webform(document.getElementById('formio')); + * form.src = 'https://examples.form.io/example'; + * form.submission = {data: { + * firstName: 'Joe', + * lastName: 'Smith', + * email: 'joe@example.com' + * }}; + * form.submit().then((submission) => { + * console.log(submission); + * }); + * + * @param {boolean} before - If this submission occured from the before handlers. + * + * @returns {Promise} - A promise when the form is done submitting. + */ + submit(before, options) { + this.submissionInProcess = true; + if (!before) { + return this.beforeSubmit(options).then(() => + this.executeSubmit(options), + ); + } else { + return this.executeSubmit(options); + } } - } - triggerRecaptcha() { - if (!this || !this.components) { - return; + submitUrl(URL, headers) { + if (!URL) { + return console.warn('Missing URL argument'); + } + + const submission = this.submission || {}; + const API_URL = URL; + const settings = { + method: 'POST', + headers: {}, + }; + + if (headers && headers.length > 0) { + headers.map((e) => { + if (e.header !== '' && e.value !== '') { + settings.headers[e.header] = this.interpolate( + e.value, + submission, + ); + } + }); + } + if (API_URL && settings) { + Formio.makeStaticRequest(API_URL, settings.method, submission, { + headers: settings.headers, + }) + .then(() => { + this.emit('requestDone'); + this.setAlert('success', '

Success

'); + }) + .catch((e) => { + const message = `${e.statusText ? e.statusText : ''} ${ + e.status ? e.status : e + }`; + this.emit('error', message); + console.error(message); + this.setAlert('danger', `

${message}

`); + return Promise.reject(this.onSubmissionError(e)); + }); + } else { + this.emit('error', 'You should add a URL to this button.'); + this.setAlert('warning', 'You should add a URL to this button.'); + return console.warn('You should add a URL to this button.'); + } } - const recaptchaComponent = searchComponents(this.components, { - 'component.type': 'recaptcha', - 'component.eventType': 'formLoad' - }); - if (recaptchaComponent.length > 0) { - recaptchaComponent[0].verify(`${this.form.name ? this.form.name : 'form'}Load`); + + triggerRecaptcha() { + if (!this || !this.components) { + return; + } + const recaptchaComponent = searchComponents(this.components, { + 'component.type': 'recaptcha', + 'component.eventType': 'formLoad', + }); + if (recaptchaComponent.length > 0) { + recaptchaComponent[0].verify( + `${this.form.name ? this.form.name : 'form'}Load`, + ); + } } - } - set nosubmit(value) { - this._nosubmit = !!value; - this.emit('nosubmit', this._nosubmit); - } + set nosubmit(value) { + this._nosubmit = !!value; + this.emit('nosubmit', this._nosubmit); + } - get nosubmit() { - return this._nosubmit || false; - } + get nosubmit() { + return this._nosubmit || false; + } - get conditions() { - return this.schema.settings?.conditions ?? []; - } + get conditions() { + return this.schema.settings?.conditions ?? []; + } - get variables() { - return this.schema.settings?.variables ?? []; - } + get variables() { + return this.schema.settings?.variables ?? []; + } } Webform.setBaseUrl = Formio.setBaseUrl; diff --git a/src/Webform.unit.js b/src/Webform.unit.js index 4ba54fbafc..7a465e4d1f 100644 --- a/src/Webform.unit.js +++ b/src/Webform.unit.js @@ -9,33 +9,33 @@ import 'flatpickr'; import AllComponents from './components'; import { Formio } from './Formio'; import { - settingErrors, - clearOnHide, - manualOverride, - validationOnBlur, - calculateValueWithManualOverride, - calculateValueWithSubmissionMetadata, - formWithAdvancedLogic, - formWithPatternValidation, - calculatedSelectboxes, - calculateZeroValue, - formWithConditionalLogic, - formWithCalculatedValueWithoutOverriding, - formWithTimeComponent, - formWithEditGridModalDrafts, - formWithBlurValidationInsidePanel, - modalEditComponents, - calculatedNotPersistentValue, - calculateValueInEditingMode, - initiallyCollapsedPanel, - multipleTextareaInsideConditionalComponent, - disabledNestedForm, - formWithEditGridAndNestedDraftModalRow, - formWithDateTimeComponents, - formWithCollapsedPanel, - formWithCustomFormatDate, - tooltipActivateCheckbox, - formWithObjectValueSelect + settingErrors, + clearOnHide, + manualOverride, + validationOnBlur, + calculateValueWithManualOverride, + calculateValueWithSubmissionMetadata, + formWithAdvancedLogic, + formWithPatternValidation, + calculatedSelectboxes, + calculateZeroValue, + formWithConditionalLogic, + formWithCalculatedValueWithoutOverriding, + formWithTimeComponent, + formWithEditGridModalDrafts, + formWithBlurValidationInsidePanel, + modalEditComponents, + calculatedNotPersistentValue, + calculateValueInEditingMode, + initiallyCollapsedPanel, + multipleTextareaInsideConditionalComponent, + disabledNestedForm, + formWithEditGridAndNestedDraftModalRow, + formWithDateTimeComponents, + formWithCollapsedPanel, + formWithCustomFormatDate, + tooltipActivateCheckbox, + formWithObjectValueSelect, } from '../test/formtest'; import UpdateErrorClassesWidgets from '../test/forms/updateErrorClasses-widgets'; import nestedModalWizard from '../test/forms/nestedModalWizard'; @@ -53,8 +53,7 @@ import checkBlurFocusEventForm from '../test/forms/checkBlurFocusEventForm'; import truncateMultipleSpaces from '../test/forms/truncateMultipleSpaces'; import calculatedValue from '../test/forms/calculatedValue'; import conditionalDataGridWithTableAndRadio from '../test/forms/conditionalDataGridWithTableAndRadio'; -import calculateValueWithManualOverrideLableValueDataGrid - from '../test/forms/calculateValueWithManualOverrideLableValueDataGrid'; +import calculateValueWithManualOverrideLableValueDataGrid from '../test/forms/calculateValueWithManualOverrideLableValueDataGrid'; import deeplyNestedDataGridAndContainer from '../test/forms/nestedDataGridsAndContainers'; import columnWithConditionalComponents from '../test/forms/columnWithConditionalComponents'; import formWithSurvey from '../test/forms/formWithSurvey'; @@ -83,4772 +82,6628 @@ global.requestAnimationFrame = (cb) => cb(); global.cancelAnimationFrame = () => {}; if (_.has(Formio, 'Components.setComponents')) { - Formio.Components.setComponents(AllComponents); + Formio.Components.setComponents(AllComponents); } /* eslint-disable max-statements */ -describe('Webform tests', function() { - this.retries(3); - - it('Should validate hidden and conditionally hidden components when validateWhenHidden is enabled for those components', function(done) { - const formElement = document.createElement('div'); - - Formio.createForm(formElement, formWithValidateWhenHidden) - .then(form => { - const errorClasses = ['has-error', 'has-message', form.options.componentErrorClass]; - const number1 = form.getComponent('number1'); - const number2 = form.getComponent('number2'); - const number = form.getComponent('number'); - const textField = form.getComponent('textField'); - const textArea = form.getComponent('textArea'); - const checkbox = form.getComponent('checkbox'); - - assert.equal(form.errors.length, 0); - - number1.setValue(5); - number2.setValue(7); - setTimeout(()=> { - assert.equal(form.errors.length, 1); - assert.equal(!!number.error, true); - - errorClasses.forEach(cl => assert.equal(number.element.classList.contains(cl), false, '(1) Should not set error classes for hidden components.')); - number2.setValue(3); - - setTimeout(() => { - assert.equal(form.errors.length, 0); - assert.equal(!!number.error, false); - errorClasses.forEach(cl => assert.equal(number.element.classList.contains(cl), false, '(2) Should not set error classes for hidden components.')); - - textField.setValue('test'); - setTimeout(() => { - assert.equal(form.errors.length, 1); - assert.equal(!!textArea.error, true); - assert.equal(textArea.visible, true); - - checkbox.setValue(true); - setTimeout(()=> { - assert.equal(textArea.visible, false); - assert.equal(form.errors.length, 1); - assert.equal(!!textArea.error, true); - errorClasses.forEach(cl => assert.equal(textArea.element.classList.contains(cl), false)); - - number2.setValue(9); - form.submit(); - setTimeout(()=> { - assert.equal(form.errors.length, 2); - assert.equal(!!textArea.error, true); - assert.equal(!!number.error, true); - assert.equal(!!form.alert, true); - assert.equal(form.refs.errorRef.length, 2); - errorClasses.forEach(cl => assert.equal(number.element.classList.contains(cl), false)); - errorClasses.forEach(cl => assert.equal(textArea.element.classList.contains(cl), false)); - - textField.setValue('test test test'); - number2.setValue(1); - setTimeout(()=> { - assert.equal(form.errors.length, 0); - assert.equal(!!textArea.error, false); - assert.equal(!!number.error, false); - assert.equal(!!form.alert, false); +describe('Webform tests', function () { + this.retries(3); + + it('Should validate hidden and conditionally hidden components when validateWhenHidden is enabled for those components', function (done) { + const formElement = document.createElement('div'); + + Formio.createForm(formElement, formWithValidateWhenHidden) + .then((form) => { + const errorClasses = [ + 'has-error', + 'has-message', + form.options.componentErrorClass, + ]; + const number1 = form.getComponent('number1'); + const number2 = form.getComponent('number2'); + const number = form.getComponent('number'); + const textField = form.getComponent('textField'); + const textArea = form.getComponent('textArea'); + const checkbox = form.getComponent('checkbox'); + + assert.equal(form.errors.length, 0); + + number1.setValue(5); + number2.setValue(7); + setTimeout(() => { + assert.equal(form.errors.length, 1); + assert.equal(!!number.error, true); + + errorClasses.forEach((cl) => + assert.equal( + number.element.classList.contains(cl), + false, + '(1) Should not set error classes for hidden components.', + ), + ); + number2.setValue(3); - done(); - }, 300); + setTimeout(() => { + assert.equal(form.errors.length, 0); + assert.equal(!!number.error, false); + errorClasses.forEach((cl) => + assert.equal( + number.element.classList.contains(cl), + false, + '(2) Should not set error classes for hidden components.', + ), + ); + + textField.setValue('test'); + setTimeout(() => { + assert.equal(form.errors.length, 1); + assert.equal(!!textArea.error, true); + assert.equal(textArea.visible, true); + + checkbox.setValue(true); + setTimeout(() => { + assert.equal(textArea.visible, false); + assert.equal(form.errors.length, 1); + assert.equal(!!textArea.error, true); + errorClasses.forEach((cl) => + assert.equal( + textArea.element.classList.contains(cl), + false, + ), + ); + + number2.setValue(9); + form.submit(); + setTimeout(() => { + assert.equal(form.errors.length, 2); + assert.equal(!!textArea.error, true); + assert.equal(!!number.error, true); + assert.equal(!!form.alert, true); + assert.equal(form.refs.errorRef.length, 2); + errorClasses.forEach((cl) => + assert.equal( + number.element.classList.contains( + cl, + ), + false, + ), + ); + errorClasses.forEach((cl) => + assert.equal( + textArea.element.classList.contains( + cl, + ), + false, + ), + ); + + textField.setValue('test test test'); + number2.setValue(1); + setTimeout(() => { + assert.equal(form.errors.length, 0); + assert.equal(!!textArea.error, false); + assert.equal(!!number.error, false); + assert.equal(!!form.alert, false); + + done(); + }, 300); + }, 300); + }, 300); + }, 300); + }, 300); }, 300); - }, 300); - }, 300); - }, 300); - }, 300); - }) - .catch(done); - }); - - it('Should not validate hidden and conditionally hidden components when validateWhenHidden is not enabled for those components', function(done) { - const formElement = document.createElement('div'); - const testForm = fastCloneDeep(formWithValidateWhenHidden); - - _.each(testForm.components, comp => { - comp.validateWhenHidden = false; + }) + .catch(done); }); - Formio.createForm(formElement, testForm) - .then(form => { - const number1 = form.getComponent('number1'); - const number2 = form.getComponent('number2'); - const number = form.getComponent('number'); - const textField = form.getComponent('textField'); - const textArea = form.getComponent('textArea'); - const checkbox = form.getComponent('checkbox'); - - assert.equal(form.errors.length, 0); - - number1.setValue(5); - number2.setValue(7); - setTimeout(()=> { - assert.equal(form.errors.length, 0); - assert.equal(!!number.error, false); - - textField.setValue('test'); - setTimeout(() => { - assert.equal(form.errors.length, 1); - assert.equal(!!textArea.error, true); - assert.equal(textArea.visible, true); - - checkbox.setValue(true); - setTimeout(()=> { - assert.equal(textArea.visible, false); - assert.equal(form.errors.length, 0); - assert.equal(!!textArea.error, false); - done(); - }, 300); - }, 300); - }, 300); - }) - .catch(done); - }); - - it('Should not lose values of conditionally visible components on setValue when server option is passed', function(done) { - const formElement = document.createElement('div'); - Formio.createForm(formElement, formWithDeeplyNestedConditionalComps, { server: true }).then((form) => { - const submission = { - data: { - submit: false, - radio1: 'yes', - container: { - checkbox: true, - checkboxInPanelInHiddenContainer: true, - textField: 'test', - editGrid: [ - { - number: 1, - textField: 'test2', - }, - { - number: 2, - }, - ], - }, - }, - }; - - form.setValue(fastCloneDeep(submission), { sanitize: true }); - setTimeout(() => { - assert.deepEqual(form.data, submission.data); - assert.deepEqual(form.getValue(), submission); - done(); - }, 500); - }).catch((err) => done(err)); - }); - - it('Should not lose values of conditionally visible components on setValue when server option is not passed', function(done) { - const formElement = document.createElement('div'); - Formio.createForm(formElement, formWithDeeplyNestedConditionalComps).then((form) => { - const submission = { - data: { - submit: false, - radio1: 'yes', - container: { - checkbox: true, - checkboxInPanelInHiddenContainer: true, - textField: 'test', - editGrid: [ - { - number: 1, - textField: 'test2', - }, - { - number: 2, - }, - ], - }, - }, - }; - form.setValue(fastCloneDeep(submission), { sanitize: true }); - - setTimeout(() => { - assert.deepEqual(form.data, submission.data); - assert.deepEqual(form.getValue(), submission); - done(); - }, 500); - }).catch((err) => done(err)); - }); - - it('Should fire error and submitError events with args on attempt to submit invalid form', function(done) { - const formElement = document.createElement('div'); - const form = new Webform(formElement); - - form.setForm(formWithValidation).then(() => { - let errorEvents = 0; - let submitErrorEvents = 0; - form.on('error', (arg) => { - assert.equal(!!arg, true, 'Error event should have argument'); - errorEvents = errorEvents + 1; - }); - - form.on('submitError', (arg) => { - assert.equal(!!arg, true, 'submitError event should have argument'); - submitErrorEvents = submitErrorEvents + 1; - }); - - const clickEvent = new Event('click'); - const submitBtn = form.element.querySelector('[name="data[submit]"]'); - submitBtn.dispatchEvent(clickEvent); - - setTimeout(() => { - assert.equal(form.errors.length, 1); - assert.equal(errorEvents, 1); - assert.equal(submitErrorEvents, 1); - done(); - }, 300); - }).catch((err) => done(err)); - }); - - it('Should keep non-component server errors visible after changes in the form', function(done) { - const element = document.createElement('div'); - const form = fastCloneDeep(formWithValidation); - form.components[0].validate = {}; - - const originalMakeRequest = Formio.makeRequest; - const errorText = 'Server error'; - Formio.makeRequest = function() { - return new Promise((res, rej) => { - setTimeout(() => { - rej(errorText); - }, 50); - }); - }; + it('Should not validate hidden and conditionally hidden components when validateWhenHidden is not enabled for those components', function (done) { + const formElement = document.createElement('div'); + const testForm = fastCloneDeep(formWithValidateWhenHidden); - Formio.createForm(element, form) - .then(instance => { - instance.formio = new Formio('http://localhost:3000/test'); - assert.equal(instance.errors.length, 0); - assert.equal(!!(instance.serverErrors && instance.serverErrors.length), false); - assert.equal(!!(instance.refs.errorRef && instance.refs.errorRef.length), false); - - const clickEvent = new Event('click'); - const submitBtn = instance.element.querySelector('[name="data[submit]"]'); - submitBtn.dispatchEvent(clickEvent); - - setTimeout(() => { - assert.equal(instance.errors.length, 0); - assert.equal(instance.serverErrors.length, 1); - assert.equal(instance.refs.errorRef.length, 1); - assert.equal(instance.refs.errorRef[0].textContent.trim(), errorText); - - const inputEvent = new Event('input'); - const textField = instance.element.querySelector('input[name="data[name]"]'); - textField.value = 'test'; - textField.dispatchEvent(inputEvent); - - setTimeout(() => { - assert.equal(instance.errors.length, 0); - assert.equal(instance.serverErrors.length, 1); - assert.equal(instance.refs.errorRef.length, 1); - assert.equal(instance.refs.errorRef[0].textContent.trim(), errorText); - Formio.makeRequest = originalMakeRequest; - done(); - }, 400); - }, 400); - }) - .catch(done); - }); - - it('Should execute form controller', function(done) { - Formio.createForm(formWithFormController).then((form) => { - setTimeout(() => { - const textField = form.getComponent('textField'); - - assert.equal(textField.getValue(), 'Hello World'); - assert.equal(textField.disabled, true); - assert.equal(form.components[0].disabled, true); - - done(); - }, 300); - }).catch((err) => done(err)); - }); - - it('Should set radio components value inside data grid correctly', function(done) { - Formio.createForm(formWithRadioInsideDataGrid).then((form) => { - const dataGridData = [{ radio: 'two' },{ radio: 'two' } ,{ radio: 'three' }]; - form.setValue({ data: { dataGrid: fastCloneDeep(dataGridData) } }); - setTimeout(() => { - const dataGrid = form.getComponent('dataGrid'); - assert.deepEqual(dataGrid.dataValue, dataGridData); - done(); - }, 200); - }).catch((err) => done(err)); - }); - - it('Should not fall into setValue calls loop when doing value calculation on server', function(done) { - const formElement = document.createElement('div'); - // Set a spy for Edit Grid setValue method - const spy = sinon.spy(Formio.Components.components.editgrid.prototype, 'setValue'); - - Formio.createForm(formElement, calculateValueOnServerForEditGrid, { server: true, noDefaults: true } ) - .then(form => { - assert.deepEqual(form.data, { editGrid: [{ fielda: undefined, fieldb: 'test' }] }); - assert.equal(spy.callCount, 1); - - const first = form.getComponent('first'); - - first.setValue('test value'); - - setTimeout(() => { - assert.deepEqual(form.data, { - first: 'test value', - editGrid: [{ fielda: 'test value', fieldb: 'test' }] - }); - assert.equal(spy.callCount, 2); - // Remove the spy from setValue method - Formio.Components.components.editgrid.prototype.setValue.restore(); - done(); - }, 300); - }) - .catch(done); - }); - - it('Should fire blur and focus events for address and select components', function(done) { - const formElement = document.createElement('div'); - const form = new Webform(formElement); + _.each(testForm.components, (comp) => { + comp.validateWhenHidden = false; + }); - form.setForm(checkBlurFocusEventForm).then(() => { - let blurEvents = 0; - let focusEvents = 0; - form.on('blur', () => { - blurEvents = blurEvents + 1; - }); + Formio.createForm(formElement, testForm) + .then((form) => { + const number1 = form.getComponent('number1'); + const number2 = form.getComponent('number2'); + const number = form.getComponent('number'); + const textField = form.getComponent('textField'); + const textArea = form.getComponent('textArea'); + const checkbox = form.getComponent('checkbox'); - form.on('focus', () => { - focusEvents = focusEvents + 1; - }); + assert.equal(form.errors.length, 0); - const focusEvent = new Event('focus'); - const blurEvent = new Event('blur'); + number1.setValue(5); + number2.setValue(7); + setTimeout(() => { + assert.equal(form.errors.length, 0); + assert.equal(!!number.error, false); + + textField.setValue('test'); + setTimeout(() => { + assert.equal(form.errors.length, 1); + assert.equal(!!textArea.error, true); + assert.equal(textArea.visible, true); + + checkbox.setValue(true); + setTimeout(() => { + assert.equal(textArea.visible, false); + assert.equal(form.errors.length, 0); + assert.equal(!!textArea.error, false); + done(); + }, 300); + }, 300); + }, 300); + }) + .catch(done); + }); - const selectChoices = form.getComponent('selectChoices'); - selectChoices.focusableElement.dispatchEvent(focusEvent); + it('Should not lose values of conditionally visible components on setValue when server option is passed', function (done) { + const formElement = document.createElement('div'); + Formio.createForm(formElement, formWithDeeplyNestedConditionalComps, { + server: true, + }) + .then((form) => { + const submission = { + data: { + submit: false, + radio1: 'yes', + container: { + checkbox: true, + checkboxInPanelInHiddenContainer: true, + textField: 'test', + editGrid: [ + { + number: 1, + textField: 'test2', + }, + { + number: 2, + }, + ], + }, + }, + }; + + form.setValue(fastCloneDeep(submission), { sanitize: true }); + setTimeout(() => { + assert.deepEqual(form.data, submission.data); + assert.deepEqual(form.getValue(), submission); + done(); + }, 500); + }) + .catch((err) => done(err)); + }); - setTimeout(() => { - selectChoices.focusableElement.dispatchEvent(blurEvent); + it('Should not lose values of conditionally visible components on setValue when server option is not passed', function (done) { + const formElement = document.createElement('div'); + Formio.createForm(formElement, formWithDeeplyNestedConditionalComps) + .then((form) => { + const submission = { + data: { + submit: false, + radio1: 'yes', + container: { + checkbox: true, + checkboxInPanelInHiddenContainer: true, + textField: 'test', + editGrid: [ + { + number: 1, + textField: 'test2', + }, + { + number: 2, + }, + ], + }, + }, + }; + form.setValue(fastCloneDeep(submission), { sanitize: true }); - const selectHtml = form.getComponent('selectHtml'); - selectHtml.refs.selectContainer.dispatchEvent(focusEvent); + setTimeout(() => { + assert.deepEqual(form.data, submission.data); + assert.deepEqual(form.getValue(), submission); + done(); + }, 500); + }) + .catch((err) => done(err)); + }); - setTimeout(() => { - selectHtml.refs.selectContainer.dispatchEvent(blurEvent); + it('Should fire error and submitError events with args on attempt to submit invalid form', function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement); + + form.setForm(formWithValidation) + .then(() => { + let errorEvents = 0; + let submitErrorEvents = 0; + form.on('error', (arg) => { + assert.equal( + !!arg, + true, + 'Error event should have argument', + ); + errorEvents = errorEvents + 1; + }); + + form.on('submitError', (arg) => { + assert.equal( + !!arg, + true, + 'submitError event should have argument', + ); + submitErrorEvents = submitErrorEvents + 1; + }); + + const clickEvent = new Event('click'); + const submitBtn = form.element.querySelector( + '[name="data[submit]"]', + ); + submitBtn.dispatchEvent(clickEvent); - const address = form.getComponent('address'); - address.refs.searchInput[0].dispatchEvent(focusEvent); + setTimeout(() => { + assert.equal(form.errors.length, 1); + assert.equal(errorEvents, 1); + assert.equal(submitErrorEvents, 1); + done(); + }, 300); + }) + .catch((err) => done(err)); + }); - setTimeout(() => { - address.refs.searchInput[0].dispatchEvent(blurEvent); + it('Should keep non-component server errors visible after changes in the form', function (done) { + const element = document.createElement('div'); + const form = fastCloneDeep(formWithValidation); + form.components[0].validate = {}; - setTimeout(() => { - assert.equal(focusEvents, 3); - assert.equal(blurEvents, 3); - done(); - }, 300); - }, 300); - }, 300); - }, 300); - }).catch((err) => done(err)); - }); - - it('Should return correct string value for checkbox radio type', function(done) { - Formio.createForm(formWithCheckboxRadioType).then((form) => { - form.setValue({ data: { radio: 'value1', checkbox: true } }); - setTimeout(() => { - const stringValues = { - checkbox1: 'Yes', - checkbox2: 'No', - checkbox: 'Yes' + const originalMakeRequest = Formio.makeRequest; + const errorText = 'Server error'; + Formio.makeRequest = function () { + return new Promise((res, rej) => { + setTimeout(() => { + rej(errorText); + }, 50); + }); }; - form.eachComponent((comp) => { - assert.equal(comp.getValueAsString(comp.dataValue), stringValues[`${comp.component.key}`], `Error for string value of ${comp.component.key}`); - }); + Formio.createForm(element, form) + .then((instance) => { + instance.formio = new Formio('http://localhost:3000/test'); + assert.equal(instance.errors.length, 0); + assert.equal( + !!(instance.serverErrors && instance.serverErrors.length), + false, + ); + assert.equal( + !!(instance.refs.errorRef && instance.refs.errorRef.length), + false, + ); + + const clickEvent = new Event('click'); + const submitBtn = instance.element.querySelector( + '[name="data[submit]"]', + ); + submitBtn.dispatchEvent(clickEvent); - form.setValue({ data: { radio: 'value2', checkbox: false } }); + setTimeout(() => { + assert.equal(instance.errors.length, 0); + assert.equal(instance.serverErrors.length, 1); + assert.equal(instance.refs.errorRef.length, 1); + assert.equal( + instance.refs.errorRef[0].textContent.trim(), + errorText, + ); + + const inputEvent = new Event('input'); + const textField = instance.element.querySelector( + 'input[name="data[name]"]', + ); + textField.value = 'test'; + textField.dispatchEvent(inputEvent); - setTimeout(() => { - const stringValues2 = { - checkbox1: 'No', - checkbox2: 'Yes', - checkbox: 'No' - }; + setTimeout(() => { + assert.equal(instance.errors.length, 0); + assert.equal(instance.serverErrors.length, 1); + assert.equal(instance.refs.errorRef.length, 1); + assert.equal( + instance.refs.errorRef[0].textContent.trim(), + errorText, + ); + Formio.makeRequest = originalMakeRequest; + done(); + }, 400); + }, 400); + }) + .catch(done); + }); - form.eachComponent((comp) => { - assert.equal(comp.getValueAsString(comp.dataValue), stringValues2[`${comp.component.key}`], `Error for string value of ${comp.component.key}`); - }); + it('Should execute form controller', function (done) { + Formio.createForm(formWithFormController) + .then((form) => { + setTimeout(() => { + const textField = form.getComponent('textField'); - done(); - }, 200); - }, 200); - }).catch((err) => done(err)); - }); + assert.equal(textField.getValue(), 'Hello World'); + assert.equal(textField.disabled, true); + assert.equal(form.components[0].disabled, true); - it('Should set value for hidden nested component through the logic triggered by event', function(done) { - const formElement = document.createElement('div'); - const form = new Webform(formElement); + done(); + }, 300); + }) + .catch((err) => done(err)); + }); - form.setForm(formWithEventLogicInHiddenComponent).then(() => { - const regesteredAddress = form.getComponent('registeredAddressInformation').getComponent('streetAddress')[0]; - const address = form.getComponent('addressInformation').getComponent('streetAddress')[0]; + it('Should set radio components value inside data grid correctly', function (done) { + Formio.createForm(formWithRadioInsideDataGrid) + .then((form) => { + const dataGridData = [ + { radio: 'two' }, + { radio: 'two' }, + { radio: 'three' }, + ]; + form.setValue({ + data: { dataGrid: fastCloneDeep(dataGridData) }, + }); + setTimeout(() => { + const dataGrid = form.getComponent('dataGrid'); + assert.deepEqual(dataGrid.dataValue, dataGridData); + done(); + }, 200); + }) + .catch((err) => done(err)); + }); - assert.equal(address.visible, true); - assert.equal(regesteredAddress.visible, false); + it('Should not fall into setValue calls loop when doing value calculation on server', function (done) { + const formElement = document.createElement('div'); + // Set a spy for Edit Grid setValue method + const spy = sinon.spy( + Formio.Components.components.editgrid.prototype, + 'setValue', + ); - const value = 'Dallas'; - address.setValue(value); + Formio.createForm(formElement, calculateValueOnServerForEditGrid, { + server: true, + noDefaults: true, + }) + .then((form) => { + assert.deepEqual(form.data, { + editGrid: [{ fielda: undefined, fieldb: 'test' }], + }); + assert.equal(spy.callCount, 1); - setTimeout(() => { - assert.equal(address.dataValue, value); - assert.equal(regesteredAddress.dataValue, value); + const first = form.getComponent('first'); - const role = form.getComponent('role'); - role.setValue(['client']); + first.setValue('test value'); - setTimeout(() => { - assert.equal(address.visible, false); - assert.equal(regesteredAddress.visible, true); - assert.equal(regesteredAddress.dataValue, value); - assert.equal(address.dataValue, value); - done(); - }, 500); - }, 500); - }).catch((err) => done(err)); - }); + setTimeout(() => { + assert.deepEqual(form.data, { + first: 'test value', + editGrid: [{ fielda: 'test value', fieldb: 'test' }], + }); + assert.equal(spy.callCount, 2); + // Remove the spy from setValue method + Formio.Components.components.editgrid.prototype.setValue.restore(); + done(); + }, 300); + }) + .catch(done); + }); - it('Should recalculate value when submission is being set in edit mode', function(done) { - const formElement = document.createElement('div'); - const form = new Webform(formElement); + it('Should fire blur and focus events for address and select components', function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement); - form.setForm(formWithCalcValue).then(() => { - const numberComp = form.getComponent('number'); - const checkbox = form.getComponent('checkbox'); + form.setForm(checkBlurFocusEventForm) + .then(() => { + let blurEvents = 0; + let focusEvents = 0; + form.on('blur', () => { + blurEvents = blurEvents + 1; + }); - form.setSubmission({}).then(() => { - setTimeout(() => { - assert.equal(numberComp.dataValue, 0); - assert.equal(checkbox.dataValue, true); - form.setSubmission({ data: { number: 7, checkbox: true } }).then(() => { - setTimeout(() => { - assert.equal(numberComp.dataValue, 7); - assert.equal(checkbox.dataValue, false); - done(); - }, 500); - }); - }, 500); - }); - }).catch((err) => done(err)); - }); - - it('Should not activate checkbox when clicking tooltip icon', function(done) { - const element = document.createElement('div'); - const form = new Webform(element); - - form.setForm(tooltipActivateCheckbox).then(() => { - const checkboxValue = form.element.querySelector('[name="data[checkbox]"]').value; - Harness.clickElement(form, form.element.querySelector('[ref="tooltip"]')); - - setTimeout(() => { - assert.equal(form.element.querySelector('[name="data[checkbox]"]').value, checkboxValue); - done(); - }, 200); - }) - .catch((err) => done(err)); - }); - - it('Should show submission if passed as option', function(done) { - const formElement = document.createElement('div'); - const form = new Webform(formElement, { renderMode: 'html', readOnly: true, submission: { data: { survey: { question1: 'a3', question2: 'a1' } } } }); + form.on('focus', () => { + focusEvents = focusEvents + 1; + }); - form.setForm(formWithSurvey).then(() => { - const survey = form.getComponent('survey'); - const values = survey.element.querySelectorAll('td'); + const focusEvent = new Event('focus'); + const blurEvent = new Event('blur'); - assert.equal(values.length, 2); - assert.equal(values[0].innerHTML.trim(), 'a3'); - assert.equal(values[1].innerHTML.trim(), 'a1'); - done(); - }).catch((err) => done(err)); - }); + const selectChoices = form.getComponent('selectChoices'); + selectChoices.focusableElement.dispatchEvent(focusEvent); - it('Should show survey values in html render mode', function(done) { - const formElement = document.createElement('div'); - const form = new Webform(formElement, { renderMode: 'html', readOnly: true }); - - form.setForm(formWithSurvey).then(() => { - form.setSubmission({ data: { survey: { question1: 'a3', question2: 'a1' } } }).then(() => { - const survey = form.getComponent('survey'); - const values = survey.element.querySelectorAll('td'); - - assert.equal(values.length, 2); - assert.equal(values[0].innerHTML.trim(), 'a3'); - assert.equal(values[1].innerHTML.trim(), 'a1'); - done(); - }); - }).catch((err) => done(err)); - }); - - it('Should show select boxes values in html render mode', function(done) { - const formElement = document.createElement('div'); - const form = new Webform(formElement, { renderMode: 'html', readOnly: true }); + setTimeout(() => { + selectChoices.focusableElement.dispatchEvent(blurEvent); + + const selectHtml = form.getComponent('selectHtml'); + selectHtml.refs.selectContainer.dispatchEvent(focusEvent); - form.setForm(formWithSelectBoxes).then(() => { - form.setSubmission({ data: { selectBoxes: { a: true, b: true, c: false } } }).then(() => { - const selectBoxes = form.getComponent('selectBoxes'); - const values = selectBoxes.element.querySelector('[ref="value"]').textContent.trim(); + setTimeout(() => { + selectHtml.refs.selectContainer.dispatchEvent( + blurEvent, + ); + + const address = form.getComponent('address'); + address.refs.searchInput[0].dispatchEvent(focusEvent); + + setTimeout(() => { + address.refs.searchInput[0].dispatchEvent( + blurEvent, + ); + + setTimeout(() => { + assert.equal(focusEvents, 3); + assert.equal(blurEvents, 3); + done(); + }, 300); + }, 300); + }, 300); + }, 300); + }) + .catch((err) => done(err)); + }); - assert.equal(values, 'a, b'); - done(); - }); - }).catch((err) => done(err)); - }); + it('Should return correct string value for checkbox radio type', function (done) { + Formio.createForm(formWithCheckboxRadioType) + .then((form) => { + form.setValue({ data: { radio: 'value1', checkbox: true } }); + setTimeout(() => { + const stringValues = { + checkbox1: 'Yes', + checkbox2: 'No', + checkbox: 'Yes', + }; + + form.eachComponent((comp) => { + assert.equal( + comp.getValueAsString(comp.dataValue), + stringValues[`${comp.component.key}`], + `Error for string value of ${comp.component.key}`, + ); + }); + + form.setValue({ + data: { radio: 'value2', checkbox: false }, + }); - it('Should show day value in html render mode', function(done) { - const formElement = document.createElement('div'); - const form = new Webform(formElement, { renderMode: 'html', readOnly: true }); + setTimeout(() => { + const stringValues2 = { + checkbox1: 'No', + checkbox2: 'Yes', + checkbox: 'No', + }; + + form.eachComponent((comp) => { + assert.equal( + comp.getValueAsString(comp.dataValue), + stringValues2[`${comp.component.key}`], + `Error for string value of ${comp.component.key}`, + ); + }); + + done(); + }, 200); + }, 200); + }) + .catch((err) => done(err)); + }); - form.setForm(formWithDayComp).then(() => { - form.setSubmission({ data: { day: '05/07/2020' } }).then(() => { - const day = form.getComponent('day'); - const value = day.element.querySelector('[ref="value"]').textContent.trim(); + it('Should set value for hidden nested component through the logic triggered by event', function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement); - assert.equal(value, '05/07/2020'); - done(); - }); - }).catch((err) => done(err)); - }); + form.setForm(formWithEventLogicInHiddenComponent) + .then(() => { + const regesteredAddress = form + .getComponent('registeredAddressInformation') + .getComponent('streetAddress')[0]; + const address = form + .getComponent('addressInformation') + .getComponent('streetAddress')[0]; - it('Should allow to input value and add rows in deeply nested conditional dataGrid', function(done) { - const formElement = document.createElement('div'); - const form = new Webform(formElement); + assert.equal(address.visible, true); + assert.equal(regesteredAddress.visible, false); - form.setForm(deeplyNestedDataGridAndContainer).then(() => { - const parentDataGrid = form.getComponent('dataGrid6'); + const value = 'Dallas'; + address.setValue(value); - assert.equal(parentDataGrid.rows.length, 1); - assert.equal(parentDataGrid.rows[0].dataGrid5.visible, false); + setTimeout(() => { + assert.equal(address.dataValue, value); + assert.equal(regesteredAddress.dataValue, value); - const checkbox = form.getComponent('checkbox'); - checkbox.setValue(true); + const role = form.getComponent('role'); + role.setValue(['client']); - setTimeout(() => { - assert.equal(parentDataGrid.rows.length, 1); - assert.equal(parentDataGrid.rows[0].dataGrid5.visible, true); + setTimeout(() => { + assert.equal(address.visible, false); + assert.equal(regesteredAddress.visible, true); + assert.equal(regesteredAddress.dataValue, value); + assert.equal(address.dataValue, value); + done(); + }, 500); + }, 500); + }) + .catch((err) => done(err)); + }); - const numberInput = parentDataGrid.rows[0].dataGrid5.rows[0].number.refs.input[0]; - numberInput.value = 555; + it('Should recalculate value when submission is being set in edit mode', function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement); - const inputEvent = new Event('input'); - numberInput.dispatchEvent(inputEvent); + form.setForm(formWithCalcValue) + .then(() => { + const numberComp = form.getComponent('number'); + const checkbox = form.getComponent('checkbox'); - setTimeout(() => { - const conditionalDataGrid = form.getComponent('dataGrid6').rows[0].dataGrid5; - const numberComp = conditionalDataGrid.rows[0].number; + form.setSubmission({}).then(() => { + setTimeout(() => { + assert.equal(numberComp.dataValue, 0); + assert.equal(checkbox.dataValue, true); + form.setSubmission({ + data: { number: 7, checkbox: true }, + }).then(() => { + setTimeout(() => { + assert.equal(numberComp.dataValue, 7); + assert.equal(checkbox.dataValue, false); + done(); + }, 500); + }); + }, 500); + }); + }) + .catch((err) => done(err)); + }); - assert.equal(numberComp.dataValue, 555); - assert.equal(numberComp.refs.input[0].value, 555); + it('Should not activate checkbox when clicking tooltip icon', function (done) { + const element = document.createElement('div'); + const form = new Webform(element); - const addRowBtn = conditionalDataGrid.refs[`${'datagrid-dataGrid5-addRow'}`][0]; - const clickEvent = new Event('click'); - addRowBtn.dispatchEvent(clickEvent); + form.setForm(tooltipActivateCheckbox) + .then(() => { + const checkboxValue = form.element.querySelector( + '[name="data[checkbox]"]', + ).value; + Harness.clickElement( + form, + form.element.querySelector('[ref="tooltip"]'), + ); - setTimeout(() => { - assert.equal(conditionalDataGrid.rows.length, 2); - done(); - }, 300); - }, 300); - }, 300); - }).catch((err) => done(err)); - }); + setTimeout(() => { + assert.equal( + form.element.querySelector('[name="data[checkbox]"]') + .value, + checkboxValue, + ); + done(); + }, 200); + }) + .catch((err) => done(err)); + }); - it('Should adjust columns when conditional fields appear/disappear', function(done) { - const formElement = document.createElement('div'); - const form = new Webform(formElement); - - form.setForm(columnWithConditionalComponents).then(() => { - const selectBoxes = form.getComponent('selectBoxes'); - const columns = form.getComponent('columns'); - - columns.columns.forEach((column, index) => { - assert.equal(column[0].visible, false, `Column ${index + 1} component should be hidden`); - assert.equal( columns.component.columns[index].currentWidth, 0, `Column ${index + 1} width should be 0`); - }); - - selectBoxes.setValue({ '1': false, '2': false, '3': true, '4': false, '5': false, '6': true }); - - setTimeout(() => { - columns.columns.forEach((column, index) => { - if ([3,6].includes(index+1)) { - assert.equal(column[0].visible, true, `Column ${index + 1} component should be visible`); - assert.equal(columns.component.columns[index].currentWidth, 2, `Column ${index + 1} width should be 2`); - } - else { - assert.equal(column[0].visible, false, `Column ${index + 1} component should be hidden`); - assert.equal( columns.component.columns[index].currentWidth, 0, `Column ${index + 1} width should be 0`); - } + it('Should show submission if passed as option', function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement, { + renderMode: 'html', + readOnly: true, + submission: { + data: { survey: { question1: 'a3', question2: 'a1' } }, + }, }); - const visibleTextField1 = columns.columns[2][0].refs.input[0]; - const visibleTextField2 = columns.columns[5][0].refs.input[0]; - - visibleTextField1.value = 'test '; - visibleTextField2.value = ' some '; - - const inputEvent = new Event('input'); - visibleTextField1.dispatchEvent(inputEvent); - visibleTextField2.dispatchEvent(inputEvent); - - setTimeout(() => { - const visibleTextField1 = columns.columns[2][0].refs.input[0]; - const visibleTextField2 = columns.columns[5][0].refs.input[0]; - - assert.equal(visibleTextField1.value,'test ', 'Should not cut whitespaces while inputting into the conditional component inside column'); - assert.equal(visibleTextField2.value,' some ', 'Should not cut whitespaces while inputting into the conditional component inside column'); - selectBoxes.setValue({ '1': false, '2': false, '3': false, '4': false, '5': false, '6': true }); - - setTimeout(() => { - columns.columns.forEach((column, index) => { - if ([6].includes(index+1)) { - assert.equal(column[0].visible, true, `Column ${index + 1} component should be visible`); - assert.equal( columns.component.columns[index].currentWidth, 2, `Column ${index + 1} width should be 2`); - } - else { - assert.equal(column[0].visible, false, `Column ${index + 1} component should be hidden`); - assert.equal( columns.component.columns[index].currentWidth, 0, `Column ${index + 1} width should be 0`); - } - }); - done(); - }, 300); - }, 300); - }, 300); - }).catch((err) => done(err)); - }); + form.setForm(formWithSurvey) + .then(() => { + const survey = form.getComponent('survey'); + const values = survey.element.querySelectorAll('td'); - it('Should not translate en value if _userInput option is provided and value presents in reserved translation names', function(done) { - const formElement = document.createElement('div'); - const form = new Webform(formElement, { - language: 'en' - }); - form.setForm(translationTestForm).then(() => { - setTimeout(() => { - const selectComp = form.getComponent('select'); - const options = selectComp.element.querySelector('[role="listbox"]').children; - const option1 = options[0].textContent.trim(); - const option2 = options[1].textContent.trim(); - const label = selectComp.element.querySelector('label').textContent.trim(); - assert.equal(option1, translationTestForm.components[0].data.values[0].label); - assert.equal(option2, translationTestForm.components[0].data.values[1].label); - assert.equal(label, translationTestForm.components[0].label); - document.body.innerHTML = ''; - done(); - }, 100); - }).catch(done); - }); - - it('Should translate in English if _userInput option is provided and value does not present in reserved translation names', function(done) { - const formElement = document.createElement('div'); - const selectLabel = 'Select test label'; - const translationForm = fastCloneDeep(translationTestForm); - translationForm.components[0].label = selectLabel; - const form = new Webform(formElement, { - language: 'en', - i18n: { - en: { - 'Select test label': 'English Label' - }, - fr: { - 'Select test label': 'French Label' - } - } + assert.equal(values.length, 2); + assert.equal(values[0].innerHTML.trim(), 'a3'); + assert.equal(values[1].innerHTML.trim(), 'a1'); + done(); + }) + .catch((err) => done(err)); }); - form.setForm(translationForm).then(() => { - const selectComp = form.getComponent('select'); - const label = selectComp.element.querySelector('label').textContent.trim(); - - assert.equal(label, 'English Label'); - document.body.innerHTML = ''; - done(); - }).catch(done); - }); + it('Should show survey values in html render mode', function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement, { + renderMode: 'html', + readOnly: true, + }); - it('Should translate value in franch if _userInput option is provided and value does not present in reserved translation names', function(done) { - const formElement = document.createElement('div'); - const selectLabel = 'Select test label'; - const translationForm = fastCloneDeep(translationTestForm); - translationForm.components[0].label = selectLabel; - const form = new Webform(formElement, { - language: 'fr', - i18n: { - en: { - 'Select test label': 'English Label' - }, - fr: { - 'Select test label': 'French Label' - } - } + form.setForm(formWithSurvey) + .then(() => { + form.setSubmission({ + data: { survey: { question1: 'a3', question2: 'a1' } }, + }).then(() => { + const survey = form.getComponent('survey'); + const values = survey.element.querySelectorAll('td'); + + assert.equal(values.length, 2); + assert.equal(values[0].innerHTML.trim(), 'a3'); + assert.equal(values[1].innerHTML.trim(), 'a1'); + done(); + }); + }) + .catch((err) => done(err)); }); - form.setForm(translationForm).then(() => { - const selectComp = form.getComponent('select'); - const label = selectComp.element.querySelector('label').textContent.trim(); + it('Should show select boxes values in html render mode', function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement, { + renderMode: 'html', + readOnly: true, + }); - assert.equal(label, 'French Label'); - document.body.innerHTML = ''; - done(); - }).catch(done); - }); + form.setForm(formWithSelectBoxes) + .then(() => { + form.setSubmission({ + data: { selectBoxes: { a: true, b: true, c: false } }, + }).then(() => { + const selectBoxes = form.getComponent('selectBoxes'); + const values = selectBoxes.element + .querySelector('[ref="value"]') + .textContent.trim(); + + assert.equal(values, 'a, b'); + done(); + }); + }) + .catch((err) => done(err)); + }); - it('Should display dataGrid conditional column once the condition is met', function(done) { - const formElement = document.createElement('div'); - const formWithCondDataGridColumn = new Webform(formElement); + it('Should show day value in html render mode', function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement, { + renderMode: 'html', + readOnly: true, + }); - formWithCondDataGridColumn.setForm(formWithDataGridWithCondColumn).then(() => { - const condDataGridField = formWithCondDataGridColumn.element.querySelector( '[name="data[dataGrid][0][numberCond]"]'); - assert.equal(!!condDataGridField, false); + form.setForm(formWithDayComp) + .then(() => { + form.setSubmission({ data: { day: '05/07/2020' } }).then(() => { + const day = form.getComponent('day'); + const value = day.element + .querySelector('[ref="value"]') + .textContent.trim(); - const textField = formWithCondDataGridColumn.element.querySelector( '[name="data[textField]"]'); - textField.value = 'show'; + assert.equal(value, '05/07/2020'); + done(); + }); + }) + .catch((err) => done(err)); + }); - const inputEvent = new Event('input'); - textField.dispatchEvent(inputEvent); + it('Should allow to input value and add rows in deeply nested conditional dataGrid', function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement); - setTimeout(() => { - const condDataGridFieldAfterFulfillingCond = formWithCondDataGridColumn.element.querySelector( '[name="data[dataGrid][0][numberCond]"]'); - assert.equal(!!condDataGridFieldAfterFulfillingCond, true); + form.setForm(deeplyNestedDataGridAndContainer) + .then(() => { + const parentDataGrid = form.getComponent('dataGrid6'); - done(); - }, 300); - }).catch((err) => done(err)); - }); + assert.equal(parentDataGrid.rows.length, 1); + assert.equal(parentDataGrid.rows[0].dataGrid5.visible, false); - it('Should remove dataGrid extra rows and components after setting value with less row number', function(done) { - const formElement = document.createElement('div'); - const formWithDG = new Webform(formElement); + const checkbox = form.getComponent('checkbox'); + checkbox.setValue(true); - formWithDG.setForm(formWithDataGrid.form).then(() => { - formWithDG.setSubmission(formWithDataGrid.submission3rows); + setTimeout(() => { + assert.equal(parentDataGrid.rows.length, 1); + assert.equal( + parentDataGrid.rows[0].dataGrid5.visible, + true, + ); - setTimeout(() => { - assert.equal(formWithDG.components[0].rows.length, 3); - assert.equal(formWithDG.components[0].components.length, 3); + const numberInput = + parentDataGrid.rows[0].dataGrid5.rows[0].number.refs + .input[0]; + numberInput.value = 555; - formWithDG.setSubmission(formWithDataGrid.submission1row); + const inputEvent = new Event('input'); + numberInput.dispatchEvent(inputEvent); - setTimeout(() => { - assert.equal(formWithDG.components[0].rows.length, 1); - assert.equal(formWithDG.components[0].components.length, 1); + setTimeout(() => { + const conditionalDataGrid = + form.getComponent('dataGrid6').rows[0].dataGrid5; + const numberComp = conditionalDataGrid.rows[0].number; + + assert.equal(numberComp.dataValue, 555); + assert.equal(numberComp.refs.input[0].value, 555); + + const addRowBtn = + conditionalDataGrid.refs[ + `${'datagrid-dataGrid5-addRow'}` + ][0]; + const clickEvent = new Event('click'); + addRowBtn.dispatchEvent(clickEvent); + + setTimeout(() => { + assert.equal(conditionalDataGrid.rows.length, 2); + done(); + }, 300); + }, 300); + }, 300); + }) + .catch((err) => done(err)); + }); - done(); - }, 200); - }, 100); - }).catch((err) => done(err)); - }); + it('Should adjust columns when conditional fields appear/disappear', function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement); + + form.setForm(columnWithConditionalComponents) + .then(() => { + const selectBoxes = form.getComponent('selectBoxes'); + const columns = form.getComponent('columns'); + + columns.columns.forEach((column, index) => { + assert.equal( + column[0].visible, + false, + `Column ${index + 1} component should be hidden`, + ); + assert.equal( + columns.component.columns[index].currentWidth, + 0, + `Column ${index + 1} width should be 0`, + ); + }); + + selectBoxes.setValue({ + 1: false, + 2: false, + 3: true, + 4: false, + 5: false, + 6: true, + }); - it('Should not delete/change date value in dataGrid after adding new row', function(done) { - const formElement = document.createElement('div'); - const formWithDate = new Webform(formElement); + setTimeout(() => { + columns.columns.forEach((column, index) => { + if ([3, 6].includes(index + 1)) { + assert.equal( + column[0].visible, + true, + `Column ${ + index + 1 + } component should be visible`, + ); + assert.equal( + columns.component.columns[index].currentWidth, + 2, + `Column ${index + 1} width should be 2`, + ); + } else { + assert.equal( + column[0].visible, + false, + `Column ${index + 1} component should be hidden`, + ); + assert.equal( + columns.component.columns[index].currentWidth, + 0, + `Column ${index + 1} width should be 0`, + ); + } + }); + + const visibleTextField1 = + columns.columns[2][0].refs.input[0]; + const visibleTextField2 = + columns.columns[5][0].refs.input[0]; + + visibleTextField1.value = 'test '; + visibleTextField2.value = ' some '; + + const inputEvent = new Event('input'); + visibleTextField1.dispatchEvent(inputEvent); + visibleTextField2.dispatchEvent(inputEvent); - formWithDate.setForm(formWithCustomFormatDate).then(() => { - setTimeout(() => { - const clickEvent = new Event('click'); + setTimeout(() => { + const visibleTextField1 = + columns.columns[2][0].refs.input[0]; + const visibleTextField2 = + columns.columns[5][0].refs.input[0]; + + assert.equal( + visibleTextField1.value, + 'test ', + 'Should not cut whitespaces while inputting into the conditional component inside column', + ); + assert.equal( + visibleTextField2.value, + ' some ', + 'Should not cut whitespaces while inputting into the conditional component inside column', + ); + selectBoxes.setValue({ + 1: false, + 2: false, + 3: false, + 4: false, + 5: false, + 6: true, + }); + + setTimeout(() => { + columns.columns.forEach((column, index) => { + if ([6].includes(index + 1)) { + assert.equal( + column[0].visible, + true, + `Column ${ + index + 1 + } component should be visible`, + ); + assert.equal( + columns.component.columns[index] + .currentWidth, + 2, + `Column ${index + 1} width should be 2`, + ); + } else { + assert.equal( + column[0].visible, + false, + `Column ${ + index + 1 + } component should be hidden`, + ); + assert.equal( + columns.component.columns[index] + .currentWidth, + 0, + `Column ${index + 1} width should be 0`, + ); + } + }); + done(); + }, 300); + }, 300); + }, 300); + }) + .catch((err) => done(err)); + }); - const dateCompInputWidget = formWithDate.element.querySelector('.formio-component-textField').querySelector('.flatpickr-input').widget; - const dateAltFormat = dateCompInputWidget.calendar.config.altFormat; - dateCompInputWidget.calendar.setDate('30-05-2020', true, dateAltFormat); + it('Should not translate en value if _userInput option is provided and value presents in reserved translation names', function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement, { + language: 'en', + }); + form.setForm(translationTestForm) + .then(() => { + setTimeout(() => { + const selectComp = form.getComponent('select'); + const options = + selectComp.element.querySelector( + '[role="listbox"]', + ).children; + const option1 = options[0].textContent.trim(); + const option2 = options[1].textContent.trim(); + const label = selectComp.element + .querySelector('label') + .textContent.trim(); + assert.equal( + option1, + translationTestForm.components[0].data.values[0].label, + ); + assert.equal( + option2, + translationTestForm.components[0].data.values[1].label, + ); + assert.equal( + label, + translationTestForm.components[0].label, + ); + document.body.innerHTML = ''; + done(); + }, 100); + }) + .catch(done); + }); - const dateCompInputWidget1 = formWithDate.element.querySelector('.formio-component-dateTime').querySelector('.flatpickr-input').widget; - const dateAltFormat1 = dateCompInputWidget1.calendar.config.altFormat; - dateCompInputWidget1.calendar.setDate('30-05-2020', true, dateAltFormat1); + it('Should translate in English if _userInput option is provided and value does not present in reserved translation names', function (done) { + const formElement = document.createElement('div'); + const selectLabel = 'Select test label'; + const translationForm = fastCloneDeep(translationTestForm); + translationForm.components[0].label = selectLabel; + const form = new Webform(formElement, { + language: 'en', + i18n: { + en: { + 'Select test label': 'English Label', + }, + fr: { + 'Select test label': 'French Label', + }, + }, + }); - const dateCompInputWidget2 = formWithDate.element.querySelector('.formio-component-textField2').querySelector('.flatpickr-input').widget; - const dateAltFormat2 = dateCompInputWidget2.calendar.config.altFormat; - dateCompInputWidget2.calendar.setDate('30-05-2020', true, dateAltFormat2); + form.setForm(translationForm) + .then(() => { + const selectComp = form.getComponent('select'); + const label = selectComp.element + .querySelector('label') + .textContent.trim(); - setTimeout(() => { - const dateCompAltInput = formWithDate.element.querySelector('.formio-component-textField').querySelector('.flatpickr-input'); - const dateComp = formWithDate.element.querySelector('.formio-component-textField').querySelector('[type="text"]'); + assert.equal(label, 'English Label'); + document.body.innerHTML = ''; + done(); + }) + .catch(done); + }); - const dateCompAltInput1 = formWithDate.element.querySelector('.formio-component-dateTime').querySelector('.flatpickr-input'); - const dateComp1 = formWithDate.element.querySelector('.formio-component-dateTime').querySelector('[type="text"]'); + it('Should translate value in franch if _userInput option is provided and value does not present in reserved translation names', function (done) { + const formElement = document.createElement('div'); + const selectLabel = 'Select test label'; + const translationForm = fastCloneDeep(translationTestForm); + translationForm.components[0].label = selectLabel; + const form = new Webform(formElement, { + language: 'fr', + i18n: { + en: { + 'Select test label': 'English Label', + }, + fr: { + 'Select test label': 'French Label', + }, + }, + }); - const dateCompAltInput2 = formWithDate.element.querySelector('.formio-component-textField2').querySelector('.flatpickr-input'); - const dateComp2 = formWithDate.element.querySelector('.formio-component-textField2').querySelector('[type="text"]'); + form.setForm(translationForm) + .then(() => { + const selectComp = form.getComponent('select'); + const label = selectComp.element + .querySelector('label') + .textContent.trim(); - assert.equal(dateCompAltInput.value,'30-05-2020'); - assert.equal(dateComp.value,'30-05-2020'); + assert.equal(label, 'French Label'); + document.body.innerHTML = ''; + done(); + }) + .catch(done); + }); - assert.equal(dateCompAltInput1.value,'2020-05-30T00:00:00'); - assert.equal(dateComp1.value,'30-05-2020'); + it('Should display dataGrid conditional column once the condition is met', function (done) { + const formElement = document.createElement('div'); + const formWithCondDataGridColumn = new Webform(formElement); - assert.equal(dateCompAltInput2.value,'2020-05-30T00:00:00'); - assert.equal(dateComp2.value,'30-05-2020'); + formWithCondDataGridColumn + .setForm(formWithDataGridWithCondColumn) + .then(() => { + const condDataGridField = + formWithCondDataGridColumn.element.querySelector( + '[name="data[dataGrid][0][numberCond]"]', + ); + assert.equal(!!condDataGridField, false); - const addNewRowBtn = formWithDate.element.querySelector('.formio-button-add-row'); - addNewRowBtn.dispatchEvent(clickEvent); + const textField = + formWithCondDataGridColumn.element.querySelector( + '[name="data[textField]"]', + ); + textField.value = 'show'; - setTimeout(() => { - const dataGridRows = formWithDate.element.querySelectorAll('[ref="datagrid-dataGrid-row"]'); - assert.equal(dataGridRows.length, 2); + const inputEvent = new Event('input'); + textField.dispatchEvent(inputEvent); - const dateCompAltInputAfterAddingRow = formWithDate.element.querySelectorAll('.formio-component-textField')[0].querySelector('.flatpickr-input'); - const dateCompAfterAddingRow = formWithDate.element.querySelectorAll('.formio-component-textField')[0].querySelector('[type="text"]'); + setTimeout(() => { + const condDataGridFieldAfterFulfillingCond = + formWithCondDataGridColumn.element.querySelector( + '[name="data[dataGrid][0][numberCond]"]', + ); + assert.equal(!!condDataGridFieldAfterFulfillingCond, true); - const dateCompAltInputAfterAddingRow1 = formWithDate.element.querySelectorAll('.formio-component-dateTime')[0].querySelector('.flatpickr-input'); - const dateCompAfterAddingRow1 = formWithDate.element.querySelectorAll('.formio-component-dateTime')[0].querySelector('[type="text"]'); + done(); + }, 300); + }) + .catch((err) => done(err)); + }); - const dateCompAltInputAfterAddingRow2 = formWithDate.element.querySelectorAll('.formio-component-textField2')[0].querySelector('.flatpickr-input'); - const dateCompAfterAddingRow2 = formWithDate.element.querySelectorAll('.formio-component-textField2')[0].querySelector('[type="text"]'); + it('Should remove dataGrid extra rows and components after setting value with less row number', function (done) { + const formElement = document.createElement('div'); + const formWithDG = new Webform(formElement); - assert.equal(dateCompAltInputAfterAddingRow.value,'30-05-2020'); - assert.equal(dateCompAfterAddingRow.value,'30-05-2020'); + formWithDG + .setForm(formWithDataGrid.form) + .then(() => { + formWithDG.setSubmission(formWithDataGrid.submission3rows); - assert.equal(dateCompAltInputAfterAddingRow1.value,'2020-05-30T00:00:00'); - assert.equal(dateCompAfterAddingRow1.value,'30-05-2020'); + setTimeout(() => { + assert.equal(formWithDG.components[0].rows.length, 3); + assert.equal(formWithDG.components[0].components.length, 3); - assert.equal(dateCompAltInputAfterAddingRow2.value,'2020-05-30T00:00:00'); - assert.equal(dateCompAfterAddingRow2.value,'30-05-2020'); + formWithDG.setSubmission(formWithDataGrid.submission1row); - done(); - }, 150); - }, 50); - }, 100); - }).catch((err) => done(err)); - }); + setTimeout(() => { + assert.equal(formWithDG.components[0].rows.length, 1); + assert.equal( + formWithDG.components[0].components.length, + 1, + ); + + done(); + }, 200); + }, 100); + }) + .catch((err) => done(err)); + }); - it('Should open collapsed panel with invalid components inside container that is inside the panel on submit', function(done) { - const formElement = document.createElement('div'); - const formWithPanel = new Webform(formElement); + it('Should not delete/change date value in dataGrid after adding new row', function (done) { + const formElement = document.createElement('div'); + const formWithDate = new Webform(formElement); - formWithPanel.setForm(formWithCollapsedPanel).then(() => { - const clickEvent = new Event('click'); + formWithDate + .setForm(formWithCustomFormatDate) + .then(() => { + setTimeout(() => { + const clickEvent = new Event('click'); + + const dateCompInputWidget = formWithDate.element + .querySelector('.formio-component-textField') + .querySelector('.flatpickr-input').widget; + const dateAltFormat = + dateCompInputWidget.calendar.config.altFormat; + dateCompInputWidget.calendar.setDate( + '30-05-2020', + true, + dateAltFormat, + ); + + const dateCompInputWidget1 = formWithDate.element + .querySelector('.formio-component-dateTime') + .querySelector('.flatpickr-input').widget; + const dateAltFormat1 = + dateCompInputWidget1.calendar.config.altFormat; + dateCompInputWidget1.calendar.setDate( + '30-05-2020', + true, + dateAltFormat1, + ); + + const dateCompInputWidget2 = formWithDate.element + .querySelector('.formio-component-textField2') + .querySelector('.flatpickr-input').widget; + const dateAltFormat2 = + dateCompInputWidget2.calendar.config.altFormat; + dateCompInputWidget2.calendar.setDate( + '30-05-2020', + true, + dateAltFormat2, + ); - assert.equal(formWithPanel.components[0].collapsed, true); + setTimeout(() => { + const dateCompAltInput = formWithDate.element + .querySelector('.formio-component-textField') + .querySelector('.flatpickr-input'); + const dateComp = formWithDate.element + .querySelector('.formio-component-textField') + .querySelector('[type="text"]'); + + const dateCompAltInput1 = formWithDate.element + .querySelector('.formio-component-dateTime') + .querySelector('.flatpickr-input'); + const dateComp1 = formWithDate.element + .querySelector('.formio-component-dateTime') + .querySelector('[type="text"]'); + + const dateCompAltInput2 = formWithDate.element + .querySelector('.formio-component-textField2') + .querySelector('.flatpickr-input'); + const dateComp2 = formWithDate.element + .querySelector('.formio-component-textField2') + .querySelector('[type="text"]'); + + assert.equal(dateCompAltInput.value, '30-05-2020'); + assert.equal(dateComp.value, '30-05-2020'); + + assert.equal( + dateCompAltInput1.value, + '2020-05-30T00:00:00', + ); + assert.equal(dateComp1.value, '30-05-2020'); + + assert.equal( + dateCompAltInput2.value, + '2020-05-30T00:00:00', + ); + assert.equal(dateComp2.value, '30-05-2020'); + + const addNewRowBtn = formWithDate.element.querySelector( + '.formio-button-add-row', + ); + addNewRowBtn.dispatchEvent(clickEvent); + + setTimeout(() => { + const dataGridRows = + formWithDate.element.querySelectorAll( + '[ref="datagrid-dataGrid-row"]', + ); + assert.equal(dataGridRows.length, 2); + + const dateCompAltInputAfterAddingRow = + formWithDate.element + .querySelectorAll( + '.formio-component-textField', + )[0] + .querySelector('.flatpickr-input'); + const dateCompAfterAddingRow = formWithDate.element + .querySelectorAll( + '.formio-component-textField', + )[0] + .querySelector('[type="text"]'); + + const dateCompAltInputAfterAddingRow1 = + formWithDate.element + .querySelectorAll( + '.formio-component-dateTime', + )[0] + .querySelector('.flatpickr-input'); + const dateCompAfterAddingRow1 = formWithDate.element + .querySelectorAll( + '.formio-component-dateTime', + )[0] + .querySelector('[type="text"]'); + + const dateCompAltInputAfterAddingRow2 = + formWithDate.element + .querySelectorAll( + '.formio-component-textField2', + )[0] + .querySelector('.flatpickr-input'); + const dateCompAfterAddingRow2 = formWithDate.element + .querySelectorAll( + '.formio-component-textField2', + )[0] + .querySelector('[type="text"]'); + + assert.equal( + dateCompAltInputAfterAddingRow.value, + '30-05-2020', + ); + assert.equal( + dateCompAfterAddingRow.value, + '30-05-2020', + ); + + assert.equal( + dateCompAltInputAfterAddingRow1.value, + '2020-05-30T00:00:00', + ); + assert.equal( + dateCompAfterAddingRow1.value, + '30-05-2020', + ); + + assert.equal( + dateCompAltInputAfterAddingRow2.value, + '2020-05-30T00:00:00', + ); + assert.equal( + dateCompAfterAddingRow2.value, + '30-05-2020', + ); + + done(); + }, 150); + }, 50); + }, 100); + }) + .catch((err) => done(err)); + }); - const submitBtn = formWithPanel.element.querySelector('[name="data[submit]"]'); - submitBtn.dispatchEvent(clickEvent); + it('Should open collapsed panel with invalid components inside container that is inside the panel on submit', function (done) { + const formElement = document.createElement('div'); + const formWithPanel = new Webform(formElement); - setTimeout(() => { - assert.equal(formWithPanel.components[0].collapsed, false); - done(); - }, 200); - }).catch((err) => done(err)); - }); + formWithPanel + .setForm(formWithCollapsedPanel) + .then(() => { + const clickEvent = new Event('click'); - it('Should correctly set date after collapsing and openning the panel', function(done) { - const formElement = document.createElement('div'); - const formWithDate = new Webform(formElement); + assert.equal(formWithPanel.components[0].collapsed, true); - formWithDate.setForm(formWithDateTimeComponents).then(() => { - const clickEvent = new Event('click'); + const submitBtn = formWithPanel.element.querySelector( + '[name="data[submit]"]', + ); + submitBtn.dispatchEvent(clickEvent); - const dateTimeCompInputWidget = formWithDate.element.querySelector('.formio-component-dateTime1').querySelector('.flatpickr-input').widget; - const dateTimeAltFormat = dateTimeCompInputWidget.calendar.config.altFormat; - dateTimeCompInputWidget.calendar.setDate('05-05-2020T00:00:00', true, dateTimeAltFormat); + setTimeout(() => { + assert.equal(formWithPanel.components[0].collapsed, false); + done(); + }, 200); + }) + .catch((err) => done(err)); + }); - const textFieldDateCompWidget = formWithDate.element.querySelector('.formio-component-textField1').querySelector('.flatpickr-input').widget; - const textFieldDateAltFormat = textFieldDateCompWidget.calendar.config.altFormat; - textFieldDateCompWidget.calendar.setDate('04-04-2020T00:00:00', true, textFieldDateAltFormat); + it('Should correctly set date after collapsing and openning the panel', function (done) { + const formElement = document.createElement('div'); + const formWithDate = new Webform(formElement); + + formWithDate + .setForm(formWithDateTimeComponents) + .then(() => { + const clickEvent = new Event('click'); + + const dateTimeCompInputWidget = formWithDate.element + .querySelector('.formio-component-dateTime1') + .querySelector('.flatpickr-input').widget; + const dateTimeAltFormat = + dateTimeCompInputWidget.calendar.config.altFormat; + dateTimeCompInputWidget.calendar.setDate( + '05-05-2020T00:00:00', + true, + dateTimeAltFormat, + ); + + const textFieldDateCompWidget = formWithDate.element + .querySelector('.formio-component-textField1') + .querySelector('.flatpickr-input').widget; + const textFieldDateAltFormat = + textFieldDateCompWidget.calendar.config.altFormat; + textFieldDateCompWidget.calendar.setDate( + '04-04-2020T00:00:00', + true, + textFieldDateAltFormat, + ); - setTimeout(() => { - const dateTimeCompAltInput = formWithDate.element.querySelector('.formio-component-dateTime1').querySelector('.flatpickr-input'); - const textFieldDateCompAltInput = formWithDate.element.querySelector('.formio-component-textField1').querySelector('.flatpickr-input'); + setTimeout(() => { + const dateTimeCompAltInput = formWithDate.element + .querySelector('.formio-component-dateTime1') + .querySelector('.flatpickr-input'); + const textFieldDateCompAltInput = formWithDate.element + .querySelector('.formio-component-textField1') + .querySelector('.flatpickr-input'); + + const dateTimeComp = formWithDate.element + .querySelector('.formio-component-dateTime1') + .querySelector('[type="text"]'); + const textFieldDateComp = formWithDate.element + .querySelector('.formio-component-textField1') + .querySelector('[type="text"]'); + + assert.equal( + dateTimeCompAltInput.value, + '2020-05-05T00:00:00', + ); + assert.equal( + textFieldDateCompAltInput.value, + '2020-04-04T00:00:00', + ); + + assert.equal(dateTimeComp.value, '05-05-2020'); + assert.equal(textFieldDateComp.value, '04-04-2020'); + + const panelCollapseBtn = formWithDate.element.querySelector( + '.formio-collapse-icon', + ); + panelCollapseBtn.dispatchEvent(clickEvent); - const dateTimeComp = formWithDate.element.querySelector('.formio-component-dateTime1').querySelector('[type="text"]'); - const textFieldDateComp = formWithDate.element.querySelector('.formio-component-textField1').querySelector('[type="text"]'); + setTimeout(() => { + const panelBody = + formWithDate.element.querySelector('.panel-body'); + assert.equal(!!panelBody, false); + + formWithDate.element + .querySelector('.formio-collapse-icon') + .dispatchEvent(clickEvent); + + setTimeout(() => { + const dateTimeCompAfterOpenningPanel = + formWithDate.element + .querySelector( + '.formio-component-dateTime1', + ) + .querySelector('[type="text"]'); + const textFieldDateCompAfterOpenningPanel = + formWithDate.element + .querySelector( + '.formio-component-textField1', + ) + .querySelector('[type="text"]'); + + const dateTimeCompAltInputAfterOpenningPanel = + formWithDate.element + .querySelector( + '.formio-component-dateTime1', + ) + .querySelector('.flatpickr-input'); + const textFieldDateCompAltInputAfterOpenningPanel = + formWithDate.element + .querySelector( + '.formio-component-textField1', + ) + .querySelector('.flatpickr-input'); + + assert.equal( + dateTimeCompAltInputAfterOpenningPanel.value, + '2020-05-05T00:00:00', + ); + assert.equal( + textFieldDateCompAltInputAfterOpenningPanel.value, + '2020-04-04T00:00:00', + ); + + assert.equal( + dateTimeCompAfterOpenningPanel.value, + '05-05-2020', + ); + assert.equal( + textFieldDateCompAfterOpenningPanel.value, + '04-04-2020', + ); + done(); + }, 250); + }, 150); + }, 50); + }) + .catch((err) => done(err)); + }); - assert.equal(dateTimeCompAltInput.value,'2020-05-05T00:00:00'); - assert.equal(textFieldDateCompAltInput.value,'2020-04-04T00:00:00'); + it(`Should show confirmation alert when clicking X btn or clicking outside modal window after editing + editGrid modal draft row`, function (done) { + const formElement = document.createElement('div'); + const formWithNestedDraftModals = new Webform(formElement); + + formWithNestedDraftModals + .setForm(formWithEditGridAndNestedDraftModalRow) + .then(() => { + const editGrid = + formWithNestedDraftModals.getComponent('editGrid'); + const clickEvent = new Event('click'); + const inputEvent = new Event('input'); + + const addRowBtn = + formWithNestedDraftModals.element.querySelector( + '[ref="editgrid-editGrid-addRow"]', + ); + //click to open row in modal view + addRowBtn.dispatchEvent(clickEvent); - assert.equal(dateTimeComp.value,'05-05-2020'); - assert.equal(textFieldDateComp.value,'04-04-2020'); + setTimeout(() => { + const rowModal = document.querySelector( + `.editgrid-row-modal-${editGrid.id}`, + ); + //checking if row modal was openned + assert.equal(!!rowModal, true); + + const textField = rowModal.querySelector( + '[name="data[textField]"]', + ); + textField.value = 'test'; + //input value + textField.dispatchEvent(inputEvent); - const panelCollapseBtn = formWithDate.element.querySelector('.formio-collapse-icon'); - panelCollapseBtn.dispatchEvent(clickEvent); + setTimeout(() => { + //checking if the value was set inside the field + assert.equal(textField.value, 'test'); + + const saveModalBtn = + rowModal.querySelector('.btn-primary'); + //clicking save button to save row draft + saveModalBtn.dispatchEvent(clickEvent); + + setTimeout(() => { + const editGridRows = + formWithNestedDraftModals.element.querySelectorAll( + '[ref="editgrid-editGrid-row"]', + ); + //checking if the editGrid row was created + assert.equal(editGridRows.length, 1); + + const editRowBtn = + editGridRows[0].querySelector('.editRow'); + //click the edit btn to open the row again + editRowBtn.dispatchEvent(clickEvent); + + setTimeout(() => { + const rowModalForEditing = + document.querySelector( + `.editgrid-row-modal-${editGrid.id}`, + ); + const textFieldInputForEditing = + rowModalForEditing.querySelector( + '[name="data[textField]"]', + ); + textFieldInputForEditing.value = + 'changed value'; + //changing textfield value + textFieldInputForEditing.dispatchEvent( + inputEvent, + ); + + setTimeout(() => { + //checking if the textfield value was changed + const inputValue = + textFieldInputForEditing.value; + assert.equal(inputValue, 'changed value'); + + const XCloseBtn = + rowModalForEditing.querySelector( + '[ref="dialogClose"]', + ); + //clicking modal close btn + XCloseBtn.dispatchEvent(clickEvent); + + setTimeout(() => { + const dialogConfirmationWindows = + document.querySelectorAll( + `.editgrid-row-modal-confirmation-${editGrid.id}`, + ); + //checking if confirmation dialog is openned + assert.equal( + dialogConfirmationWindows.length, + 1, + ); + + const dialogCancelBtn = + dialogConfirmationWindows[0].querySelector( + '[ref="dialogCancelButton"]', + ); + //closing confirmation dialog + dialogCancelBtn.dispatchEvent( + clickEvent, + ); + + setTimeout(() => { + const confirmationWindows = + document.querySelectorAll( + `.editgrid-row-modal-confirmation-${editGrid.id}`, + ); + //checking if confirmation dialig is closed + assert.equal( + confirmationWindows.length, + 0, + ); + + const dialog = + document.querySelector( + `.editgrid-row-modal-${editGrid.id}`, + ); + const overlay = + dialog.querySelector( + '[ref="dialogOverlay"]', + ); + //clocking model overlay to open confirmation dialog again + overlay.dispatchEvent(clickEvent); + + setTimeout(() => { + const confirmationDialogsAfterClickingOverlay = + document.querySelectorAll( + `.editgrid-row-modal-confirmation-${editGrid.id}`, + ); + assert.equal( + confirmationDialogsAfterClickingOverlay.length, + 1, + ); + + document.body.innerHTML = ''; + done(); + }, 190); + }, 170); + }, 150); + }, 130); + }, 110); + }, 100); + }, 70); + }, 50); + }) + .catch((err) => done(err)); + }); - setTimeout(() => { - const panelBody = formWithDate.element.querySelector('.panel-body'); - assert.equal(!!panelBody, false); + it('Should not show validation errors when saving invalid draft row in dataGrid', function (done) { + const formElement = document.createElement('div'); + const formWithDraftModals = new Webform(formElement); - formWithDate.element.querySelector('.formio-collapse-icon').dispatchEvent(clickEvent); + formWithDraftModals + .setForm(formWithEditGridModalDrafts) + .then(() => { + const clickEvent = new Event('click'); + const inputEvent = new Event('input'); - setTimeout(() => { - const dateTimeCompAfterOpenningPanel = formWithDate.element.querySelector('.formio-component-dateTime1').querySelector('[type="text"]'); - const textFieldDateCompAfterOpenningPanel = formWithDate.element.querySelector('.formio-component-textField1').querySelector('[type="text"]'); + const addRowBtn = formWithDraftModals.element.querySelector( + '[ref="editgrid-editGrid-addRow"]', + ); + //click to open row in modal view + addRowBtn.dispatchEvent(clickEvent); - const dateTimeCompAltInputAfterOpenningPanel = formWithDate.element.querySelector('.formio-component-dateTime1').querySelector('.flatpickr-input'); - const textFieldDateCompAltInputAfterOpenningPanel = formWithDate.element.querySelector('.formio-component-textField1').querySelector('.flatpickr-input'); + setTimeout(() => { + const rowModal = document.querySelector( + '.formio-dialog-content', + ); + //checking if row modal was openned + assert.equal(!!rowModal, true); + + const textFieldInput = rowModal.querySelector( + '[name="data[editGrid][0][textField]"]', + ); + textFieldInput.value = 'test'; + //input value in one of required row fields + textFieldInput.dispatchEvent(inputEvent); - assert.equal(dateTimeCompAltInputAfterOpenningPanel.value,'2020-05-05T00:00:00'); - assert.equal(textFieldDateCompAltInputAfterOpenningPanel.value,'2020-04-04T00:00:00'); + setTimeout(() => { + //checking if the value was set inside the field + assert.equal(textFieldInput.value, 'test'); + + const saveModalBtn = + rowModal.querySelector('.btn-primary'); + //clicking save button to save row draft + saveModalBtn.dispatchEvent(clickEvent); + + setTimeout(() => { + const editGridRows = + formWithDraftModals.element.querySelectorAll( + '[ref="editgrid-editGrid-row"]', + ); + //checking if the editGrid row was created + assert.equal(editGridRows.length, 1); + const rowError = formWithDraftModals.element + .querySelector('.editgrid-row-error') + .textContent.trim(); + const editGridError = formWithDraftModals.element + .querySelector('[ref="messageContainer"]') + .querySelector('.error'); + + assert.equal(!!rowError, false); + assert.equal(!!editGridError, false); + + done(); + }, 200); + }, 100); + }, 50); + }) + .catch((err) => done(err)); + }); - assert.equal(dateTimeCompAfterOpenningPanel.value,'05-05-2020'); - assert.equal(textFieldDateCompAfterOpenningPanel.value,'04-04-2020'); - done(); - }, 250); - }, 150); - }, 50); - }).catch((err) => done(err)); - }); + it('Should show dataGrid rows when viewing submission in dataGrid with initEmpty option', function (done) { + const formElement = document.createElement('div'); + const formWithDataGridInitEmptyOption = new Webform(formElement); - it(`Should show confirmation alert when clicking X btn or clicking outside modal window after editing - editGrid modal draft row`, function(done) { - const formElement = document.createElement('div'); - const formWithNestedDraftModals = new Webform(formElement); + formWithDataGridInitEmptyOption + .setForm(formWithDataGridInitEmpty.form) + .then(() => { + formWithDataGridInitEmptyOption.setSubmission( + formWithDataGridInitEmpty.submission2, + ); - formWithNestedDraftModals.setForm(formWithEditGridAndNestedDraftModalRow).then(() => { - const editGrid = formWithNestedDraftModals.getComponent('editGrid'); - const clickEvent = new Event('click'); - const inputEvent = new Event('input'); + setTimeout(() => { + const dataGridRows = + formWithDataGridInitEmptyOption.element.querySelectorAll( + '[ref = "datagrid-dataGrid-row"]', + ); + const dataGrid1Rows = + formWithDataGridInitEmptyOption.element.querySelectorAll( + '[ref = "datagrid-dataGrid1-row"]', + ); + + assert.equal(dataGrid1Rows.length, 1); + assert.equal(dataGridRows.length, 1); + + formWithDataGridInitEmptyOption.setSubmission( + formWithDataGridInitEmpty.submission3, + ); - const addRowBtn = formWithNestedDraftModals.element.querySelector( '[ref="editgrid-editGrid-addRow"]'); - //click to open row in modal view - addRowBtn.dispatchEvent(clickEvent); + setTimeout(() => { + const dataGridRows1 = + formWithDataGridInitEmptyOption.element.querySelectorAll( + '[ref = "datagrid-dataGrid-row"]', + ); + const dataGrid1Rows1 = + formWithDataGridInitEmptyOption.element.querySelectorAll( + '[ref = "datagrid-dataGrid1-row"]', + ); + const dataGridSecondRowComponentValue = + formWithDataGridInitEmptyOption.element.querySelector( + '[name = "data[dataGrid][1][textField]"]', + ); + const dataGrid1FirstRowComponentValue = + formWithDataGridInitEmptyOption.element.querySelector( + '[name = "data[dataGrid1][0][textArea]"]', + ); + const dataGrid1SecondRowComponentValue = + formWithDataGridInitEmptyOption.element.querySelector( + '[name = "data[dataGrid1][1][number]"]', + ); + + assert.equal(dataGrid1Rows1.length, 2); + assert.equal(dataGridRows1.length, 2); + assert.equal( + dataGridSecondRowComponentValue.value, + 'test2', + ); + assert.equal( + dataGrid1FirstRowComponentValue.textContent, + 'test3', + ); + assert.equal( + dataGrid1SecondRowComponentValue.value, + 222, + ); + + done(); + }, 300); + }, 200); + }) + .catch((err) => done(err)); + }); - setTimeout(() => { - const rowModal = document.querySelector(`.editgrid-row-modal-${editGrid.id}`); - //checking if row modal was openned - assert.equal(!!rowModal, true); + it('Should not show dataGrid rows when empty submission is set for dataGrid with initEmpty', function (done) { + const formElement = document.createElement('div'); + const formWithDataGridInitEmptyOption = new Webform(formElement); - const textField = rowModal.querySelector('[name="data[textField]"]'); - textField.value = 'test'; - //input value - textField.dispatchEvent(inputEvent); + formWithDataGridInitEmptyOption + .setForm(formWithDataGridInitEmpty.form) + .then(() => { + formWithDataGridInitEmptyOption.setSubmission( + formWithDataGridInitEmpty.submission1, + ); - setTimeout(() => { - //checking if the value was set inside the field - assert.equal(textField.value, 'test'); + setTimeout(() => { + const dataGridRows = + formWithDataGridInitEmptyOption.element.querySelectorAll( + '[ref = "datagrid-dataGrid-row"]', + ); + const dataGrid1Rows = + formWithDataGridInitEmptyOption.element.querySelectorAll( + '[ref = "datagrid-dataGrid1-row"]', + ); + + assert.equal(dataGridRows.length, 0); + assert.equal(dataGrid1Rows.length, 0); + + formWithDataGridInitEmptyOption.setSubmission({ data: {} }); + setTimeout(() => { + const dataGridRows1 = + formWithDataGridInitEmptyOption.element.querySelectorAll( + '[ref = "datagrid-dataGrid-row"]', + ); + const dataGrid1Rows1 = + formWithDataGridInitEmptyOption.element.querySelectorAll( + '[ref = "datagrid-dataGrid1-row"]', + ); + + assert.equal(dataGridRows1.length, 0); + assert.equal(dataGrid1Rows1.length, 0); + + done(); + }, 300); + }, 200); + }) + .catch((err) => done(err)); + }); - const saveModalBtn = rowModal.querySelector('.btn-primary'); - //clicking save button to save row draft - saveModalBtn.dispatchEvent(clickEvent); + it('Should show address submission data inside dataGrid', function (done) { + const formElement = document.createElement('div'); + const formWithAddress = new Webform(formElement); - setTimeout(() => { - const editGridRows = formWithNestedDraftModals.element.querySelectorAll('[ref="editgrid-editGrid-row"]'); - //checking if the editGrid row was created - assert.equal(editGridRows.length, 1); + formWithAddress + .setForm(formWithAddressComponent.form) + .then(() => { + formWithAddress.setSubmission({ + data: formWithAddressComponent.submission, + }); - const editRowBtn = editGridRows[0].querySelector('.editRow'); - //click the edit btn to open the row again - editRowBtn.dispatchEvent(clickEvent); + setTimeout(() => { + const addressInput = formWithAddress.element.querySelector( + '[name = "data[dataGrid][0][address]"]', + ); - setTimeout(() => { - const rowModalForEditing = document.querySelector(`.editgrid-row-modal-${editGrid.id}`); - const textFieldInputForEditing = rowModalForEditing.querySelector('[name="data[textField]"]'); - textFieldInputForEditing.value = 'changed value'; - //changing textfield value - textFieldInputForEditing.dispatchEvent(inputEvent); + assert.equal( + addressInput.value, + formWithAddressComponent.submission.dataGrid[0].address[ + 'formatted_address' + ], + ); - setTimeout(() => { - //checking if the textfield value was changed - const inputValue = textFieldInputForEditing.value; - assert.equal(inputValue, 'changed value'); + done(); + }, 300); + }) + .catch((err) => done(err)); + }); - const XCloseBtn = rowModalForEditing.querySelector('[ref="dialogClose"]'); - //clicking modal close btn - XCloseBtn.dispatchEvent(clickEvent); + it('Should validate field on blur inside panel', function (done) { + const formElement = document.createElement('div'); + const formWithBlurValidation = new Webform(formElement); + + formWithBlurValidation + .setForm(formWithBlurValidationInsidePanel) + .then(() => { + const inputEvent = new Event('input'); + const focusEvent = new Event('focus'); + const blurEvent = new Event('blur'); + const fieldWithBlurValidation = + formWithBlurValidation.element.querySelector( + '[name="data[textField]"]', + ); + + fieldWithBlurValidation.dispatchEvent(focusEvent); + 'test'.split('').forEach((character) => { + fieldWithBlurValidation.value = + fieldWithBlurValidation.value + character; + fieldWithBlurValidation.dispatchEvent(inputEvent); + }); setTimeout(() => { - const dialogConfirmationWindows = document.querySelectorAll(`.editgrid-row-modal-confirmation-${editGrid.id}`); - //checking if confirmation dialog is openned - assert.equal(dialogConfirmationWindows.length, 1); - - const dialogCancelBtn = dialogConfirmationWindows[0].querySelector('[ref="dialogCancelButton"]'); - //closing confirmation dialog - dialogCancelBtn.dispatchEvent(clickEvent); + const validationErrorBeforeBlur = + formWithBlurValidation.element.querySelector('.error'); + assert.equal(!!validationErrorBeforeBlur, false); + assert.equal(formWithBlurValidation.data.textField, 'test'); - setTimeout(() => { - const confirmationWindows = document.querySelectorAll(`.editgrid-row-modal-confirmation-${editGrid.id}`); - //checking if confirmation dialig is closed - assert.equal(confirmationWindows.length, 0); - - const dialog = document.querySelector(`.editgrid-row-modal-${editGrid.id}`); - const overlay = dialog.querySelector('[ref="dialogOverlay"]'); - //clocking model overlay to open confirmation dialog again - overlay.dispatchEvent(clickEvent); + fieldWithBlurValidation.dispatchEvent(blurEvent); setTimeout(() => { - const confirmationDialogsAfterClickingOverlay = document.querySelectorAll(`.editgrid-row-modal-confirmation-${editGrid.id}`); - assert.equal(confirmationDialogsAfterClickingOverlay.length, 1); - - document.body.innerHTML = ''; - done(); - }, 190); - }, 170); - }, 150); - }, 130); - }, 110); - }, 100); - }, 70); - }, 50); - }).catch((err) => done(err)); - }); - - it('Should not show validation errors when saving invalid draft row in dataGrid', function(done) { - const formElement = document.createElement('div'); - const formWithDraftModals = new Webform(formElement); - - formWithDraftModals.setForm(formWithEditGridModalDrafts).then(() => { - const clickEvent = new Event('click'); - const inputEvent = new Event('input'); - - const addRowBtn = formWithDraftModals.element.querySelector( '[ref="editgrid-editGrid-addRow"]'); - //click to open row in modal view - addRowBtn.dispatchEvent(clickEvent); - - setTimeout(() => { - const rowModal = document.querySelector('.formio-dialog-content'); - //checking if row modal was openned - assert.equal(!!rowModal, true); - - const textFieldInput = rowModal.querySelector('[name="data[editGrid][0][textField]"]'); - textFieldInput.value = 'test'; - //input value in one of required row fields - textFieldInput.dispatchEvent(inputEvent); - - setTimeout(() => { - //checking if the value was set inside the field - assert.equal(textFieldInput.value, 'test'); + const validationErrorAfterBlur = + formWithBlurValidation.element.querySelector( + '.error', + ); + + assert.equal(!!validationErrorAfterBlur, true); + assert.equal( + validationErrorAfterBlur.textContent, + 'Text Field must have at least 5 characters.', + ); + + done(); + }, 350); + }, 300); + }) + .catch((err) => done(err)); + }); - const saveModalBtn = rowModal.querySelector('.btn-primary'); - //clicking save button to save row draft - saveModalBtn.dispatchEvent(clickEvent); + it('Should submit form with empty time field when time field is not required', function (done) { + const formElement = document.createElement('div'); + const formWithTime = new Webform(formElement); - setTimeout(() => { - const editGridRows = formWithDraftModals.element.querySelectorAll( '[ref="editgrid-editGrid-row"]'); - //checking if the editGrid row was created - assert.equal(editGridRows.length, 1); - const rowError = formWithDraftModals.element.querySelector('.editgrid-row-error').textContent.trim(); - const editGridError = formWithDraftModals.element.querySelector('[ref="messageContainer"]').querySelector('.error'); + formWithTime + .setForm(formWithTimeComponent) + .then(() => { + const clickEvent = new Event('click'); + const submitBtn = formWithTime.element.querySelector( + '[name="data[submit]"]', + ); - assert.equal(!!rowError, false); - assert.equal(!!editGridError, false); + submitBtn.dispatchEvent(clickEvent); - done(); - }, 200); - }, 100); - }, 50); - }).catch((err) => done(err)); - }); + setTimeout(() => { + assert.equal(formWithTime.errors.length, 0); + assert.equal(formWithTime.data.submit, true); - it('Should show dataGrid rows when viewing submission in dataGrid with initEmpty option', function(done) { - const formElement = document.createElement('div'); - const formWithDataGridInitEmptyOption = new Webform(formElement); + done(); + }, 200); + }) + .catch((err) => done(err)); + }); - formWithDataGridInitEmptyOption.setForm(formWithDataGridInitEmpty.form).then(() => { - formWithDataGridInitEmptyOption.setSubmission(formWithDataGridInitEmpty.submission2); + it(`Should show validation errors and update validation errors list when openning and editing edit grid rows + in draft modal mode after pushing submit btn`, function (done) { + const formElement = document.createElement('div'); + const formWithDraftModals = new Webform(formElement, { + sanitize: true, + }); - setTimeout(() => { - const dataGridRows = formWithDataGridInitEmptyOption.element.querySelectorAll('[ref = "datagrid-dataGrid-row"]'); - const dataGrid1Rows = formWithDataGridInitEmptyOption.element.querySelectorAll('[ref = "datagrid-dataGrid1-row"]'); + formWithDraftModals + .setForm(formWithEditGridModalDrafts) + .then(() => { + const clickEvent = new Event('click'); + const inputEvent = new Event('input'); - assert.equal(dataGrid1Rows.length, 1); - assert.equal(dataGridRows.length, 1); + const addRowBtn = formWithDraftModals.element.querySelector( + '[ref="editgrid-editGrid-addRow"]', + ); + //click to open row in modal view + addRowBtn.dispatchEvent(clickEvent); - formWithDataGridInitEmptyOption.setSubmission(formWithDataGridInitEmpty.submission3); + setTimeout(() => { + const editGrid = + formWithDraftModals.getComponent('editGrid'); + + assert.equal( + editGrid.editRows.length, + 1, + 'Should create a row', + ); + + const rowModal = editGrid.editRows[0].dialog; + //checking if row modal was openned + assert.equal( + !!rowModal, + true, + 'Should open a modal window', + ); + + const textFieldInput = rowModal.querySelector( + '[name="data[editGrid][0][textField]"]', + ); + textFieldInput.value = 'test'; + //input value in one of required row fields + textFieldInput.dispatchEvent(inputEvent); - setTimeout(() => { - const dataGridRows1 = formWithDataGridInitEmptyOption.element.querySelectorAll('[ref = "datagrid-dataGrid-row"]'); - const dataGrid1Rows1 = formWithDataGridInitEmptyOption.element.querySelectorAll('[ref = "datagrid-dataGrid1-row"]'); - const dataGridSecondRowComponentValue = formWithDataGridInitEmptyOption.element.querySelector('[name = "data[dataGrid][1][textField]"]'); - const dataGrid1FirstRowComponentValue = formWithDataGridInitEmptyOption.element.querySelector('[name = "data[dataGrid1][0][textArea]"]'); - const dataGrid1SecondRowComponentValue = formWithDataGridInitEmptyOption.element.querySelector('[name = "data[dataGrid1][1][number]"]'); + setTimeout(() => { + //checking if the value was set inside the field + assert.equal(textFieldInput.value, 'test'); + + const saveModalBtn = + rowModal.querySelector('.btn-primary'); + //clicking save button to save row draft + saveModalBtn.dispatchEvent(clickEvent); + + setTimeout(() => { + const editGridRows = + formWithDraftModals.element.querySelectorAll( + '[ref="editgrid-editGrid-row"]', + ); + //checking if the editGrid row was created + assert.equal(editGridRows.length, 1); + + const submitBtn = + formWithDraftModals.element.querySelector( + '[name="data[submit]"]', + ); + //pushing submit button to trigger validation + submitBtn.dispatchEvent(clickEvent); + + setTimeout(() => { + //checking the number of appeared errors + assert.equal( + formWithDraftModals.errors.length, + 2, + ); + + const rowError = formWithDraftModals.element + .querySelector('.editgrid-row-error') + .textContent.trim(); + const editGridError = + formWithDraftModals.element + .querySelector( + '[ref="messageContainer"]', + ) + .querySelector('.error').textContent; + //checking if right errors were shown in right places + assert.equal( + rowError, + 'Invalid row. Please correct it or delete.', + ); + assert.equal( + editGridError, + 'Please correct invalid rows before proceeding.', + ); + + const rowEditBtn = + editGridRows[0].querySelector('.editRow'); + //open row modal again to check if there are errors + rowEditBtn.dispatchEvent(clickEvent); + + setTimeout(() => { + const rowModalAfterValidation = + editGrid.editRows[0].dialog; + + const alertWithErrorText = + rowModalAfterValidation.querySelector( + '.alert-danger', + ); + //checking if alert with errors list appeared inside the modal + assert.equal( + !!alertWithErrorText, + true, + 'Should show error alert', + ); + + const alertErrorMessages = + rowModalAfterValidation.querySelectorAll( + '[ref="messageRef"]', + ); + assert.equal(alertErrorMessages.length, 1); + + const numberComponentError = + rowModalAfterValidation + .querySelector( + '.formio-component-number', + ) + .querySelector( + '.error', + ).textContent; + //checking if error was shown for empty required field + assert.equal( + numberComponentError, + 'Number is required', + ); + + const numberInput = + rowModalAfterValidation.querySelector( + '[name="data[editGrid][0][number]"]', + ); + numberInput.value = 123; + //input value to make the field valid + numberInput.dispatchEvent(inputEvent); + + setTimeout(() => { + const rowModalWithValidFields = + document.querySelector( + `.editgrid-row-modal-${editGrid.id}`, + ); + const alertErrorMessagesAfterInputtingValidValues = + rowModalWithValidFields.querySelectorAll( + '[ref="messageRef"]', + ); + assert.equal( + alertErrorMessagesAfterInputtingValidValues.length, + 0, + ); + + //input values to make all row fields invalid + const validNumberInput = + rowModalWithValidFields.querySelector( + '[name="data[editGrid][0][number]"]', + ); + validNumberInput.value = null; + validNumberInput.dispatchEvent( + inputEvent, + ); + + const validTextInput = + rowModalWithValidFields.querySelector( + '[name="data[editGrid][0][textField]"]', + ); + validTextInput.value = ''; + validTextInput.dispatchEvent( + inputEvent, + ); + + setTimeout(() => { + const alertErrorMessagesAfterInputtingInvalidValues = + document + .querySelector( + `.editgrid-row-modal-${editGrid.id}`, + ) + .querySelectorAll( + '[ref="messageRef"]', + ); + + assert.equal( + alertErrorMessagesAfterInputtingInvalidValues.length, + 2, + ); + document.body.innerHTML = ''; + + done(); + }, 280); + }, 240); + }, 200); + }, 160); + }, 120); + }, 80); + }, 50); + }) + .catch((err) => done(err)); + }); - assert.equal(dataGrid1Rows1.length, 2); - assert.equal(dataGridRows1.length, 2); - assert.equal(dataGridSecondRowComponentValue.value, 'test2'); - assert.equal(dataGrid1FirstRowComponentValue.textContent, 'test3'); - assert.equal(dataGrid1SecondRowComponentValue.value, 222); + it('Should not override calculated value', function (done) { + const formElement = document.createElement('div'); + const formWithCalculatedAmount = new Webform(formElement); - done(); - }, 300); - }, 200); - }) - .catch((err) => done(err)); - }); + formWithCalculatedAmount + .setForm(formWithCalculatedValueWithoutOverriding) + .then(() => { + const inputEvent = new Event('input'); - it('Should not show dataGrid rows when empty submission is set for dataGrid with initEmpty', function(done) { - const formElement = document.createElement('div'); - const formWithDataGridInitEmptyOption = new Webform(formElement); + const amountInput1 = + formWithCalculatedAmount.element.querySelector( + '[name="data[amount1]"]', + ); + const amountInput2 = + formWithCalculatedAmount.element.querySelector( + '[name="data[amount2]"]', + ); - formWithDataGridInitEmptyOption.setForm(formWithDataGridInitEmpty.form).then(() => { - formWithDataGridInitEmptyOption.setSubmission(formWithDataGridInitEmpty.submission1); + amountInput1.value = 6; + amountInput2.value = 4; - setTimeout(() => { - const dataGridRows = formWithDataGridInitEmptyOption.element.querySelectorAll('[ref = "datagrid-dataGrid-row"]'); - const dataGrid1Rows = formWithDataGridInitEmptyOption.element.querySelectorAll('[ref = "datagrid-dataGrid1-row"]'); + amountInput1.dispatchEvent(inputEvent); + amountInput2.dispatchEvent(inputEvent); - assert.equal(dataGridRows.length, 0); - assert.equal(dataGrid1Rows.length, 0); + setTimeout(() => { + const totalAmountInput = + formWithCalculatedAmount.element.querySelector( + '[name="data[currency]"]', + ); + //checking if the value was calculated correctly + assert.equal(totalAmountInput.value, '$10.00'); + + const inputEvent = new Event('input'); + //trying to override calculated value + totalAmountInput.value = 55; + totalAmountInput.dispatchEvent(inputEvent); - formWithDataGridInitEmptyOption.setSubmission({ data: {} }); - setTimeout(() => { - const dataGridRows1 = formWithDataGridInitEmptyOption.element.querySelectorAll('[ref = "datagrid-dataGrid-row"]'); - const dataGrid1Rows1 = formWithDataGridInitEmptyOption.element.querySelectorAll('[ref = "datagrid-dataGrid1-row"]'); + setTimeout(() => { + const totalAmountInput = + formWithCalculatedAmount.element.querySelector( + '[name="data[currency]"]', + ); + //checking if the value was overridden + assert.equal(totalAmountInput.value, '$10.00'); + + done(); + }, 400); + }, 300); + }) + .catch((err) => done(err)); + }); - assert.equal(dataGridRows1.length, 0); - assert.equal(dataGrid1Rows1.length, 0); + it('Should modify calculated value only if it was not manually modified when allowCalculateOverride is true', function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement); - done(); - }, 300); - }, 200); - }) - .catch((err) => done(err)); - }); + form.setForm(formWithAllowCalculateOverride) + .then(() => { + const labelComp = form.getComponent('label'); + const valueComp = form.getComponent('value'); - it('Should show address submission data inside dataGrid', function(done) { - const formElement = document.createElement('div'); - const formWithAddress = new Webform(formElement); + const inputEvent = new Event('input'); + const labelInput = labelComp.refs.input[0]; + const valueInput = valueComp.refs.input[0]; + labelInput.value = 'Hello'; + labelInput.dispatchEvent(inputEvent); - formWithAddress.setForm(formWithAddressComponent.form).then(() => { - formWithAddress.setSubmission({ data: formWithAddressComponent.submission }); + setTimeout(() => { + assert.equal(labelComp.dataValue, 'Hello'); + assert.equal(valueComp.dataValue, 'hello'); - setTimeout(() => { - const addressInput = formWithAddress.element.querySelector('[name = "data[dataGrid][0][address]"]'); + valueInput.value = 'hello123'; + valueInput.dispatchEvent(inputEvent); - assert.equal(addressInput.value, formWithAddressComponent.submission.dataGrid[0].address['formatted_address']); + setTimeout(() => { + assert.equal(valueComp.dataValue, 'hello123'); + + labelInput.value = 'HeLLo World'; + labelInput.dispatchEvent(inputEvent); + + setTimeout(() => { + assert.equal(labelComp.dataValue, 'HeLLo World'); + assert.equal(valueComp.dataValue, 'hello123'); + done(); + }, 500); + }, 500); + }, 500); + }) + .catch(done); + }); - done(); - }, 300); - }) - .catch((err) => done(err)); - }); + it(`Should show field only in container where radio component has 'yes' value when containers contain radio + components with the same key`, function (done) { + const formElement = document.createElement('div'); + const formWithCondition = new Webform(formElement); + + formWithCondition + .setForm(formWithConditionalLogic) + .then(() => { + Harness.clickElement( + formWithCondition, + formWithCondition.element + .querySelector('.formio-component-container1') + .querySelector('[value="yes"]'), + ); - it('Should validate field on blur inside panel', function(done) { - const formElement = document.createElement('div'); - const formWithBlurValidation = new Webform(formElement); + setTimeout(() => { + const conditionalFieldInContainer1 = + formWithCondition.element.querySelector( + '[name="data[container1][textField]"]', + ); + const conditionalFieldInContainer2 = + formWithCondition.element.querySelector( + '[name="data[container2][textField]"]', + ); + + assert.equal(!!conditionalFieldInContainer1, true); + assert.equal(!!conditionalFieldInContainer2, false); - formWithBlurValidation.setForm(formWithBlurValidationInsidePanel).then(() => { - const inputEvent = new Event('input'); - const focusEvent = new Event('focus'); - const blurEvent = new Event('blur'); - const fieldWithBlurValidation = formWithBlurValidation.element.querySelector('[name="data[textField]"]'); + done(); + }, 400); + }) + .catch((err) => done(err)); + }); - fieldWithBlurValidation.dispatchEvent(focusEvent); - 'test'.split('').forEach(character => { - fieldWithBlurValidation.value = fieldWithBlurValidation.value + character; - fieldWithBlurValidation.dispatchEvent(inputEvent); - }); + it('Should show only "required field" error when submitting empty required field with pattern validation', function (done) { + const formElement = document.createElement('div'); + const formWithPattern = new Webform(formElement); - setTimeout(() => { - const validationErrorBeforeBlur = formWithBlurValidation.element.querySelector('.error'); - assert.equal(!!validationErrorBeforeBlur, false); - assert.equal(formWithBlurValidation.data.textField, 'test'); + formWithPattern + .setForm(formWithPatternValidation) + .then(() => { + Harness.clickElement( + formWithPattern, + formWithPattern.element.querySelector( + '[name="data[submit]"]', + ), + ); - fieldWithBlurValidation.dispatchEvent(blurEvent); + setTimeout(() => { + assert.equal( + formWithPattern.element + .querySelector('.formio-component-textField') + .querySelectorAll('.error').length, + 1, + ); + assert.equal(formWithPattern.errors[0].messages.length, 1); + assert.equal( + formWithPattern.errors[0].messages[0].message, + 'Text Field is required', + ); + assert.equal( + formWithPattern.element + .querySelector('[ref="errorRef"]') + .textContent.trim(), + 'Text Field is required', + ); + done(); + }, 500); + }) + .catch((err) => done(err)); + }); - setTimeout(() => { - const validationErrorAfterBlur = formWithBlurValidation.element.querySelector('.error'); + it('Should disable field applying advanced logic if dot is used inside component key', function (done) { + const formElement = document.createElement('div'); + const formWithLogic = new Webform(formElement); - assert.equal(!!validationErrorAfterBlur, true); - assert.equal(validationErrorAfterBlur.textContent, 'Text Field must have at least 5 characters.'); + formWithLogic + .setForm(formWithAdvancedLogic) + .then(() => { + assert.equal(formWithLogic.components[1].disabled, false); - done(); - }, 350); - }, 300); - }) - .catch((err) => done(err)); - }); + Harness.clickElement( + formWithLogic, + formWithLogic.element.querySelector( + '[name="data[requestedCovers.HOUSECONTENT_JEWELRY]"]', + ), + ); - it('Should submit form with empty time field when time field is not required', function(done) { - const formElement = document.createElement('div'); - const formWithTime = new Webform(formElement); + setTimeout(() => { + assert.equal(formWithLogic.components[1].disabled, true); + done(); + }, 500); + }) + .catch((err) => done(err)); + }); - formWithTime.setForm(formWithTimeComponent).then(() => { - const clickEvent = new Event('click'); - const submitBtn = formWithTime.element.querySelector('[name="data[submit]"]'); + it('Should only scroll to alerts dialog when submitting an invalid form', function (done) { + const formJson = { + components: [ + { + label: 'Number', + inputFormat: 'plain', + validate: { + required: true, + max: 10, + }, + key: 'number', + type: 'number', + input: true, + }, + { + label: 'Submit', + showValidations: false, + tableView: false, + key: 'submit', + type: 'button', + input: true, + saveOnEnter: false, + }, + ], + }; - submitBtn.dispatchEvent(clickEvent); + const formElement = document.createElement('div'); + const form = new Webform(formElement); + const scrollIntoView = sinon.spy(form, 'scrollIntoView'); - setTimeout(() => { - assert.equal(formWithTime.errors.length, 0); - assert.equal(formWithTime.data.submit, true); + form.setForm(formJson) + .then(() => { + Harness.clickElement( + form, + form.element.querySelector('[name="data[submit]"]'), + ); - done(); - }, 200); - }) - .catch((err) => done(err)); - }); + setTimeout(() => { + assert.equal(form.errors[0].messages.length, 1); + assert(scrollIntoView.calledOnceWith(form.root.alert)); - it(`Should show validation errors and update validation errors list when openning and editing edit grid rows - in draft modal mode after pushing submit btn`, function(done) { - const formElement = document.createElement('div'); - const formWithDraftModals = new Webform(formElement, { sanitize: true }); + //changes do not trigger scrolling + const inputEvent = new Event('input'); + const input1 = form.components[0].refs.input[0]; - formWithDraftModals.setForm(formWithEditGridModalDrafts).then(() => { - const clickEvent = new Event('click'); - const inputEvent = new Event('input'); + //invalid input value + input1.value = 55; + input1.dispatchEvent(inputEvent); - const addRowBtn = formWithDraftModals.element.querySelector( '[ref="editgrid-editGrid-addRow"]'); - //click to open row in modal view - addRowBtn.dispatchEvent(clickEvent); + setTimeout(() => { + assert.equal(form.errors[0].messages.length, 1); + assert.equal(scrollIntoView.callCount, 1); + + //valid input value + input1.value = 5; + input1.dispatchEvent(inputEvent); + + setTimeout(() => { + assert.equal(form.errors.length, 0); + assert.equal(scrollIntoView.callCount, 1); + done(); + }, 250); + }, 250); + }, 250); + }) + .catch((err) => done(err)); + }); - setTimeout(() => { - const editGrid = formWithDraftModals.getComponent('editGrid'); + let formWithCalculatedValue; + + it('Should calculate the field value after validation errors appeared on submit', function (done) { + const formElement = document.createElement('div'); + formWithCalculatedValue = new Webform(formElement); + formWithCalculatedValue + .setForm(manualOverride) + .then(() => { + Harness.clickElement( + formWithCalculatedValue, + formWithCalculatedValue.components[2].refs.button, + ); + setTimeout(() => { + const inputEvent = new Event('input'); + const input1 = + formWithCalculatedValue.components[0].refs.input[0]; - assert.equal(editGrid.editRows.length, 1, 'Should create a row'); + input1.value = 55; + input1.dispatchEvent(inputEvent); - const rowModal = editGrid.editRows[0].dialog; - //checking if row modal was openned - assert.equal(!!rowModal, true, 'Should open a modal window'); + setTimeout(() => { + const input2 = formElement.querySelector( + 'input[name="data[number2]"]', + ); + assert.equal(input2.value, '55'); + assert.equal(input1.value, 55); + done(); + }, 250); + }, 250); + }) + .catch((err) => done(err)); + }); - const textFieldInput = rowModal.querySelector('[name="data[editGrid][0][textField]"]'); - textFieldInput.value = 'test'; - //input value in one of required row fields - textFieldInput.dispatchEvent(inputEvent); + it('Should calculate the value when editing set values with possibility of manual override', function (done) { + const formElement = document.createElement('div'); + formWithCalculatedValue = new Webform(formElement); + formWithCalculatedValue.setForm(manualOverride).then(() => { + formWithCalculatedValue + .setSubmission({ + data: { + number1: 66, + number2: 66, + }, + }) + .then(() => { + setTimeout(() => { + const input1 = formElement.querySelector( + 'input[name="data[number1]"]', + ); + const input2 = formElement.querySelector( + 'input[name="data[number2]"]', + ); + + assert.equal(input2.value, '66'); + assert.equal(input1.value, 66); + + const inputEvent = new Event('input'); + + input1.value = `${input1.value}` + '78'; + input1.dispatchEvent(inputEvent); + + setTimeout(() => { + assert.equal(input2.value, '66'); + assert.equal(input1.value, 6678); + //set a number as calculated value + formWithCalculatedValue.components[1].calculatedValue = 6678; + //change the value + input1.value = +(`${input1.value}` + '90'); + input1.dispatchEvent(inputEvent); + + setTimeout(() => { + assert.equal(input2.value, '66'); + assert.equal(input1.value, 667890); + done(); + }, 250); + }, 250); + }, 900); + }); + }); + }); - setTimeout(() => { - //checking if the value was set inside the field - assert.equal(textFieldInput.value, 'test'); + let simpleForm = null; - const saveModalBtn = rowModal.querySelector('.btn-primary'); - //clicking save button to save row draft - saveModalBtn.dispatchEvent(clickEvent); + it('Should create a simple form', function (done) { + const formElement = document.createElement('div'); + simpleForm = new Webform(formElement); + simpleForm + .setForm({ + title: 'Simple Form', + components: [ + { + type: 'textfield', + key: 'firstName', + input: true, + }, + { + type: 'textfield', + key: 'lastName', + input: true, + }, + ], + }) + .then(() => { + Harness.testElements(simpleForm, 'input[type="text"]', 2); + Harness.testElements( + simpleForm, + 'input[name="data[firstName]"]', + 1, + ); + Harness.testElements( + simpleForm, + 'input[name="data[lastName]"]', + 1, + ); + done(); + }) + .catch(done); + }); - setTimeout(() => { - const editGridRows = formWithDraftModals.element.querySelectorAll( '[ref="editgrid-editGrid-row"]'); - //checking if the editGrid row was created - assert.equal(editGridRows.length, 1); + it('Should set a submission to the form.', function () { + Harness.testSubmission(simpleForm, { + data: { + firstName: 'Joe', + lastName: 'Smith', + }, + }); + }); - const submitBtn = formWithDraftModals.element.querySelector('[name="data[submit]"]'); - //pushing submit button to trigger validation - submitBtn.dispatchEvent(clickEvent); + it('Should translate a form from options', function (done) { + const formElement = document.createElement('div'); + const translateForm = new Webform(formElement, { + language: 'es', + i18n: { + es: { + 'Default Label': 'Spanish Label', + }, + }, + }); + translateForm + .setForm({ + title: 'Translate Form', + components: [ + { + type: 'textfield', + label: 'Default Label', + key: 'myfield', + input: true, + inputType: 'text', + validate: {}, + }, + ], + }) + .then(() => { + const label = formElement.querySelector('.col-form-label'); + assert.equal(label.innerHTML.trim(), 'Spanish Label'); + done(); + }) + .catch((err) => { + done(err); + }); + }); - setTimeout(() => { - //checking the number of appeared errors - assert.equal(formWithDraftModals.errors.length, 2); + it('Should get the language passed via options', function () { + const formElement = document.createElement('div'); + const form = new Webform(formElement, { + language: 'es', + }); - const rowError = formWithDraftModals.element.querySelector('.editgrid-row-error').textContent.trim(); - const editGridError = formWithDraftModals.element.querySelector('[ref="messageContainer"]').querySelector('.error').textContent; - //checking if right errors were shown in right places - assert.equal(rowError, 'Invalid row. Please correct it or delete.'); - assert.equal(editGridError, 'Please correct invalid rows before proceeding.'); + assert.equal(form.language, 'es'); + }); - const rowEditBtn = editGridRows[0].querySelector('.editRow'); - //open row modal again to check if there are errors - rowEditBtn.dispatchEvent(clickEvent); + it('Should translate form errors in alerts', function () { + const formElement = document.createElement('div'); + const form = new Webform(formElement, { + language: 'es', + i18n: { + es: { + alertMessage: '{{message}}', + required: '{{field}} es obligatorio', + }, + }, + }); - setTimeout(() => { - const rowModalAfterValidation = editGrid.editRows[0].dialog; + return form + .setForm({ + components: [ + { + type: 'textfield', + label: 'Field Label', + key: 'myfield', + input: true, + inputType: 'text', + validate: { + required: true, + }, + }, + ], + }) + .then(() => form.submit()) + .catch(() => { + // console.warn('nooo:', error) + }) + .then(() => { + const ref = formElement.querySelector('[ref="errorRef"]'); + assert.equal( + ref.textContent.trim(), + 'Field Label es obligatorio', + ); + }); + }); - const alertWithErrorText = rowModalAfterValidation.querySelector('.alert-danger'); - //checking if alert with errors list appeared inside the modal - assert.equal(!!alertWithErrorText, true, 'Should show error alert'); - - const alertErrorMessages = rowModalAfterValidation.querySelectorAll('[ref="messageRef"]'); - assert.equal(alertErrorMessages.length, 1); - - const numberComponentError = rowModalAfterValidation.querySelector('.formio-component-number').querySelector('.error').textContent; - //checking if error was shown for empty required field - assert.equal(numberComponentError, 'Number is required'); - - const numberInput = rowModalAfterValidation.querySelector('[name="data[editGrid][0][number]"]'); - numberInput.value = 123; - //input value to make the field valid - numberInput.dispatchEvent(inputEvent); - - setTimeout(() => { - const rowModalWithValidFields = document.querySelector(`.editgrid-row-modal-${editGrid.id}`); - const alertErrorMessagesAfterInputtingValidValues = rowModalWithValidFields.querySelectorAll('[ref="messageRef"]'); - assert.equal(alertErrorMessagesAfterInputtingValidValues.length, 0); - - //input values to make all row fields invalid - const validNumberInput = rowModalWithValidFields.querySelector('[name="data[editGrid][0][number]"]'); - validNumberInput.value = null; - validNumberInput.dispatchEvent(inputEvent); + it('Should translate a form after instantiate', function (done) { + const formElement = document.createElement('div'); + const translateForm = new Webform(formElement, { + i18n: { + es: { + 'Default Label': 'Spanish Label', + }, + }, + }); + translateForm + .setForm({ + title: 'Translate Form', + components: [ + { + type: 'textfield', + label: 'Default Label', + key: 'myfield', + input: true, + inputType: 'text', + validate: {}, + }, + ], + }) + .then(() => { + translateForm.language = 'es'; + const label = formElement.querySelector('.col-form-label'); + assert.equal(label.innerHTML.trim(), 'Spanish Label'); + done(); + }) + .catch(done); + }); - const validTextInput = rowModalWithValidFields.querySelector('[name="data[editGrid][0][textField]"]'); - validTextInput.value = ''; - validTextInput.dispatchEvent(inputEvent); + it('Should add a translation after instantiate', function (done) { + const formElement = document.createElement('div'); + const translateForm = new Webform(formElement, { + i18n: { + language: 'es', + es: { + 'Default Label': 'Spanish Label', + }, + fr: { + 'Default Label': 'French Label', + }, + }, + }); + translateForm + .setForm({ + title: 'Translate Form', + components: [ + { + type: 'textfield', + label: 'Default Label', + key: 'myfield', + input: true, + inputType: 'text', + validate: {}, + }, + ], + }) + .then(() => { + translateForm.language = 'fr'; + const label = formElement.querySelector('.col-form-label'); + assert.equal(label.innerHTML.trim(), 'French Label'); + done(); + }) + .catch(done); + }); - setTimeout(() => { - const alertErrorMessagesAfterInputtingInvalidValues = document - .querySelector(`.editgrid-row-modal-${editGrid.id}`) - .querySelectorAll('[ref="messageRef"]'); + it('Should switch a translation after instantiate', function (done) { + const formElement = document.createElement('div'); + const translateForm = new Webform(formElement); + translateForm + .setForm({ + title: 'Translate Form', + components: [ + { + type: 'textfield', + label: 'Default Label', + key: 'myfield', + input: true, + inputType: 'text', + validate: {}, + }, + ], + }) + .then(() => { + translateForm.addLanguage( + 'es', + { 'Default Label': 'Spanish Label' }, + true, + ); + const label = formElement.querySelector('.col-form-label'); + assert.equal(label.innerHTML.trim(), 'Spanish Label'); + done(); + }) + .catch(done); + }); - assert.equal(alertErrorMessagesAfterInputtingInvalidValues.length, 2); - document.body.innerHTML = ''; + it('Should keep translation after redraw', function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement); + const schema = { + title: 'Translate Form', + components: [ + { + type: 'textfield', + label: 'Default Label', + key: 'myfield', + input: true, + inputType: 'text', + validate: {}, + }, + ], + }; + try { + form.setForm(schema) + .then(() => { + form.addLanguage( + 'ru', + { 'Default Label': 'Russian Label' }, + true, + ); + return (form.language = 'ru'); + }, done) + .then(() => { + expect(form.options.language).to.equal('ru'); + expect( + formElement + .querySelector('.col-form-label') + .innerHTML.trim(), + ).to.equal('Russian Label'); + form.redraw(); + expect(form.options.language).to.equal('ru'); + expect( + formElement + .querySelector('.col-form-label') + .innerHTML.trim(), + ).to.equal('Russian Label'); done(); - }, 280); - }, 240); - }, 200); - }, 160); - }, 120); - }, 80); - }, 50); - }).catch((err) => done(err)); - }); - - it('Should not override calculated value', function(done) { - const formElement = document.createElement('div'); - const formWithCalculatedAmount = new Webform(formElement); - - formWithCalculatedAmount.setForm(formWithCalculatedValueWithoutOverriding).then(() => { - const inputEvent = new Event('input'); - - const amountInput1 = formWithCalculatedAmount.element.querySelector('[name="data[amount1]"]'); - const amountInput2 = formWithCalculatedAmount.element.querySelector('[name="data[amount2]"]'); - - amountInput1.value = 6; - amountInput2.value = 4; - - amountInput1.dispatchEvent(inputEvent); - amountInput2.dispatchEvent(inputEvent); - - setTimeout(() => { - const totalAmountInput = formWithCalculatedAmount.element.querySelector('[name="data[currency]"]'); - //checking if the value was calculated correctly - assert.equal(totalAmountInput.value, '$10.00'); - - const inputEvent = new Event('input'); - //trying to override calculated value - totalAmountInput.value = 55; - totalAmountInput.dispatchEvent(inputEvent); - - setTimeout(() => { - const totalAmountInput = formWithCalculatedAmount.element.querySelector('[name="data[currency]"]'); - //checking if the value was overridden - assert.equal(totalAmountInput.value, '$10.00'); - - done(); - }, 400); - }, 300); - }) - .catch((err) => done(err)); - }); - - it('Should modify calculated value only if it was not manually modified when allowCalculateOverride is true', function(done) { - const formElement = document.createElement('div'); - const form = new Webform(formElement); - - form.setForm(formWithAllowCalculateOverride).then(() => { - const labelComp = form.getComponent('label'); - const valueComp = form.getComponent('value'); - - const inputEvent = new Event('input'); - const labelInput = labelComp.refs.input[0]; - const valueInput = valueComp.refs.input[0]; - labelInput.value = 'Hello'; - labelInput.dispatchEvent(inputEvent); - - setTimeout(() => { - assert.equal(labelComp.dataValue, 'Hello'); - assert.equal(valueComp.dataValue, 'hello'); - - valueInput.value = 'hello123'; - valueInput.dispatchEvent(inputEvent); - - setTimeout(() => { - assert.equal(valueComp.dataValue, 'hello123'); - - labelInput.value = 'HeLLo World'; - labelInput.dispatchEvent(inputEvent); - - setTimeout(() => { - assert.equal(labelComp.dataValue, 'HeLLo World'); - assert.equal(valueComp.dataValue, 'hello123'); - done(); - }, 500); - }, 500); - }, 500); - }).catch(done); - }); - - it(`Should show field only in container where radio component has 'yes' value when containers contain radio - components with the same key`, function(done) { - const formElement = document.createElement('div'); - const formWithCondition = new Webform(formElement); - - formWithCondition.setForm(formWithConditionalLogic).then(() => { - Harness.clickElement(formWithCondition, formWithCondition.element.querySelector('.formio-component-container1').querySelector('[value="yes"]')); - - setTimeout(() => { - const conditionalFieldInContainer1 = formWithCondition.element.querySelector('[name="data[container1][textField]"]'); - const conditionalFieldInContainer2 = formWithCondition.element.querySelector('[name="data[container2][textField]"]'); - - assert.equal(!!conditionalFieldInContainer1, true); - assert.equal(!!conditionalFieldInContainer2, false); - - done(); - }, 400); - }) - .catch((err) => done(err)); - }); - - it('Should show only "required field" error when submitting empty required field with pattern validation', function(done) { - const formElement = document.createElement('div'); - const formWithPattern = new Webform(formElement); - - formWithPattern.setForm(formWithPatternValidation).then(() => { - Harness.clickElement(formWithPattern, formWithPattern.element.querySelector('[name="data[submit]"]')); - - setTimeout(() => { - assert.equal(formWithPattern.element.querySelector('.formio-component-textField').querySelectorAll('.error').length, 1); - assert.equal(formWithPattern.errors[0].messages.length, 1); - assert.equal(formWithPattern.errors[0].messages[0].message, 'Text Field is required'); - assert.equal(formWithPattern.element.querySelector('[ref="errorRef"]').textContent.trim(), 'Text Field is required'); - done(); - }, 500); - }) - .catch((err) => done(err)); - }); - - it('Should disable field applying advanced logic if dot is used inside component key', function(done) { - const formElement = document.createElement('div'); - const formWithLogic = new Webform(formElement); - - formWithLogic.setForm(formWithAdvancedLogic).then(() => { - assert.equal(formWithLogic.components[1].disabled, false); - - Harness.clickElement(formWithLogic, formWithLogic.element.querySelector('[name="data[requestedCovers.HOUSECONTENT_JEWELRY]"]')); - - setTimeout(() => { - assert.equal(formWithLogic.components[1].disabled, true); - done(); - }, 500); - }) - .catch((err) => done(err)); - }); - - it('Should only scroll to alerts dialog when submitting an invalid form', function(done) { - const formJson = { - components: [ - { - 'label': 'Number', - 'inputFormat': 'plain', - 'validate': { - 'required': true, - 'max': 10 - }, - 'key': 'number', - 'type': 'number', - 'input': true - }, - { - label: 'Submit', - showValidations: false, - tableView: false, - key: 'submit', - type: 'button', - input: true, - saveOnEnter: false, + }, done) + .catch(done); + } catch (error) { + done(error); } - ] - }; - - const formElement = document.createElement('div'); - const form = new Webform(formElement); - const scrollIntoView = sinon.spy(form, 'scrollIntoView'); - - form.setForm(formJson).then(() => { - Harness.clickElement(form, form.element.querySelector('[name="data[submit]"]')); - - setTimeout(() => { - assert.equal(form.errors[0].messages.length, 1); - assert(scrollIntoView.calledOnceWith(form.root.alert)); - - //changes do not trigger scrolling - const inputEvent = new Event('input'); - const input1 = form.components[0].refs.input[0]; - - //invalid input value - input1.value = 55; - input1.dispatchEvent(inputEvent); - - setTimeout(() => { - assert.equal(form.errors[0].messages.length, 1); - assert.equal(scrollIntoView.callCount, 1); - - //valid input value - input1.value = 5; - input1.dispatchEvent(inputEvent); - - setTimeout(() => { - assert.equal(form.errors.length, 0); - assert.equal(scrollIntoView.callCount, 1); - done(); - }, 250); - }, 250); - }, 250); - }) - .catch((err) => done(err)); - }); + }); - let formWithCalculatedValue; + it('Should fire languageChanged event when language is set', function (done) { + let isLanguageChangedEventFired = false; + const formElement = document.createElement('div'); + const form = new Webform(formElement); + const schema = { + title: 'Translate Form', + components: [ + { + type: 'textfield', + label: 'Default Label', + key: 'myfield', + input: true, + inputType: 'text', + validate: {}, + }, + ], + }; - it('Should calculate the field value after validation errors appeared on submit', function(done) { - const formElement = document.createElement('div'); - formWithCalculatedValue = new Webform(formElement); - formWithCalculatedValue.setForm(manualOverride).then(() => { - Harness.clickElement(formWithCalculatedValue, formWithCalculatedValue.components[2].refs.button); - setTimeout(() => { - const inputEvent = new Event('input'); - const input1 = formWithCalculatedValue.components[0].refs.input[0]; - - input1.value = 55; - input1.dispatchEvent(inputEvent); - - setTimeout(() => { - const input2 = formElement.querySelector('input[name="data[number2]"]'); - assert.equal(input2.value, '55'); - assert.equal(input1.value, 55); - done(); - }, 250); - }, 250); - }) - .catch((err) => done(err)); - }); - - it('Should calculate the value when editing set values with possibility of manual override', function(done) { - const formElement = document.createElement('div'); - formWithCalculatedValue = new Webform(formElement); - formWithCalculatedValue.setForm(manualOverride).then(() => { - formWithCalculatedValue.setSubmission({ - data:{ - number1: 66, - number2:66 + try { + form.setForm(schema) + .then(() => { + form.addLanguage( + 'ru', + { 'Default Label': 'Russian Label' }, + false, + ); + form.on('languageChanged', () => { + isLanguageChangedEventFired = true; + }); + return (form.language = 'ru'); + }, done) + .then(() => { + assert(isLanguageChangedEventFired); + done(); + }, done) + .catch(done); + } catch (error) { + done(error); } - }).then(()=>{ - setTimeout(()=>{ - const input1 = formElement.querySelector('input[name="data[number1]"]'); - const input2 = formElement.querySelector('input[name="data[number2]"]'); - - assert.equal(input2.value, '66'); - assert.equal(input1.value, 66); - - const inputEvent = new Event('input'); + }); - input1.value = `${input1.value}` + '78'; - input1.dispatchEvent(inputEvent); + it('When submitted should strip fields with persistent: client-only from submission', function (done) { + const formElement = document.createElement('div'); + simpleForm = new Webform(formElement); + /* eslint-disable quotes */ + simpleForm.setForm({ + title: 'Simple Form', + components: [ + { + label: 'Name', + allowMultipleMasks: false, + showWordCount: false, + showCharCount: false, + tableView: true, + type: 'textfield', + input: true, + key: 'name', + widget: { + type: '', + }, + }, + { + label: 'Age', + persistent: 'client-only', + mask: false, + tableView: true, + type: 'number', + input: true, + key: 'age', + }, + ], + }); + /* eslint-enable quotes */ - setTimeout(() => { - assert.equal(input2.value, '66'); - assert.equal(input1.value, 6678); - //set a number as calculated value - formWithCalculatedValue.components[1].calculatedValue = 6678; - //change the value - input1.value = +(`${input1.value}` + '90'); - input1.dispatchEvent(inputEvent); + Harness.testSubmission(simpleForm, { + data: { name: 'noname', age: '1' }, + }); - setTimeout(() => { - assert.equal(input2.value, '66'); - assert.equal(input1.value, 667890); - done(); - }, 250); - }, 250); - }, 900); - }); + simpleForm.submit().then((submission) => { + assert.deepEqual(submission.data, { name: 'noname' }); + done(); + }); }); - }); - - let simpleForm = null; - it('Should create a simple form', function(done) { - const formElement = document.createElement('div'); - simpleForm = new Webform(formElement); - simpleForm.setForm({ - title: 'Simple Form', - components: [ - { - type: 'textfield', - key: 'firstName', - input: true - }, - { - type: 'textfield', - key: 'lastName', - input: true - } - ] - }).then(() => { - Harness.testElements(simpleForm, 'input[type="text"]', 2); - Harness.testElements(simpleForm, 'input[name="data[firstName]"]', 1); - Harness.testElements(simpleForm, 'input[name="data[lastName]"]', 1); - done(); - }).catch(done); - }); - - it('Should set a submission to the form.', function() { - Harness.testSubmission(simpleForm, { data: { - firstName: 'Joe', - lastName: 'Smith' - } }); - }); - - it('Should translate a form from options', function(done) { - const formElement = document.createElement('div'); - const translateForm = new Webform(formElement, { - language: 'es', - i18n: { - es: { - 'Default Label': 'Spanish Label' - } - } - }); - translateForm.setForm({ - title: 'Translate Form', - components: [ - { - type: 'textfield', - label: 'Default Label', - key: 'myfield', - input: true, - inputType: 'text', - validate: {} - } - ] - }).then(() => { - const label = formElement.querySelector('.col-form-label'); - assert.equal(label.innerHTML.trim(), 'Spanish Label'); - done(); - }).catch((err) => { - done(err); - }); - }); + it('Should keep components valid if they are pristine', function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement, { language: 'en' }); + form.setForm(settingErrors) + .then(() => { + const inputEvent = new Event('input', { + bubbles: true, + cancelable: true, + }); + const input = form.element.querySelector( + 'input[name="data[textField]"]', + ); + for (let i = 0; i < 50; i++) { + input.value += i; + input.dispatchEvent(inputEvent); + } - it('Should get the language passed via options', function() { - const formElement = document.createElement('div'); - const form = new Webform(formElement, { - language: 'es' + setTimeout(() => { + assert.equal(form.errors.length, 0); + Harness.setInputValue(form, 'data[textField]', ''); + setTimeout(() => { + assert.equal(form.errors.length, 1); + done(); + }, 250); + }, 250); + }) + .catch(done); }); - assert.equal(form.language, 'es'); - }); + it('Should delete value of hidden component if clearOnHide is turned on', function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement, { language: 'en' }); + form.setForm(clearOnHide).then(() => { + const visibleData = { + data: { + visible: 'yes', + clearOnHideField: 'some text', + submit: false, + }, + metadata: {}, + }; - it('Should translate form errors in alerts', function() { - const formElement = document.createElement('div'); - const form = new Webform(formElement, { - language: 'es', - i18n: { - es: { - alertMessage: '{{message}}', - required: '{{field}} es obligatorio' - } - } - }); + const hiddenData = { + data: { + visible: 'no', + submit: false, + }, + }; + const inputEvent = new Event('input', { + bubbles: true, + cancelable: true, + }); + const textField = form.element.querySelector( + 'input[name="data[clearOnHideField]"]', + ); + textField.value = 'some text'; + textField.dispatchEvent(inputEvent); + this.timeout(1000); + setTimeout(() => { + assert.deepEqual(form.data, visibleData.data); + Harness.setInputValue(form, 'data[visible]', 'no'); - return form.setForm({ - components: [ - { - type: 'textfield', - label: 'Field Label', - key: 'myfield', - input: true, - inputType: 'text', - validate: { - required: true - } - } - ] - }) - .then(() => form.submit()) - .catch(() => { - // console.warn('nooo:', error) - }) - .then(() => { - const ref = formElement.querySelector('[ref="errorRef"]'); - assert.equal(ref.textContent.trim(), 'Field Label es obligatorio'); - }); - }); - - it('Should translate a form after instantiate', function(done) { - const formElement = document.createElement('div'); - const translateForm = new Webform(formElement, { - i18n: { - es: { - 'Default Label': 'Spanish Label' - } - } - }); - translateForm.setForm({ - title: 'Translate Form', - components: [ - { - type: 'textfield', - label: 'Default Label', - key: 'myfield', - input: true, - inputType: 'text', - validate: {} - } - ] - }).then(() => { - translateForm.language = 'es'; - const label = formElement.querySelector('.col-form-label'); - assert.equal(label.innerHTML.trim(), 'Spanish Label'); - done(); - }).catch(done); - }); - - it('Should add a translation after instantiate', function(done) { - const formElement = document.createElement('div'); - const translateForm = new Webform(formElement, { - i18n: { - language: 'es', - es: { - 'Default Label': 'Spanish Label' - }, - fr: { - 'Default Label': 'French Label' - } - } + setTimeout(() => { + assert.deepEqual(form.data, hiddenData.data); + done(); + }, 250); + }, 250); + }); }); - translateForm.setForm({ - title: 'Translate Form', - components: [ - { - type: 'textfield', - label: 'Default Label', - key: 'myfield', - input: true, - inputType: 'text', - validate: {} - } - ] - }).then(() => { - translateForm.language = 'fr'; - const label = formElement.querySelector('.col-form-label'); - assert.equal(label.innerHTML.trim(), 'French Label'); - done(); - }).catch(done); - }); - - it('Should switch a translation after instantiate', function(done) { - const formElement = document.createElement('div'); - const translateForm = new Webform(formElement); - translateForm.setForm({ - title: 'Translate Form', - components: [ - { - type: 'textfield', - label: 'Default Label', - key: 'myfield', - input: true, - inputType: 'text', - validate: {} - } - ] - }).then(() => { - translateForm.addLanguage('es', { 'Default Label': 'Spanish Label' }, true); - const label = formElement.querySelector('.col-form-label'); - assert.equal(label.innerHTML.trim(), 'Spanish Label'); - done(); - }).catch(done); - }); - - it('Should keep translation after redraw', function(done) { - const formElement = document.createElement('div'); - const form = new Webform(formElement); - const schema = { - title: 'Translate Form', - components: [ - { - type: 'textfield', - label: 'Default Label', - key: 'myfield', - input: true, - inputType: 'text', - validate: {} - } - ] - }; - - try { - form.setForm(schema) - .then(() => { - form.addLanguage('ru', { 'Default Label': 'Russian Label' }, true); - return form.language = 'ru'; - }, done) - .then(() => { - expect(form.options.language).to.equal('ru'); - expect(formElement.querySelector('.col-form-label').innerHTML.trim()).to.equal('Russian Label'); - form.redraw(); - expect(form.options.language).to.equal('ru'); - expect(formElement.querySelector('.col-form-label').innerHTML.trim()).to.equal('Russian Label'); - done(); - }, done) - .catch(done); - } - catch (error) { - done(error); - } - }); - it('Should fire languageChanged event when language is set', function(done) { - let isLanguageChangedEventFired = false; const formElement = document.createElement('div'); - const form = new Webform(formElement); - const schema = { - title: 'Translate Form', - components: [ - { - type: 'textfield', - label: 'Default Label', - key: 'myfield', - input: true, - inputType: 'text', - validate: {} - } - ] + const checkForErrors = function ( + form, + flags = {}, + submission, + numErrors, + done, + ) { + form.setSubmission(submission, flags) + .then(() => { + setTimeout(() => { + const errors = formElement.querySelectorAll( + '.formio-error-wrapper', + ); + expect(errors.length).to.equal(numErrors); + expect(form.errors.length).to.equal(numErrors); + done(); + }, 100); + }) + .catch(done); }; - try { - form.setForm(schema) - .then(() => { - form.addLanguage('ru', { 'Default Label': 'Russian Label' }, false); - form.on('languageChanged', () => { - isLanguageChangedEventFired = true; - }); - return form.language = 'ru'; - }, done) - .then(() => { - assert(isLanguageChangedEventFired); - done(); - }, done) - .catch(done); - } - catch (error) { - done(error); - } - }); - - it('When submitted should strip fields with persistent: client-only from submission', function(done) { - const formElement = document.createElement('div'); - simpleForm = new Webform(formElement); - /* eslint-disable quotes */ - simpleForm.setForm({ - title: 'Simple Form', - components: [ - { - "label": "Name", - "allowMultipleMasks": false, - "showWordCount": false, - "showCharCount": false, - "tableView": true, - "type": "textfield", - "input": true, - "key": "name", - "widget": { - "type": "" - } - }, - { - "label": "Age", - "persistent": "client-only", - "mask": false, - "tableView": true, - "type": "number", - "input": true, - "key": "age" - } - ] - }); - /* eslint-enable quotes */ - - Harness.testSubmission(simpleForm, { - data: { name: 'noname', age: '1' } - }); - - simpleForm.submit().then((submission) => { - assert.deepEqual(submission.data, { name: 'noname' }); - done(); - }); - }); - - it('Should keep components valid if they are pristine', function(done) { - const formElement = document.createElement('div'); - const form = new Webform(formElement, { language: 'en' }); - form.setForm(settingErrors).then(() => { - const inputEvent = new Event('input', { bubbles: true, cancelable: true }); - const input = form.element.querySelector('input[name="data[textField]"]'); - for (let i = 0; i < 50; i++) { - input.value += i; - input.dispatchEvent(inputEvent); - } - - setTimeout(() => { - assert.equal(form.errors.length, 0); - Harness.setInputValue(form, 'data[textField]', ''); - setTimeout(() => { - assert.equal(form.errors.length, 1); - done(); - }, 250); - }, 250); - }).catch(done); - }); - - it('Should delete value of hidden component if clearOnHide is turned on', function(done) { - const formElement = document.createElement('div'); - const form = new Webform(formElement, { language: 'en' }); - form.setForm(clearOnHide).then(() => { - const visibleData = { - data: { - visible: 'yes', - clearOnHideField: 'some text', - submit: false - }, - metadata: {} - }; - - const hiddenData = { - data: { - visible: 'no', - submit: false - } - }; - const inputEvent = new Event('input', { bubbles: true, cancelable: true }); - const textField = form.element.querySelector('input[name="data[clearOnHideField]"]'); - textField.value = 'some text'; - textField.dispatchEvent(inputEvent); - this.timeout(1000); - setTimeout(() => { - assert.deepEqual(form.data, visibleData.data); - Harness.setInputValue(form, 'data[visible]', 'no'); - - setTimeout(() => { - assert.deepEqual(form.data, hiddenData.data); - done(); - }, 250); - }, 250); - }); - }); - - const formElement = document.createElement('div'); - const checkForErrors = function(form, flags = {}, submission, numErrors, done) { - form.setSubmission(submission, flags).then(() => { - setTimeout(() => { - const errors = formElement.querySelectorAll('.formio-error-wrapper'); - expect(errors.length).to.equal(numErrors); - expect(form.errors.length).to.equal(numErrors); - done(); - }, 100); - }).catch(done); - }; - - //BUG - uncomment once fixed (ticket FIO-6042) - // it('Should not fire validations when fields are either protected or not persistent.', (done) => { - // const form = new Webform(formElement,{ language: 'en' }); - // form.setForm( - // { - // title: 'protected and persistent', - // components: [ - // { - // type: 'textfield', - // label: 'A', - // key: 'a', - // validate: { - // required: true - // } - // }, - // { - // type: 'textfield', - // label: 'B', - // key: 'b', - // protected: true, - // validate: { - // required: true - // } - // } - // ], - // }).then(() => { - // checkForErrors(form, {}, {}, 0, () => { - // checkForErrors(form, {}, { - // data: { - // a: 'Testing', - // b: '' - // } - // }, 1, () => { - // checkForErrors(form, {}, { - // _id: '123123123', - // data: { - // a: 'Testing', - // b: '' - // } - // }, 0, done); - // }); - // }); - // }); - // }); - - it('Should not fire validation on init.', function(done) { - formElement.innerHTML = ''; - const form = new Webform(formElement,{ language: 'en' }); - form.setForm( - { title: 'noValidation flag', - components: [{ - label: 'Number', - validate: { - required: true, - min: 5 - }, - key: 'number', - type: 'number', - input: true - }, { - label: 'Text Area', - validate: { - required: true, - minLength: 10 - }, - key: 'textArea', - type: 'textarea', - input: true - }], - }).then(() => { - checkForErrors(form, {}, {}, 0, done); + //BUG - uncomment once fixed (ticket FIO-6042) + // it('Should not fire validations when fields are either protected or not persistent.', (done) => { + // const form = new Webform(formElement,{ language: 'en' }); + // form.setForm( + // { + // title: 'protected and persistent', + // components: [ + // { + // type: 'textfield', + // label: 'A', + // key: 'a', + // validate: { + // required: true + // } + // }, + // { + // type: 'textfield', + // label: 'B', + // key: 'b', + // protected: true, + // validate: { + // required: true + // } + // } + // ], + // }).then(() => { + // checkForErrors(form, {}, {}, 0, () => { + // checkForErrors(form, {}, { + // data: { + // a: 'Testing', + // b: '' + // } + // }, 1, () => { + // checkForErrors(form, {}, { + // _id: '123123123', + // data: { + // a: 'Testing', + // b: '' + // } + // }, 0, done); + // }); + // }); + // }); + // }); + + it('Should not fire validation on init.', function (done) { + formElement.innerHTML = ''; + const form = new Webform(formElement, { language: 'en' }); + form.setForm({ + title: 'noValidation flag', + components: [ + { + label: 'Number', + validate: { + required: true, + min: 5, + }, + key: 'number', + type: 'number', + input: true, + }, + { + label: 'Text Area', + validate: { + required: true, + minLength: 10, + }, + key: 'textArea', + type: 'textarea', + input: true, + }, + ], + }).then(() => { + checkForErrors(form, {}, {}, 0, done); + }); }); - }); - it('Should validation on init when alwaysDirty flag is set.', function(done) { - formElement.innerHTML = ''; - const form = new Webform(formElement, { - language: 'en', - alwaysDirty: true - }); - form.setForm( - { title: 'noValidation flag', - components: [{ - label: 'Number', - validate: { - required: true, - min: 5 - }, - key: 'number', - type: 'number', - input: true - }, { - label: 'Text Area', - validate: { - required: true, - minLength: 10 - }, - key: 'textArea', - type: 'textarea', - input: true - }], - }).then(() => { - checkForErrors(form, {}, {}, 2, done); + it('Should validation on init when alwaysDirty flag is set.', function (done) { + formElement.innerHTML = ''; + const form = new Webform(formElement, { + language: 'en', + alwaysDirty: true, + }); + form.setForm({ + title: 'noValidation flag', + components: [ + { + label: 'Number', + validate: { + required: true, + min: 5, + }, + key: 'number', + type: 'number', + input: true, + }, + { + label: 'Text Area', + validate: { + required: true, + minLength: 10, + }, + key: 'textArea', + type: 'textarea', + input: true, + }, + ], + }).then(() => { + checkForErrors(form, {}, {}, 2, done); + }); }); - }); - it('Should validation on init when dirty flag is set.', function(done) { - formElement.innerHTML = ''; - const form = new Webform(formElement, { - language: 'en', - }); - form.setForm( - { title: 'noValidation flag', - components: [{ - label: 'Number', - validate: { - required: true, - min: 5 - }, - key: 'number', - type: 'number', - input: true - }, { - label: 'Text Area', - validate: { - required: true, - minLength: 10 - }, - key: 'textArea', - type: 'textarea', - input: true - }], - }).then(() => { - checkForErrors(form, { - dirty: true - }, {}, 2, done); - }); - }); - - it('Should not show any errors on setSubmission when providing an empty data object', function(done) { - formElement.innerHTML = ''; - const form = new Webform(formElement,{ language: 'en' }); - form.setForm( - { title: 'noValidation flag', - components: [{ - label: 'Number', - validate: { - required: true, - min: 5 - }, - key: 'number', - type: 'number', - input: true - }, { - label: 'Text Area', - validate: { - required: true, - minLength: 10 - }, - key: 'textArea', - type: 'textarea', - input: true - }], - } - ).then(() => { - checkForErrors(form, {}, {}, 0, done); - }); - }); - - it('Should not show errors when providing empty data object with data set.', function(done) { - formElement.innerHTML = ''; - const form = new Webform(formElement,{ language: 'en' }); - form.setForm( - { title: 'noValidation flag', - components: [{ - label: 'Number', - validate: { - required: true, - min: 5 - }, - key: 'number', - type: 'number', - input: true - }, { - label: 'Text Area', - validate: { - required: true, - minLength: 10 - }, - key: 'textArea', - type: 'textarea', - input: true - }], - } - ).then(() => { - checkForErrors(form, {}, { data: {} }, 0, done); - }); - }); - - it('Should show errors on setSubmission when providing explicit data values.', function(done) { - formElement.innerHTML = ''; - const form = new Webform(formElement,{ language: 'en' }); - form.setForm( - { title: 'noValidation flag', - components: [{ - label: 'Number', - validate: { - required: true, - min: 5 - }, - key: 'number', - type: 'number', - input: true - }, { - label: 'Text Area', - validate: { - required: true, - minLength: 10 - }, - key: 'textArea', - type: 'textarea', - input: true - }], - } - ).then(() => { - checkForErrors(form, {}, { - data:{ - number: 2, - textArea: '' - } - }, 2, done); - }); - }); - - it('Should not show errors on setSubmission with noValidate:TRUE', function(done) { - formElement.innerHTML = ''; - const form = new Webform(formElement,{ language: 'en' }); - form.setForm( - { title: 'noValidation flag', - components: [{ - label: 'Number', - validate: { - required: true, - min: 5 - }, - key: 'number', - type: 'number', - input: true - }, { - label: 'Text Area', - validate: { - required: true, - minLength: 10 - }, - key: 'textArea', - type: 'textarea', - input: true - }], - } - ).then(() => { - checkForErrors(form, { - noValidate:true - }, { - data:{ - number: 2, - textArea: '' - } - }, 0, done); - }); - }); - - it('Should set calculated value correctly', function(done) { - formElement.innerHTML = ''; - const form = new Webform(formElement); - form.setForm(calculateZeroValue).then(() => { - const a = form.components[0]; - const b = form.components[1]; - const sum = form.components[2]; - - a.setValue(10); - b.setValue(5); - setTimeout(() => { - assert.equal(a.dataValue, 10); - assert.equal(b.dataValue, 5); - assert.equal(sum.dataValue, 15); - - a.setValue('0'); - b.setValue('0'); - setTimeout(() => { - assert.equal(a.dataValue, 0); - assert.equal(b.dataValue,0); - assert.equal(sum.dataValue, 0); - - done(); - }, 250); - }, 250); - }).catch(done); - }); - - it('Should render Nested Modal Wizard Form correctly', function(done) { - formElement.innerHTML = ''; - const form = new Webform(formElement); - form.setForm(nestedModalWizard).then(() => { - const openModalRef = form.element.querySelector('[ref="openModal"]'); - assert(openModalRef, 'Should render Open Modal button'); - const wizard = form.components[1].subForm; - wizard.setPage(1); - setTimeout(() => { - const openModalRef = form.element.querySelector('[ref="openModal"]'); - assert(openModalRef, 'Should render Open Modal button after the page was changed'); - done(); - }, 250); - }).catch(done); - }); - - it('Should set calculated value correctly, given a file component', function(done) { - formElement.innerHTML = ''; - const form = new Webform(formElement); - form.setForm(disableSubmitButton).then(() => { - const textField = form.getComponent(['textField']); - const fileA = form.getComponent(['upload']); - const fileB = form.getComponent(['file']); - const submitButton = form.getComponent(['submit']); - assert.equal(submitButton.disabled, false, 'Button should be enabled at the beginning'); - - const simulateFileUploading = (comp, debounce = 250) => { - const filePromise = new Promise((resolve) => { - setTimeout(() => resolve(), debounce); + it('Should validation on init when dirty flag is set.', function (done) { + formElement.innerHTML = ''; + const form = new Webform(formElement, { + language: 'en', }); - filePromise.then(() => comp.emit('fileUploadingEnd', filePromise)); - comp.emit('fileUploadingStart', filePromise); - }; - - simulateFileUploading(fileA, 1000); - textField.setValue('12345'); - setTimeout(() => { - assert.equal(submitButton.filesUploading.length, 1); - assert.equal(submitButton.isDisabledOnInvalid, true, 'Should be disabled on invalid due to the invalid TextField\'s value'); - assert.equal(submitButton.disabled, true, 'Should be disabled'); - simulateFileUploading(fileB, 500); - setTimeout(() => { - assert.equal(submitButton.filesUploading.length, 2); - assert.equal(submitButton.disabled, true, 'Should be disabled'); - setTimeout(() => { - assert.equal(submitButton.filesUploading.length, 0); - assert.equal(submitButton.disabled, true, 'Should be disabled since TextField is still invalid'); - textField.setValue('123'); - setTimeout(() => { - assert.equal(submitButton.disabled, false, 'Should be enabled'); - done(); - }, 250); - }, 650); - }, 100); - }, 250); - }).catch(done); - }); - - describe('set/get nosubmit', function() { - it('should set/get nosubmit flag and emit nosubmit event', function() { - const form = new Webform(null, {}); - const emit = sinon.spy(form, 'emit'); - expect(form.nosubmit).to.be.false; - form.nosubmit = true; - expect(form.nosubmit).to.be.true; - expect(emit.callCount).to.equal(1); - expect(emit.args[0]).to.deep.equal(['nosubmit', true]); - form.nosubmit = false; - expect(form.nosubmit).to.be.false; - expect(emit.callCount).to.equal(2); - expect(emit.args[1]).to.deep.equal(['nosubmit', false]); - }); - }); - - describe('getValue and setValue', function() { - it('should setValue and getValue', function(done) { - formElement.innerHTML = ''; - const form = new Webform(formElement, { language: 'en' }); - form.setForm({ - components: [ - { - type: 'textfield', - key: 'a' - }, - { - type: 'container', - key: 'b', + form.setForm({ + title: 'noValidation flag', components: [ - { - type: 'datagrid', - key: 'c', - components: [ - { - type: 'textfield', - key: 'd' - }, - { - type: 'textfield', - key: 'e' - }, - { - type: 'editgrid', - key: 'f', - components: [ - { - type: 'textfield', - key: 'g' - } - ] - } - ] - } - ] - } - ] - }).then(() => { - let count = 0; - const onChange = form.onChange; - form.onChange = function(...args) { - count++; - return onChange.apply(form, args); - }; - - // Ensure that it says it changes. - assert.equal(form.setValue({ - a: 'a', - b: { - c: [ - { d: 'd1', e: 'e1', f: [{ g: 'g1' }] }, - { d: 'd2', e: 'e2', f: [{ g: 'g2' }] }, - ] - } - }), true); - - setTimeout(() => { - // It should have only updated once. - assert.equal(count, 1); - done(); - }, 500); - }); - }); - }); - - describe('ReadOnly Form', function() { - it('Should apply conditionals when in readOnly mode.', function(done) { - done = _.once(done); - const Conditions = require('../test/forms/conditions').default; - const formElement = document.createElement('div'); - const form = new Webform(formElement, { - readOnly: true, - language: 'en' - }); - form.setForm(Conditions.form).then(() => { - Harness.testConditionals(form, { - data: { - typeShow: 'Show', - typeMe: 'Me', - typeThe: 'The', - typeMonkey: 'Monkey!' - } - }, [], (error) => { - form.destroy(); - if (error) { - throw new Error(error); - } - done(); + { + label: 'Number', + validate: { + required: true, + min: 5, + }, + key: 'number', + type: 'number', + input: true, + }, + { + label: 'Text Area', + validate: { + required: true, + minLength: 10, + }, + key: 'textArea', + type: 'textarea', + input: true, + }, + ], + }).then(() => { + checkForErrors( + form, + { + dirty: true, + }, + {}, + 2, + done, + ); }); - }); }); - }); - - describe('Validate onBlur', function() { - it('Should keep component valid onChange', function(done) { - formElement.innerHTML = ''; - const form = new Webform(formElement, { language: 'en' }); - form.setForm(validationOnBlur).then(() => { - const field = form.components[0]; - const field2 = form.components[1]; - const fieldInput = field.refs.input[0]; - - Harness.setInputValue(field, 'data[textField]', '12'); - - setTimeout(() => { - assert(!field.error, 'Should be valid while changing'); - const blurEvent = new Event('blur'); - fieldInput.dispatchEvent(blurEvent); - setTimeout(() => { - assert(field.error, 'Should set error after component was blurred'); - Harness.setInputValue(field2, 'data[textField1]', 'ab'); - - setTimeout(() => { - assert(field.error, 'Should keep error when editing another component'); - done(); - }, 200); - }, 200); - }, 200); - }).catch(done); + it('Should not show any errors on setSubmission when providing an empty data object', function (done) { + formElement.innerHTML = ''; + const form = new Webform(formElement, { language: 'en' }); + form.setForm({ + title: 'noValidation flag', + components: [ + { + label: 'Number', + validate: { + required: true, + min: 5, + }, + key: 'number', + type: 'number', + input: true, + }, + { + label: 'Text Area', + validate: { + required: true, + minLength: 10, + }, + key: 'textArea', + type: 'textarea', + input: true, + }, + ], + }).then(() => { + checkForErrors(form, {}, {}, 0, done); + }); }); - it('Should keep components inside DataGrid valid onChange', function(done) { - formElement.innerHTML = ''; - const form = new Webform(formElement, { language: 'en' }); - form.setForm(dataGridOnBlurValidation).then(() => { - const component = form.components[0]; - Harness.setInputValue(component, 'data[dataGrid][0][textField]', '12'); - - setTimeout(() => { - const textField = component.iteratableRows[0].components.textField; - assert.equal(!!textField.error, false, 'Should stay valid on input'); - const blur = new Event('blur', { bubbles: true, cancelable: true }); - const input = textField.refs.input[0]; - input.dispatchEvent(blur); - textField.element.dispatchEvent(blur); - setTimeout(() => { - assert(textField.error, 'Should be validated after blur'); - done(); - }, 250); - }, 250); - }).catch(done); - }); - }); - - describe('Reset values', function() { - it('Should reset all values correctly.', function() { - formElement.innerHTML = ''; - const form = new Webform(formElement, { language: 'en' }); - return form.setForm( - { - components: [ - { - type: 'textfield', - key: 'firstName', - label: 'First Name', - placeholder: 'Enter your first name.', - input: true, - tooltip: 'Enter your First Name', - description: 'Enter your First Name' - }, - { - type: 'textfield', - key: 'lastName', - label: 'Last Name', - placeholder: 'Enter your last name', - input: true, - tooltip: 'Enter your Last Name', - description: 'Enter your Last Name' - }, - { - type: 'select', - label: 'Favorite Things', - key: 'favoriteThings', - placeholder: 'These are a few of your favorite things...', - data: { - values: [ - { - value: 'raindropsOnRoses', - label: 'Raindrops on roses' - }, - { - value: 'whiskersOnKittens', - label: 'Whiskers on Kittens' - }, - { - value: 'brightCopperKettles', - label: 'Bright Copper Kettles' - }, - { - value: 'warmWoolenMittens', - label: 'Warm Woolen Mittens' - } - ] - }, - dataSrc: 'values', - template: '{{ item.label }}', - multiple: true, - input: true - }, - { - type: 'number', - key: 'number', - label: 'Number', - input: true - }, - { - type: 'button', - action: 'submit', - label: 'Submit', - theme: 'primary' - } - ] - } - ).then(() => { - form.setSubmission({ - data: { - firstName: 'Joe', - lastName: 'Bob', - favoriteThings: ['whiskersOnKittens', 'warmWoolenMittens'], - number: 233 - } + it('Should not show errors when providing empty data object with data set.', function (done) { + formElement.innerHTML = ''; + const form = new Webform(formElement, { language: 'en' }); + form.setForm({ + title: 'noValidation flag', + components: [ + { + label: 'Number', + validate: { + required: true, + min: 5, + }, + key: 'number', + type: 'number', + input: true, + }, + { + label: 'Text Area', + validate: { + required: true, + minLength: 10, + }, + key: 'textArea', + type: 'textarea', + input: true, + }, + ], }).then(() => { - expect(form.submission).to.deep.equal({ - data: { - firstName: 'Joe', - lastName: 'Bob', - favoriteThings: ['whiskersOnKittens', 'warmWoolenMittens'], - number: 233, - submit: false - } - }); - form.setSubmission({ data: {} }).then(() => { - expect(form.submission).to.deep.equal({ - data: { - firstName: '', - lastName: '', - favoriteThings: [], - submit: false - } - }); - }); + checkForErrors(form, {}, { data: {} }, 0, done); }); - }); }); - }); - - describe('New Simple Conditions', function() { - it('Should show field if all conditions are met', function(done) { - const formElement = document.createElement('div'); - const form = new Webform(formElement); - - form.setForm(formsWithNewSimpleConditions.form1).then(() => { - const conditionalComponent = form.getComponent('conditionalField'); - assert.equal(conditionalComponent.visible, false, '(1) Component should be conditionally hidden'); - - form.setValue({ data: { number: 11, email: 'test@form.io', radio: 'one' } }); - - setTimeout(() => { - assert.equal(conditionalComponent.visible, true, '(2) Component should be conditionally visible'); - const emailComponent = form.getComponent('email'); - - emailComponent.setValue('test@form1.io'); - setTimeout(() => { - assert.equal(conditionalComponent.visible, false, '(3) Component should be conditionally hidden'); - done(); - }, 300); - }, 300); - }).catch((err) => done(err)); + it('Should show errors on setSubmission when providing explicit data values.', function (done) { + formElement.innerHTML = ''; + const form = new Webform(formElement, { language: 'en' }); + form.setForm({ + title: 'noValidation flag', + components: [ + { + label: 'Number', + validate: { + required: true, + min: 5, + }, + key: 'number', + type: 'number', + input: true, + }, + { + label: 'Text Area', + validate: { + required: true, + minLength: 10, + }, + key: 'textArea', + type: 'textarea', + input: true, + }, + ], + }).then(() => { + checkForErrors( + form, + {}, + { + data: { + number: 2, + textArea: '', + }, + }, + 2, + done, + ); + }); }); - it('Should show field if any condition is met', function(done) { - const formElement = document.createElement('div'); - const form = new Webform(formElement); - const formCopy = fastCloneDeep(formsWithNewSimpleConditions.form1); - _.set(formCopy, 'components[0].conditional.conjunction', 'any'); - - form.setForm(formCopy).then(() => { - const conditionalComponent = form.getComponent('conditionalField'); - assert.equal(conditionalComponent.visible, false, '(1) Component should be conditionally hidden'); - - form.setValue({ data: { number: 1100, email: 'test@form.io' } }); - - setTimeout(() => { - assert.equal(conditionalComponent.visible, true, '(2) Component should be conditionally visible'); - form.setValue({ data: { number: 10 , email: 'test@form1.io' } }); - - setTimeout(() => { - assert.equal(conditionalComponent.visible, true, '(3) Component should be conditionally visible'); - form.setValue({ data: { number: 10000 } }); - - setTimeout(() => { - assert.equal(conditionalComponent.visible, false, '(4) Component should be conditionally hidden'); - form.setValue({ data: { radio: 'one' } }); - - setTimeout(() => { - assert.equal(conditionalComponent.visible, true, '(5) Component should be conditionally visible'); - - done(); - }, 450); - }, 400); - }, 350); - }, 300); - }).catch((err) => done(err)); + it('Should not show errors on setSubmission with noValidate:TRUE', function (done) { + formElement.innerHTML = ''; + const form = new Webform(formElement, { language: 'en' }); + form.setForm({ + title: 'noValidation flag', + components: [ + { + label: 'Number', + validate: { + required: true, + min: 5, + }, + key: 'number', + type: 'number', + input: true, + }, + { + label: 'Text Area', + validate: { + required: true, + minLength: 10, + }, + key: 'textArea', + type: 'textarea', + input: true, + }, + ], + }).then(() => { + checkForErrors( + form, + { + noValidate: true, + }, + { + data: { + number: 2, + textArea: '', + }, + }, + 0, + done, + ); + }); }); - it('Should hide field if any condition is met', function(done) { - const formElement = document.createElement('div'); - const form = new Webform(formElement); - const formCopy = fastCloneDeep(formsWithNewSimpleConditions.form1); - _.set(formCopy, 'components[0].conditional.show', false); - _.set(formCopy, 'components[0].conditional.conjunction', 'any'); - - form.setForm(formCopy).then(() => { - const conditionalComponent = form.getComponent('conditionalField'); - assert.equal(conditionalComponent.visible, true, 'Component should be conditionally visible'); - - form.setValue({ data: { number: 1100, email: 'test@form.io' } }); - - setTimeout(() => { - assert.equal(conditionalComponent.visible, false, 'Component should be conditionally hidden'); - form.setValue({ data: { number: 10 , email: 'test@form1.io' } }); - - setTimeout(() => { - assert.equal(conditionalComponent.visible, false, 'Component should be conditionally hidden'); - form.setValue({ data: { number: 10000 } }); - - setTimeout(() => { - assert.equal(conditionalComponent.visible, true, 'Component should be conditionally visible'); - form.setValue({ data: { radio: 'one' } }); + it('Should set calculated value correctly', function (done) { + formElement.innerHTML = ''; + const form = new Webform(formElement); + form.setForm(calculateZeroValue) + .then(() => { + const a = form.components[0]; + const b = form.components[1]; + const sum = form.components[2]; + + a.setValue(10); + b.setValue(5); + setTimeout(() => { + assert.equal(a.dataValue, 10); + assert.equal(b.dataValue, 5); + assert.equal(sum.dataValue, 15); - setTimeout(() => { - assert.equal(conditionalComponent.visible, false, 'Component should be conditionally hidden'); - done(); - }, 300); - }, 300); - }, 300); - }, 300); - }).catch((err) => done(err)); + a.setValue('0'); + b.setValue('0'); + setTimeout(() => { + assert.equal(a.dataValue, 0); + assert.equal(b.dataValue, 0); + assert.equal(sum.dataValue, 0); + + done(); + }, 250); + }, 250); + }) + .catch(done); }); - it('Should hide field if all conditions are met', function(done) { - const formElement = document.createElement('div'); - const form = new Webform(formElement); - const formCopy = fastCloneDeep(formsWithNewSimpleConditions.form1); - _.set(formCopy, 'components[0].conditional.show', false); - - form.setForm(formCopy).then(() => { - const conditionalComponent = form.getComponent('conditionalField'); - assert.equal(conditionalComponent.visible, true, 'Component should be conditionally visible'); - - form.setValue({ data: { number: 11, email: 'test@form.io', radio: 'one' } }); - - setTimeout(() => { - assert.equal(conditionalComponent.visible, false, 'Component should be conditionally hidden'); - const emailComponent = form.getComponent('email'); - - emailComponent.setValue('test@form1.io'); - - setTimeout(() => { - assert.equal(conditionalComponent.visible, true, 'Component should be conditionally visible'); - done(); - }, 300); - }, 300); - }).catch((err) => done(err)); + it('Should render Nested Modal Wizard Form correctly', function (done) { + formElement.innerHTML = ''; + const form = new Webform(formElement); + form.setForm(nestedModalWizard) + .then(() => { + const openModalRef = + form.element.querySelector('[ref="openModal"]'); + assert(openModalRef, 'Should render Open Modal button'); + const wizard = form.components[1].subForm; + wizard.setPage(1); + setTimeout(() => { + const openModalRef = + form.element.querySelector('[ref="openModal"]'); + assert( + openModalRef, + 'Should render Open Modal button after the page was changed', + ); + done(); + }, 250); + }) + .catch(done); }); - it('Should show field if all conditions are met (test with different component types + multiple components)', function(done) { - const formElement = document.createElement('div'); - const form = new Webform(formElement); - - form.setForm(formsWithNewSimpleConditions.form2).then(() => { - const conditionalComponent = form.getComponent('conditionalField'); - assert.equal(conditionalComponent.visible, false, 'Component should be conditionally hidden'); - - form.setValue({ - data: { - email: 'test@form.io', - day: '10/06/2022', - survey: { - q1: 'true', - }, - number: [100, 25, 350], - checkbox: true, - selectBoxes: { - one: true, - two: false, - three: false, - four: false, - five: false, - }, - radio: 'two', - tags: 'test,newtag', - selectValues: 'one', - selectCustomWithValuesOfNumberType: 1, - submit: true, - currency: 35, - } - }); - - setTimeout(() => { - assert.equal(conditionalComponent.visible, true, 'Component should be conditionally visible'); - const dayComponent = form.getComponent('day'); - - dayComponent.setValue('8/09/2022'); - - setTimeout(() => { - assert.equal(conditionalComponent.visible, false, 'Component should be conditionally hidden'); - done(); - }, 300); - }, 300); - }).catch((err) => done(err)); + it('Should set calculated value correctly, given a file component', function (done) { + formElement.innerHTML = ''; + const form = new Webform(formElement); + form.setForm(disableSubmitButton) + .then(() => { + const textField = form.getComponent(['textField']); + const fileA = form.getComponent(['upload']); + const fileB = form.getComponent(['file']); + const submitButton = form.getComponent(['submit']); + assert.equal( + submitButton.disabled, + false, + 'Button should be enabled at the beginning', + ); + + const simulateFileUploading = (comp, debounce = 250) => { + const filePromise = new Promise((resolve) => { + setTimeout(() => resolve(), debounce); + }); + filePromise.then(() => + comp.emit('fileUploadingEnd', filePromise), + ); + comp.emit('fileUploadingStart', filePromise); + }; + + simulateFileUploading(fileA, 1000); + textField.setValue('12345'); + setTimeout(() => { + assert.equal(submitButton.filesUploading.length, 1); + assert.equal( + submitButton.isDisabledOnInvalid, + true, + "Should be disabled on invalid due to the invalid TextField's value", + ); + assert.equal( + submitButton.disabled, + true, + 'Should be disabled', + ); + simulateFileUploading(fileB, 500); + setTimeout(() => { + assert.equal(submitButton.filesUploading.length, 2); + assert.equal( + submitButton.disabled, + true, + 'Should be disabled', + ); + setTimeout(() => { + assert.equal(submitButton.filesUploading.length, 0); + assert.equal( + submitButton.disabled, + true, + 'Should be disabled since TextField is still invalid', + ); + textField.setValue('123'); + setTimeout(() => { + assert.equal( + submitButton.disabled, + false, + 'Should be enabled', + ); + done(); + }, 250); + }, 650); + }, 100); + }, 250); + }) + .catch(done); }); - it('Should show/hide field inside datagrid rows', function(done) { - const formElement = document.createElement('div'); - const form = new Webform(formElement); - - form.setForm(formsWithNewSimpleConditions.form4).then(() => { - const dataGrid = form.getComponent('dataGrid'); - - dataGrid.setValue([ - { number: 50 }, - { number: 55 }, - { number: 12 }, - { number: 105 }, - ]); - - setTimeout(() => { - const expectedValues = { - '0': true, - '1': false, - '2': true, - '3': false - }; - - _.each(dataGrid.rows, (row, index) => { - assert.equal(row['textField'].visible, expectedValues[`${index}`], `Component should be conditionally ${expectedValues[`${index}`] ? 'visible': 'hidden'} in row ${index}`); - }); - done(); - }, 300); - }).catch((err) => done(err)); + describe('set/get nosubmit', function () { + it('should set/get nosubmit flag and emit nosubmit event', function () { + const form = new Webform(null, {}); + const emit = sinon.spy(form, 'emit'); + expect(form.nosubmit).to.be.false; + form.nosubmit = true; + expect(form.nosubmit).to.be.true; + expect(emit.callCount).to.equal(1); + expect(emit.args[0]).to.deep.equal(['nosubmit', true]); + form.nosubmit = false; + expect(form.nosubmit).to.be.false; + expect(emit.callCount).to.equal(2); + expect(emit.args[1]).to.deep.equal(['nosubmit', false]); + }); }); - it('Should set component value through logic triggered by simple condition', function(done) { - const formElement = document.createElement('div'); - const form = new Webform(formElement); - - form.setForm(formsWithNewSimpleConditions.form3).then(() => { - const componentWithLogic = form.getComponent('fieldWithLogic'); - assert.equal(componentWithLogic.isEmpty(), true, 'Component should be empty'); + describe('getValue and setValue', function () { + it('should setValue and getValue', function (done) { + formElement.innerHTML = ''; + const form = new Webform(formElement, { language: 'en' }); + form.setForm({ + components: [ + { + type: 'textfield', + key: 'a', + }, + { + type: 'container', + key: 'b', + components: [ + { + type: 'datagrid', + key: 'c', + components: [ + { + type: 'textfield', + key: 'd', + }, + { + type: 'textfield', + key: 'e', + }, + { + type: 'editgrid', + key: 'f', + components: [ + { + type: 'textfield', + key: 'g', + }, + ], + }, + ], + }, + ], + }, + ], + }).then(() => { + let count = 0; + const onChange = form.onChange; + form.onChange = function (...args) { + count++; + return onChange.apply(form, args); + }; + + // Ensure that it says it changes. + assert.equal( + form.setValue({ + a: 'a', + b: { + c: [ + { d: 'd1', e: 'e1', f: [{ g: 'g1' }] }, + { d: 'd2', e: 'e2', f: [{ g: 'g2' }] }, + ], + }, + }), + true, + ); - form.setValue({ - data: { - number: 2, - radio: 'two' - } + setTimeout(() => { + // It should have only updated once. + assert.equal(count, 1); + done(); + }, 500); + }); }); - - setTimeout(() => { - assert.equal(componentWithLogic.dataValue, 'logic works', 'Component should have value set by logic'); - done(); - }, 300); - }).catch((err) => done(err)); }); - it('Should show field if all conditions are met (test all operators)', function(done) { - const formElement = document.createElement('div'); - const form = new Webform(formElement); - - form.setForm(formsWithNewSimpleConditions.form5).then(() => { - const conditionalComponent = form.getComponent('conditionalField'); - assert.equal(conditionalComponent.visible, false, '(1) Component should be conditionally hidden'); - - form.setValue({ data: { - dateTime: '2022-09-29T12:00:00+03:00', - day: '09/29/2022', - dateTime1: '2022-09-29T12:00:00+03:00', - day1: '09/29/2022', - url: 'portal.form.io', - number: 100, - currency: 100, - textField1: 'some test text', - day2: '09/29/2022', - select: '', - radio: 'one', - dateTime3: '2022-09-12T12:00:00+03:00', - textArea: 'test', - textField2: 'test2', - number2: [100], - currency2: 100, - email: 'some@form.io', - url2: 'portal.form.io', - } }); - - setTimeout(() => { - assert.equal(conditionalComponent.visible, true, '(2) Component should be conditionally visible'); - const selectComponent = form.getComponent('select'); - - selectComponent.setValue('one'); - - setTimeout(() => { - assert.equal(conditionalComponent.visible, false, '(3) Component should be conditionally hidden'); - done(); - }, 300); - }, 300); - }).catch((err) => done(err)); + describe('ReadOnly Form', function () { + it('Should apply conditionals when in readOnly mode.', function (done) { + done = _.once(done); + const Conditions = require('../test/forms/conditions').default; + const formElement = document.createElement('div'); + const form = new Webform(formElement, { + readOnly: true, + language: 'en', + }); + form.setForm(Conditions.form).then(() => { + Harness.testConditionals( + form, + { + data: { + typeShow: 'Show', + typeMe: 'Me', + typeThe: 'The', + typeMonkey: 'Monkey!', + }, + }, + [], + (error) => { + form.destroy(); + if (error) { + throw new Error(error); + } + done(); + }, + ); + }); + }); }); - it('Should show the field on select boxes value', function(done) { - const formElement = document.createElement('div'); - const form = new Webform(formElement); - - form.setForm(formsWithNewSimpleConditions.form6).then(() => { - const conditionalComponent = form.getComponent('textField'); - const selectBoxes = form.getComponent('selectBoxes'); - assert.equal(conditionalComponent.visible, false, '(1) Component should be conditionally hidden'); - - const selectBoxesValue = { - '111': true, - '222': false - }; - selectBoxes.setValue(selectBoxesValue); + describe('Validate onBlur', function () { + it('Should keep component valid onChange', function (done) { + formElement.innerHTML = ''; + const form = new Webform(formElement, { language: 'en' }); + form.setForm(validationOnBlur) + .then(() => { + const field = form.components[0]; + const field2 = form.components[1]; + const fieldInput = field.refs.input[0]; - setTimeout(() => { - assert.deepEqual(selectBoxes.dataValue, selectBoxesValue, '(2) SelectBoxes value should be set'); - assert.equal(conditionalComponent.visible, true, '(3) Component should be conditionally visible'); + Harness.setInputValue(field, 'data[textField]', '12'); - selectBoxes.setValue({ - '111': false, - '222': false - }); + setTimeout(() => { + assert(!field.error, 'Should be valid while changing'); + const blurEvent = new Event('blur'); + fieldInput.dispatchEvent(blurEvent); + + setTimeout(() => { + assert( + field.error, + 'Should set error after component was blurred', + ); + Harness.setInputValue( + field2, + 'data[textField1]', + 'ab', + ); + + setTimeout(() => { + assert( + field.error, + 'Should keep error when editing another component', + ); + done(); + }, 200); + }, 200); + }, 200); + }) + .catch(done); + }); - setTimeout(() => { - assert.equal(conditionalComponent.visible, false, '(4) Component should be conditionally hidden'); - done(); - }, 300); - }, 300); - }).catch((err) => done(err)); - }); + it('Should keep components inside DataGrid valid onChange', function (done) { + formElement.innerHTML = ''; + const form = new Webform(formElement, { language: 'en' }); + form.setForm(dataGridOnBlurValidation) + .then(() => { + const component = form.components[0]; + Harness.setInputValue( + component, + 'data[dataGrid][0][textField]', + '12', + ); - it('Should show field when condition is based on the values of select resource with object value', function(done) { - const element = document.createElement('div'); - const values = [ - { - _id: '656daabeebc67ecca1141569', - form: '656daab0ebc67ecca1141226', - data: { - number: 4, - notes: 'notes 4', - }, - project: '656daa20ebc67ecca1140e8d', - state: 'submitted', - created: '2023-12-04T10:32:30.821Z', - modified: '2023-12-06T14:25:00.886Z', - }, - { - _id: '656daabbebc67ecca11414a7', - form: '656daab0ebc67ecca1141226', - data: { - number: 3, - notes: 'notes 3', - }, - project: '656daa20ebc67ecca1140e8d', - state: 'submitted', - created: '2023-12-04T10:32:27.322Z', - modified: '2023-12-06T14:25:07.494Z', - }, - { - _id: '656daab8ebc67ecca11413e5', - form: '656daab0ebc67ecca1141226', - data: { - number: 2, - notes: 'notes 2', - }, - project: '656daa20ebc67ecca1140e8d', - state: 'submitted', - created: '2023-12-04T10:32:24.514Z', - modified: '2023-12-06T14:25:13.610Z', - }, - { - _id: '656daab5ebc67ecca1141322', - form: '656daab0ebc67ecca1141226', - data: { - number: 1, - notes: 'notes 1', - }, - project: '656daa20ebc67ecca1140e8d', - state: 'submitted', - created: '2023-12-04T10:32:21.205Z', - modified: '2023-12-06T14:25:20.930Z', - }, - ]; - const originalMakeRequest = Formio.makeRequest; - Formio.makeRequest = function() { - return new Promise(resolve => { - setTimeout(() => { - resolve(values); - }, 50); + setTimeout(() => { + const textField = + component.iteratableRows[0].components.textField; + assert.equal( + !!textField.error, + false, + 'Should stay valid on input', + ); + const blur = new Event('blur', { + bubbles: true, + cancelable: true, + }); + const input = textField.refs.input[0]; + input.dispatchEvent(blur); + textField.element.dispatchEvent(blur); + setTimeout(() => { + assert( + textField.error, + 'Should be validated after blur', + ); + done(); + }, 250); + }, 250); + }) + .catch(done); }); - }; - - Formio.createForm(element, formWithObjectValueSelect) - .then(form => { - const numberComp = form.getComponent('number'); - assert.equal(numberComp.visible, false); - - const selectRef = form.getComponent('selectRef'); - selectRef.setValue(fastCloneDeep(values[3])); - const selectNoValuePropertyMult = form.getComponent('selectNoValueProperty'); - selectNoValuePropertyMult.setValue([fastCloneDeep(values[2])]); - const selectEntireObject = form.getComponent('selectEntireObject'); - selectEntireObject.setValue(fastCloneDeep(values[1].data)); - const selectEntireObjectMult = form.getComponent('selectEntireObjectMult'); - selectEntireObjectMult.setValue([fastCloneDeep(values[0].data)]); - - setTimeout(() => { - assert.equal(numberComp.visible, true); - selectRef.setValue(fastCloneDeep(values[2])); - setTimeout(() => { - assert.equal(numberComp.visible, false); - Formio.makeRequest = originalMakeRequest; - done(); - }, 400); - }, 400); - }) - .catch(done); }); - }); - - describe('Calculate Value with allowed manual override', function() { - const initialSubmission = { - data: { - dataGrid: [ - { label: 'yes', value: 'yes' }, - { label: 'no', value: 'no' }, - ], - checkbox: false, - submit: false - }, - metadata: {} - }; - const submissionWithOverridenValues = { - data: { - dataGrid: [ - { label: 'yes', value: 'y' }, - { label: 'no', value: 'n' }, - ], - checkbox: false, - submit: false - }, - metadata: {} - }; - const submissionWithOverridenValues2 = { - data: { - dataGrid: [ - { label: 'yes2', value: 'y' }, - { label: 'no', value: 'n' }, - ], - checkbox: false, - submit: false - }, - metadata: {} - }; - it('Should reset all values correctly.', function(done) { - const formElement = document.createElement('div'); - const form = new Webform(formElement, { language: 'en' }); - form.setForm(calculateValueWithManualOverride).then(() => { - const dataGrid = form.getComponent('dataGrid'); - dataGrid.setValue([{ label: 'yes' }, { label: 'no' }]); - setTimeout(() => { - expect(form.submission).to.deep.equal(initialSubmission); - const row1Value = form.getComponent(['dataGrid', 0, 'value']); - const row2Value = form.getComponent(['dataGrid', 1, 'value']); - row1Value.setValue('y'); - row2Value.setValue('n'); - - setTimeout(() => { - expect(form.submission).to.deep.equal(submissionWithOverridenValues); - const row1Label = form.getComponent(['dataGrid', 0, 'label']); - row1Label.setValue('yes2'); - setTimeout(() => { - expect(form.submission).to.deep.equal(submissionWithOverridenValues2); - form.setSubmission(submissionWithOverridenValues).then(() => { - setTimeout(() => { - const tabs = form.getComponent(['tabs']); - tabs.setTab(1); - setTimeout(() => { - expect(form.submission).to.deep.equal(submissionWithOverridenValues); - done(); - }, 250); - }, 150); - }); - }, 250); - }, 250); - }, 250); - }).catch(done); + describe('Reset values', function () { + it('Should reset all values correctly.', function () { + formElement.innerHTML = ''; + const form = new Webform(formElement, { language: 'en' }); + return form + .setForm({ + components: [ + { + type: 'textfield', + key: 'firstName', + label: 'First Name', + placeholder: 'Enter your first name.', + input: true, + tooltip: 'Enter your First Name', + description: + 'Enter your First Name', + }, + { + type: 'textfield', + key: 'lastName', + label: 'Last Name', + placeholder: 'Enter your last name', + input: true, + tooltip: 'Enter your Last Name', + description: + 'Enter your Last Name', + }, + { + type: 'select', + label: 'Favorite Things', + key: 'favoriteThings', + placeholder: + 'These are a few of your favorite things...', + data: { + values: [ + { + value: 'raindropsOnRoses', + label: 'Raindrops on roses', + }, + { + value: 'whiskersOnKittens', + label: 'Whiskers on Kittens', + }, + { + value: 'brightCopperKettles', + label: 'Bright Copper Kettles', + }, + { + value: 'warmWoolenMittens', + label: 'Warm Woolen Mittens', + }, + ], + }, + dataSrc: 'values', + template: '{{ item.label }}', + multiple: true, + input: true, + }, + { + type: 'number', + key: 'number', + label: 'Number', + input: true, + }, + { + type: 'button', + action: 'submit', + label: 'Submit', + theme: 'primary', + }, + ], + }) + .then(() => { + form.setSubmission({ + data: { + firstName: 'Joe', + lastName: 'Bob', + favoriteThings: [ + 'whiskersOnKittens', + 'warmWoolenMittens', + ], + number: 233, + }, + }).then(() => { + expect(form.submission).to.deep.equal({ + data: { + firstName: 'Joe', + lastName: 'Bob', + favoriteThings: [ + 'whiskersOnKittens', + 'warmWoolenMittens', + ], + number: 233, + submit: false, + }, + }); + form.setSubmission({ data: {} }).then(() => { + expect(form.submission).to.deep.equal({ + data: { + firstName: '', + lastName: '', + favoriteThings: [], + submit: false, + }, + }); + }); + }); + }); + }); }); - it('Should apply submission metadata value in calculation.', function(done) { - const formElement = document.createElement('div'); - const form = new Webform(formElement, { language: 'en' }); - form.setForm(calculateValueWithSubmissionMetadata).then(() => { - const textField = form.getComponent('textField'); + describe('New Simple Conditions', function () { + it('Should show field if all conditions are met', function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement); + + form.setForm(formsWithNewSimpleConditions.form1) + .then(() => { + const conditionalComponent = + form.getComponent('conditionalField'); + assert.equal( + conditionalComponent.visible, + false, + '(1) Component should be conditionally hidden', + ); + + form.setValue({ + data: { + number: 11, + email: 'test@form.io', + radio: 'one', + }, + }); - textField.setValue('test value'); + setTimeout(() => { + assert.equal( + conditionalComponent.visible, + true, + '(2) Component should be conditionally visible', + ); + const emailComponent = form.getComponent('email'); + + emailComponent.setValue('test@form1.io'); + + setTimeout(() => { + assert.equal( + conditionalComponent.visible, + false, + '(3) Component should be conditionally hidden', + ); + done(); + }, 300); + }, 300); + }) + .catch((err) => done(err)); + }); - form.submit(false, {}); + it('Should show field if any condition is met', function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement); + const formCopy = fastCloneDeep(formsWithNewSimpleConditions.form1); + _.set(formCopy, 'components[0].conditional.conjunction', 'any'); + + form.setForm(formCopy) + .then(() => { + const conditionalComponent = + form.getComponent('conditionalField'); + assert.equal( + conditionalComponent.visible, + false, + '(1) Component should be conditionally hidden', + ); + + form.setValue({ + data: { number: 1100, email: 'test@form.io' }, + }); - setTimeout(() => { - expect(form.submission.metadata).to.exist; - expect(form.submission.metadata.timezone).to.be.not.empty; - expect(form.submission.data.textField).to.be.not.empty; - expect(form.submission.data.textArea).to.be.not.empty; - expect(form.submission.data.textArea).to.equal( - form.submission.data.textField + form.submission.metadata.timezone - ); + setTimeout(() => { + assert.equal( + conditionalComponent.visible, + true, + '(2) Component should be conditionally visible', + ); + form.setValue({ + data: { number: 10, email: 'test@form1.io' }, + }); + + setTimeout(() => { + assert.equal( + conditionalComponent.visible, + true, + '(3) Component should be conditionally visible', + ); + form.setValue({ data: { number: 10000 } }); + + setTimeout(() => { + assert.equal( + conditionalComponent.visible, + false, + '(4) Component should be conditionally hidden', + ); + form.setValue({ data: { radio: 'one' } }); + + setTimeout(() => { + assert.equal( + conditionalComponent.visible, + true, + '(5) Component should be conditionally visible', + ); + + done(); + }, 450); + }, 400); + }, 350); + }, 300); + }) + .catch((err) => done(err)); + }); - done(); - }, 250); - }).catch(done); - }); + it('Should hide field if any condition is met', function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement); + const formCopy = fastCloneDeep(formsWithNewSimpleConditions.form1); + _.set(formCopy, 'components[0].conditional.show', false); + _.set(formCopy, 'components[0].conditional.conjunction', 'any'); + + form.setForm(formCopy) + .then(() => { + const conditionalComponent = + form.getComponent('conditionalField'); + assert.equal( + conditionalComponent.visible, + true, + 'Component should be conditionally visible', + ); + + form.setValue({ + data: { number: 1100, email: 'test@form.io' }, + }); - it('Should allow to change value.', function(done) { - const formElement = document.createElement('div'); - const form = new Webform(formElement, { language: 'en' }); - form.setForm(calculatedSelectboxes).then(() => { - const radio = form.getComponent(['radio']); - radio.setValue('a'); - setTimeout(() => { - assert.equal(radio.dataValue, 'a'); - const selectBoxes = form.getComponent(['selectBoxes']); - assert.equal(selectBoxes.dataValue['a'], true, 'Should calculate value and set it to "a"'); - selectBoxes.setValue({ - 'a': true, - 'b': true, - 'c': false - }); - setTimeout(() => { - assert.deepEqual(selectBoxes.dataValue, { - 'a': true, - 'b': true, - 'c': false - }, 'Should change the value'); - done(); - }, 250); - }, 250); - }).catch(done); - }); + setTimeout(() => { + assert.equal( + conditionalComponent.visible, + false, + 'Component should be conditionally hidden', + ); + form.setValue({ + data: { number: 10, email: 'test@form1.io' }, + }); + + setTimeout(() => { + assert.equal( + conditionalComponent.visible, + false, + 'Component should be conditionally hidden', + ); + form.setValue({ data: { number: 10000 } }); + + setTimeout(() => { + assert.equal( + conditionalComponent.visible, + true, + 'Component should be conditionally visible', + ); + form.setValue({ data: { radio: 'one' } }); + + setTimeout(() => { + assert.equal( + conditionalComponent.visible, + false, + 'Component should be conditionally hidden', + ); + done(); + }, 300); + }, 300); + }, 300); + }, 300); + }) + .catch((err) => done(err)); + }); - it('Should recalculate values for components with "allow override" after first and only dataGrid row is removed/reset', function(done) { - const formElement = document.createElement('div'); - Formio.createForm(formElement, formsWithAllowOverride.withDataGrid).then((form) => { - const calculatedValues = { - number: 11111, - textField: 'test DataGrid', - textArea: 'test', - }; + it('Should hide field if all conditions are met', function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement); + const formCopy = fastCloneDeep(formsWithNewSimpleConditions.form1); + _.set(formCopy, 'components[0].conditional.show', false); + + form.setForm(formCopy) + .then(() => { + const conditionalComponent = + form.getComponent('conditionalField'); + assert.equal( + conditionalComponent.visible, + true, + 'Component should be conditionally visible', + ); + + form.setValue({ + data: { + number: 11, + email: 'test@form.io', + radio: 'one', + }, + }); - const overridenValues = { - number: 11111222, - textField: 'test DataGrid 222', - textArea: 'test 222', - }; + setTimeout(() => { + assert.equal( + conditionalComponent.visible, + false, + 'Component should be conditionally hidden', + ); + const emailComponent = form.getComponent('email'); + + emailComponent.setValue('test@form1.io'); + + setTimeout(() => { + assert.equal( + conditionalComponent.visible, + true, + 'Component should be conditionally visible', + ); + done(); + }, 300); + }, 300); + }) + .catch((err) => done(err)); + }); - const number = form.getComponent('number'); - const textArea = form.getComponent('textArea'); - const textField = form.getComponent('textField'); - const dgRadio = form.getComponent('dgRadio'); - const dataGrid = form.getComponent('dataGrid'); - // check if values are calculated on form load - assert.deepEqual(dataGrid.dataValue, [ - { - ...calculatedValues, - textField: textField.emptyValue, - dgRadio: dgRadio.emptyValue - } - ], 1); - - dgRadio.setValue('a'); - - setTimeout(() => { - // check if values are calculated correctly - assert.deepEqual(dataGrid.dataValue, [ - { - ...calculatedValues, - dgRadio: 'a' - } - ]); - - // override calculated values - const numberInput = number.refs.input[0]; - const textFieldInput = textField.refs.input[0]; - const textAreaInput = textArea.refs.input[0]; - - numberInput.value = overridenValues.number; - textFieldInput.value = overridenValues.textField; - textAreaInput.value = overridenValues.textArea; - - const inputEvent = new Event('input'); - - numberInput.dispatchEvent(inputEvent); - textFieldInput.dispatchEvent(inputEvent); - textAreaInput.dispatchEvent(inputEvent); - - setTimeout(() => { - // check if values are overriden - assert.deepEqual(dataGrid.dataValue, [ - { - ...overridenValues, - dgRadio: 'a' - } - ], 2); - - // clear first row - dataGrid.removeRow(0); + it('Should show field if all conditions are met (test with different component types + multiple components)', function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement); + + form.setForm(formsWithNewSimpleConditions.form2) + .then(() => { + const conditionalComponent = + form.getComponent('conditionalField'); + assert.equal( + conditionalComponent.visible, + false, + 'Component should be conditionally hidden', + ); + + form.setValue({ + data: { + email: 'test@form.io', + day: '10/06/2022', + survey: { + q1: 'true', + }, + number: [100, 25, 350], + checkbox: true, + selectBoxes: { + one: true, + two: false, + three: false, + four: false, + five: false, + }, + radio: 'two', + tags: 'test,newtag', + selectValues: 'one', + selectCustomWithValuesOfNumberType: 1, + submit: true, + currency: 35, + }, + }); - setTimeout(() => { - const dgRadio = form.getComponent('dgRadio'); - // make sure values are reset and recalculated - assert.deepEqual(dataGrid.dataValue, [ - { - ...calculatedValues, - textField: textField.emptyValue, - dgRadio: dgRadio.emptyValue - } - ], 3); - - dgRadio.setValue('a'); - setTimeout(() => { - // check if all values are calculated correctly - assert.deepEqual(dataGrid.dataValue, [ - { - ...calculatedValues, - dgRadio: 'a' - } - ], 4); + setTimeout(() => { + assert.equal( + conditionalComponent.visible, + true, + 'Component should be conditionally visible', + ); + const dayComponent = form.getComponent('day'); + + dayComponent.setValue('8/09/2022'); + + setTimeout(() => { + assert.equal( + conditionalComponent.visible, + false, + 'Component should be conditionally hidden', + ); + done(); + }, 300); + }, 300); + }) + .catch((err) => done(err)); + }); - document.body.innerHTML = ''; - done(); - }, 400); - }, 400); - }, 400); - }, 400); - }).catch((err) => done(err)); - }); + it('Should show/hide field inside datagrid rows', function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement); - it('Should recalculate values for components with "allow override" after the form is reset', function(done) { - const formElement = document.createElement('div'); - Formio.createForm(formElement, formsWithAllowOverride.withResetBtn).then((form) => { - const calculatedValues = { - number: 11111, - textField: 'test DataGrid', - textArea: 'test', - radio: 'one' - }; + form.setForm(formsWithNewSimpleConditions.form4) + .then(() => { + const dataGrid = form.getComponent('dataGrid'); - const overridenValues = { - number: 11111222, - textField: 'test DataGrid 222', - textArea: 'test 222', - radio: 'two' - }; - const checkbox = form.getComponent('checkbox'); - const number = form.getComponent('number'); - const textArea = form.getComponent('textArea'); - const radio = form.getComponent('radio'); - const textField = form.getComponent('textField'); - const dgRadio = form.getComponent('dgRadio'); - const resetBtn = form.getComponent('reset'); - - dgRadio.setValue('a'); - checkbox.setValue(true); - setTimeout(() => { - // check if values were calculated correctly - assert.equal(number.dataValue, calculatedValues.number); - assert.equal(textField.dataValue, calculatedValues.textField); - assert.equal(textArea.dataValue, calculatedValues.textArea); - assert.equal(radio.dataValue, calculatedValues.radio); - - // override calculated values - const numberInput = number.refs.input[0]; - const textFieldInput = textField.refs.input[0]; - const textAreaInput = textArea.refs.input[0]; - const radioInput =radio.refs.input[1]; - - numberInput.value = overridenValues.number; - textFieldInput.value = overridenValues.textField; - textAreaInput.value = overridenValues.textArea; - radioInput.checked = true; - const inputEvent = new Event('input'); - const clickEvent = new Event('click'); - - numberInput.dispatchEvent(inputEvent); - textFieldInput.dispatchEvent(inputEvent); - textAreaInput.dispatchEvent(inputEvent); - radioInput.dispatchEvent(clickEvent); - - setTimeout(() => { - // check if values were overriden - assert.equal(number.getValue(), overridenValues.number); - assert.equal(textField.dataValue, overridenValues.textField); - assert.equal(textArea.dataValue, overridenValues.textArea); - assert.equal(radio.dataValue, overridenValues.radio); - - checkbox.setValue(false); + dataGrid.setValue([ + { number: 50 }, + { number: 55 }, + { number: 12 }, + { number: 105 }, + ]); - setTimeout(() => { - // reset form - resetBtn.refs.button.dispatchEvent(clickEvent); - - setTimeout(() => { - // make sure that values are reset - assert.equal(number.dataValue, calculatedValues.number); - assert.equal(textArea.dataValue, calculatedValues.textArea); - assert.equal(textField.dataValue, textField.emptyValue); - assert.equal(radio.dataValue, radio.emptyValue); - assert.equal(dgRadio.dataValue, dgRadio.emptyValue); - assert.equal(checkbox.dataValue, checkbox.emptyValue); - - // trigger values calculation - dgRadio.setValue('a'); - checkbox.setValue(true); + setTimeout(() => { + const expectedValues = { + 0: true, + 1: false, + 2: true, + 3: false, + }; + + _.each(dataGrid.rows, (row, index) => { + assert.equal( + row['textField'].visible, + expectedValues[`${index}`], + `Component should be conditionally ${ + expectedValues[`${index}`] + ? 'visible' + : 'hidden' + } in row ${index}`, + ); + }); + done(); + }, 300); + }) + .catch((err) => done(err)); + }); - setTimeout(() => { - // make sure that values are recalculated - assert.equal(number.dataValue, calculatedValues.number); - assert.equal(textField.dataValue, calculatedValues.textField); - assert.equal(textArea.dataValue, calculatedValues.textArea); - assert.equal(radio.dataValue, calculatedValues.radio); - document.body.innerHTML = ''; - done(); - }, 300); - }, 300); - }, 300); - }, 300); - }, 400); - }).catch((err) => done(err)); - }); + it('Should set component value through logic triggered by simple condition', function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement); + + form.setForm(formsWithNewSimpleConditions.form3) + .then(() => { + const componentWithLogic = + form.getComponent('fieldWithLogic'); + assert.equal( + componentWithLogic.isEmpty(), + true, + 'Component should be empty', + ); + + form.setValue({ + data: { + number: 2, + radio: 'two', + }, + }); - it('Should recalculate values for conditional components with "allow override" and "clear on hide" enabled when components become visible again', function(done) { - const formElement = document.createElement('div'); - Formio.createForm(formElement, formsWithAllowOverride.withClearOnHide).then((form) => { - const calculatedValues = { - number: 111, - textField: 'test', - textArea: 'test value', - radio: 'a' - }; + setTimeout(() => { + assert.equal( + componentWithLogic.dataValue, + 'logic works', + 'Component should have value set by logic', + ); + done(); + }, 300); + }) + .catch((err) => done(err)); + }); - const overridenValues = { - number: 11123, - textField: 'test123', - textArea: 'test123', - radio: 'b' - }; - const checkbox = form.getComponent('checkbox'); - const number = form.getComponent('number'); - const textField = form.getComponent('textField'); - const textArea = form.getComponent('textArea'); - const radio = form.getComponent('radio'); - - assert.equal(number.visible, false); - assert.equal(textField.visible, false); - assert.equal(textArea.visible, false); - assert.equal(radio.visible, false); - - checkbox.setValue(true); - setTimeout(() => { - assert.equal(number.visible, true); - assert.equal(textField.visible, true); - assert.equal(textArea.visible, true); - assert.equal(radio.visible, true); - // check if values were calculated correctly - assert.equal(number.dataValue, calculatedValues.number); - assert.equal(textField.dataValue, calculatedValues.textField); - assert.equal(textArea.dataValue, calculatedValues.textArea); - assert.equal(radio.dataValue, calculatedValues.radio); - - // override calculated values - const numberInput = number.refs.input[0]; - const textFieldInput = textField.refs.input[0]; - const textAreaInput = textArea.refs.input[0]; - const radioInput =radio.refs.input[1]; - - numberInput.value = overridenValues.number; - textFieldInput.value = overridenValues.textField; - textAreaInput.value = overridenValues.textArea; - radioInput.checked = true; - const inputEvent = new Event('input'); - const clickEvent = new Event('click'); - - numberInput.dispatchEvent(inputEvent); - textFieldInput.dispatchEvent(inputEvent); - textAreaInput.dispatchEvent(inputEvent); - radioInput.dispatchEvent(clickEvent); - - setTimeout(() => { - // check if values were overriden - assert.equal(number.getValue(), overridenValues.number); - assert.equal(textField.dataValue, overridenValues.textField); - assert.equal(textArea.dataValue, overridenValues.textArea); - assert.equal(radio.dataValue, overridenValues.radio); - - checkbox.setValue(false); + it('Should show field if all conditions are met (test all operators)', function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement); + + form.setForm(formsWithNewSimpleConditions.form5) + .then(() => { + const conditionalComponent = + form.getComponent('conditionalField'); + assert.equal( + conditionalComponent.visible, + false, + '(1) Component should be conditionally hidden', + ); + + form.setValue({ + data: { + dateTime: '2022-09-29T12:00:00+03:00', + day: '09/29/2022', + dateTime1: '2022-09-29T12:00:00+03:00', + day1: '09/29/2022', + url: 'portal.form.io', + number: 100, + currency: 100, + textField1: 'some test text', + day2: '09/29/2022', + select: '', + radio: 'one', + dateTime3: '2022-09-12T12:00:00+03:00', + textArea: 'test', + textField2: 'test2', + number2: [100], + currency2: 100, + email: 'some@form.io', + url2: 'portal.form.io', + }, + }); - setTimeout(() => { - // check if conditional components were hidden - assert.equal(number.visible, false); - assert.equal(textField.visible, false); - assert.equal(textArea.visible, false); - assert.equal(radio.visible, false); - - checkbox.setValue(true); - setTimeout(() => { - // make sure that components appear again and values were recalculated - assert.equal(number.visible, true); - assert.equal(textField.visible, true); - assert.equal(textArea.visible, true); - assert.equal(radio.visible, true); - - assert.equal(number.dataValue, calculatedValues.number); - assert.equal(textField.dataValue, calculatedValues.textField); - assert.equal(textArea.dataValue, calculatedValues.textArea); - assert.equal(radio.dataValue, calculatedValues.radio); - document.body.innerHTML = ''; + setTimeout(() => { + assert.equal( + conditionalComponent.visible, + true, + '(2) Component should be conditionally visible', + ); + const selectComponent = form.getComponent('select'); + + selectComponent.setValue('one'); + + setTimeout(() => { + assert.equal( + conditionalComponent.visible, + false, + '(3) Component should be conditionally hidden', + ); + done(); + }, 300); + }, 300); + }) + .catch((err) => done(err)); + }); - done(); - }, 300); - }, 300); - }, 300); - }, 400); - }).catch((err) => done(err)); - }); - }); - - describe('Modal Edit', function() { - const submission = { - state: 'submitted', - data: { - checkbox: true, - selectBoxes: { - a: true, - b: true - }, - textfield: 'My Text', - select: 'f', - submit: true - } - }; - const componentsKeys = ['checkbox', 'selectBoxes', 'select', 'textfield']; - const expectedValues = { - checkbox: 'Yes', - selectBoxes: 'a, b', - select: 'f', - textfield: 'My Text' - }; + it('Should show the field on select boxes value', function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement); + + form.setForm(formsWithNewSimpleConditions.form6) + .then(() => { + const conditionalComponent = form.getComponent('textField'); + const selectBoxes = form.getComponent('selectBoxes'); + assert.equal( + conditionalComponent.visible, + false, + '(1) Component should be conditionally hidden', + ); + + const selectBoxesValue = { + 111: true, + 222: false, + }; + selectBoxes.setValue(selectBoxesValue); - it('Test rendering previews after the submission is set', function(done) { - const formElement = document.createElement('div'); - const form = new Webform(formElement, { language: 'en' }); - form.setForm(modalEditComponents).then(() => { - return form.setSubmission(submission, { fromSubmission: true }).then(() => { - componentsKeys.forEach((key) => { - const comp = form.getComponent([key]); - assert(comp); - const preview = comp.componentModal.refs.openModal; - assert(preview); - assert.equal(preview.textContent.replace(/\n|\t/g, '').trim(), expectedValues[key]); - }); - done(); + setTimeout(() => { + assert.deepEqual( + selectBoxes.dataValue, + selectBoxesValue, + '(2) SelectBoxes value should be set', + ); + assert.equal( + conditionalComponent.visible, + true, + '(3) Component should be conditionally visible', + ); + + selectBoxes.setValue({ + 111: false, + 222: false, + }); + + setTimeout(() => { + assert.equal( + conditionalComponent.visible, + false, + '(4) Component should be conditionally hidden', + ); + done(); + }, 300); + }, 300); + }) + .catch((err) => done(err)); }); - }).catch(done); - }); - it('Test updating previews after aboting changes', function(done) { - const formElement = document.createElement('div'); - const form = new Webform(formElement, { language: 'en' }); - form.setForm(modalEditComponents).then(() => { - return form.setSubmission(submission, { fromSubmission: true }).then(() => { - const comp = form.getComponent(['textfield']); - comp.componentModal.openModal(); - Harness.dispatchEvent('input', comp.componentModal.refs.modalContents, '[name="data[textfield]"]', (el) => { - el.value = 'My Text v2'; - }); - setTimeout(() => { - const fakeEvent = { - preventDefault: () => {} + it('Should show field when condition is based on the values of select resource with object value', function (done) { + const element = document.createElement('div'); + const values = [ + { + _id: '656daabeebc67ecca1141569', + form: '656daab0ebc67ecca1141226', + data: { + number: 4, + notes: 'notes 4', + }, + project: '656daa20ebc67ecca1140e8d', + state: 'submitted', + created: '2023-12-04T10:32:30.821Z', + modified: '2023-12-06T14:25:00.886Z', + }, + { + _id: '656daabbebc67ecca11414a7', + form: '656daab0ebc67ecca1141226', + data: { + number: 3, + notes: 'notes 3', + }, + project: '656daa20ebc67ecca1140e8d', + state: 'submitted', + created: '2023-12-04T10:32:27.322Z', + modified: '2023-12-06T14:25:07.494Z', + }, + { + _id: '656daab8ebc67ecca11413e5', + form: '656daab0ebc67ecca1141226', + data: { + number: 2, + notes: 'notes 2', + }, + project: '656daa20ebc67ecca1140e8d', + state: 'submitted', + created: '2023-12-04T10:32:24.514Z', + modified: '2023-12-06T14:25:13.610Z', + }, + { + _id: '656daab5ebc67ecca1141322', + form: '656daab0ebc67ecca1141226', + data: { + number: 1, + notes: 'notes 1', + }, + project: '656daa20ebc67ecca1140e8d', + state: 'submitted', + created: '2023-12-04T10:32:21.205Z', + modified: '2023-12-06T14:25:20.930Z', + }, + ]; + const originalMakeRequest = Formio.makeRequest; + Formio.makeRequest = function () { + return new Promise((resolve) => { + setTimeout(() => { + resolve(values); + }, 50); + }); }; - comp.componentModal.closeModalHandler(fakeEvent); - const preview = comp.componentModal.refs.openModal; - assert.equal(preview.textContent.replace(/\n|\t/g, '').trim(), 'My Text'); - done(); - }, 100); - }); - }).catch(done); - }); - }); - describe('Initially Collapsed Panel', function() { - const formElement = document.createElement('div'); - const form = new Webform(formElement, { language: 'en' }); - form.setForm(initiallyCollapsedPanel).then(() => { - it('Should be collapsed', function(done) { - try { - const panelBody = form.element.querySelector('[ref=nested-panel]'); - assert.equal(panelBody, null, 'Should not render the panel\'s body when initially collapsed'); - done(); - } - catch (err) { - done(err); - } - }); - it('Should open when an Error occured', function(done) { - form.executeSubmit().catch(() => { - try { - const panelBody = form.element.querySelector('[ref=nested-panel]'); - assert(panelBody, 'Should open the panel when an error occured'); - done(); - } - catch (err) { - done(err); - } - }); - }); - }).catch((err) => console.error(err)); - }); - - describe('Calculate Value', function() { - it('Should calculate value when set submission if the component is not persistent', function(done) { - const formElement = document.createElement('div'); - const form = new Webform(formElement, { language: 'en', pdf: true }); - form.setForm(calculatedNotPersistentValue).then(() => { - form.setSubmission({ - data: - { - a: 'testValue' - }, - state: 'submitted' + Formio.createForm(element, formWithObjectValueSelect) + .then((form) => { + const numberComp = form.getComponent('number'); + assert.equal(numberComp.visible, false); + + const selectRef = form.getComponent('selectRef'); + selectRef.setValue(fastCloneDeep(values[3])); + const selectNoValuePropertyMult = form.getComponent( + 'selectNoValueProperty', + ); + selectNoValuePropertyMult.setValue([ + fastCloneDeep(values[2]), + ]); + const selectEntireObject = + form.getComponent('selectEntireObject'); + selectEntireObject.setValue(fastCloneDeep(values[1].data)); + const selectEntireObjectMult = form.getComponent( + 'selectEntireObjectMult', + ); + selectEntireObjectMult.setValue([ + fastCloneDeep(values[0].data), + ]); + + setTimeout(() => { + assert.equal(numberComp.visible, true); + selectRef.setValue(fastCloneDeep(values[2])); + setTimeout(() => { + assert.equal(numberComp.visible, false); + Formio.makeRequest = originalMakeRequest; + done(); + }, 400); + }, 400); + }) + .catch(done); }); - setTimeout(() => { - const persistentField = form.getComponent(['a']); - assert.equal(persistentField.dataValue, 'testValue', 'Should set the value from the submission'); - const notPersistentFieldInput = form.element.querySelector('input[name="data[textField]"]'); - assert.equal(notPersistentFieldInput.value, 'testValue', 'Should calculate the value'); - done(); - }, 550); - }).catch(done); }); - it('Should calculate value by datasouce component when editing mode is on', function(done) { - const formElement = document.createElement('div'); - const form = new Webform(formElement, { language: 'en', pdf: true }); - form.setForm(calculateValueInEditingMode).then(() => { - form.editing = true; - form.setSubmission({ - data: - { - select: { label: 'Dummy #1', value: 'dummy1' }, - dataSourceDisplay: 'some value' + describe('Calculate Value with allowed manual override', function () { + const initialSubmission = { + data: { + dataGrid: [ + { label: 'yes', value: 'yes' }, + { label: 'no', value: 'no' }, + ], + checkbox: false, + submit: false, }, - state: 'submitted' - }) - .then(() => { - const dataSource = form.getComponent('datasource'); - dataSource.dataValue = { value: 'some value' }; - form.checkData(null, { dataSourceInitialLoading: true }); - setTimeout(() => { - const dataSourceDisplay = form.getComponent('dataSourceDisplay'); - assert.equal(dataSourceDisplay.dataValue, 'some value', 'Should set and keep the value'); - done(); - }, 1000); - }); - }).catch(done); - }); - - it('Should calculate value properly in editing mode', function(done) { - const formElement = document.createElement('div'); - const form = new Webform(formElement, { language: 'en', pdf: true }); - form.setForm(calculatedValue).then(() => { - form.editing = true; - form.setSubmission({ - data: - { - a: 4, - b: 5, - total: 9, + metadata: {}, + }; + const submissionWithOverridenValues = { + data: { + dataGrid: [ + { label: 'yes', value: 'y' }, + { label: 'no', value: 'n' }, + ], + checkbox: false, + submit: false, }, - state: 'submitted' - }); - setTimeout(() => { - const total = form.getComponent(['total']); - assert.equal(total.dataValue, 9, 'Should set and keep the value'); - - const b = form.getComponent(['b']); - Harness.dispatchEvent('input', b.element, 'input', (i) => i.value = '6'); - - setTimeout(() => { - assert.equal(total.dataValue, 10, 'Should recalculate the value'); - }, 300); - done(); - }, 1000); - }).catch(done); - }); - - it('Should not override value which was set from submission', function(done) { - const formElement = document.createElement('div'); - const form = new Webform(formElement, { language: 'en', pdf: true }); - form.setForm(calculateValueWithManualOverrideLableValueDataGrid).then(() => { - form.editing = true; - form.setSubmission({ - state: 'submitted', - data: { - dataGrid: [ - { label: '1', value: '1a' }, - { label: '2', value: '2a' }, - { label: '3', value: '3a' }, - ] - }, - }); - setTimeout(() => { - const value1 = form.getComponent(['dataGrid', 0, 'value']); - assert.equal(value1.dataValue, '1a', 'Should have a value set from submission'); - const value2 = form.getComponent(['dataGrid', 1, 'value']); - assert.equal(value2.dataValue, '2a', 'Should have a value set from submission'); - const value3 = form.getComponent(['dataGrid', 2, 'value']); - assert.equal(value3.dataValue, '3a', 'Should have a value set from submission'); - done(); - }, 1000); - }).catch(done); - }); - }); - - it('Should set different ids for components inside different Table rows', function(done) { - const formElement = document.createElement('div'); - const form = new Webform(formElement, { language: 'en', pdf: true }); - form.setForm(conditionalDataGridWithTableAndRadio).then(() => { - const radioInspection0 = form.getComponent(['inspectionDataGrid', 0, 'initialExam']); - Harness.dispatchEvent( - 'click', - radioInspection0.element, - 'input[value="reject"]', - i => i.checked = true, - ); - - setTimeout(() => { - const repairDataGrid0 = form.getComponent(['inspectionDataGrid', 0, 'repairDataGrid']); - assert.equal(radioInspection0.dataValue, 'reject', 'Should set value'); - assert.equal(repairDataGrid0.visible, true, 'Should become visible'); - - const radioRepair0 = form.getComponent(['inspectionDataGrid', 0, 'repairDataGrid', 0, 'repairExam']); - Harness.dispatchEvent( - 'click', - radioRepair0.element, - 'input[value="accept"]', - i => i.checked = true, - ); - - setTimeout(() => { - assert.equal(radioRepair0.dataValue, 'accept', 'Should set value'); - const inspectionDataGrid = form.getComponent(['inspectionDataGrid']); - inspectionDataGrid.addRow(); - - setTimeout(() => { - assert.equal(inspectionDataGrid.rows.length, 2, 'Should add a row'); - - const radioInspection1 = form.getComponent(['inspectionDataGrid', 1, 'initialExam']); - Harness.dispatchEvent( - 'click', - radioInspection1.element, - 'input[value="reject"]', - i => i.checked = true, - ); - - setTimeout(() => { - const repairDataGrid1 = form.getComponent(['inspectionDataGrid', 1, 'repairDataGrid']); - assert.equal(radioInspection1.dataValue, 'reject', 'Should set value'); - assert.equal(repairDataGrid1.visible, true, 'Should become visible'); - - const radioRepair1 = form.getComponent(['inspectionDataGrid', 1, 'repairDataGrid', 0, 'repairExam']); - Harness.dispatchEvent( - 'click', - form.element, - form.element.querySelector(`#${radioRepair1.root.id}-${radioRepair1.id}-${radioRepair1.row}-accept`), - i => i.checked = true - ); - - setTimeout(() => { - assert.equal(radioRepair1.dataValue, 'accept', 'Should set value of the clicked radio'); - assert.equal(radioRepair0.dataValue, 'accept', 'Value of the radio inside another row should stay the same'); - - done(); - }, 300); - }, 350); - }, 300); - }, 250); - }, 350); - }).catch(done); - }); - - it('Should render components properly', function(done) { - const formElement = document.createElement('div'); - const form = new Webform(formElement, { language: 'en' }); - form.setForm(multipleTextareaInsideConditionalComponent).then(() => { - form.setSubmission({ - data: { - textArea2: [ - 'test' - ], - didAnyBehavioralIssuesOccurOnYourShift: 'yes', - submit: false, - } - }); - setTimeout(() => { - const textarea = form.getComponent(['textArea2']); - const panel = form.getComponent(['behavioralIssues']); - assert.equal(panel.visible, true, 'Should be visible'); - assert.deepEqual(textarea.dataValue, ['test'], 'Should set the value from the submission'); - const inputRows = textarea.element.querySelectorAll('[ref="input"]'); - assert.equal(inputRows.length, 1, 'Should render all the rows of the Textarea'); - done(); - }, 750); - }).catch(done); - }); - - it('Should disable all the components inside Nested Form if it is disabled', function(done) { - const formElement = document.createElement('div'); - const form = new Webform(formElement, { language: 'en' }); - form.setForm(disabledNestedForm).then(() => { - assert.equal(form.components[0].disabled, false, 'Component that is outside of disabled Nested Form should be editable'); - const subFormComponents = form.components[1].subForm.components; - assert.deepEqual([subFormComponents[0].disabled, subFormComponents[1].disabled], [true, true], 'Components that are inside of disabled Nested Form should be disabled'); - done(); - }).catch(done); - }); - - it('Should restore value correctly if NestedForm is saved as reference', function(done) { - const formElement = document.createElement('div'); - const form = new Webform(formElement, { language: 'en' }); - form.setForm(nestedFormInsideDataGrid).then(() => { - const nestedForm = form.getComponent(['dataGrid', 0, 'form1']); - const submissionWithIdOnly = { _id: '1232', data: {} }; - nestedForm.dataValue = { ...submissionWithIdOnly }; - nestedForm.restoreValue(); - - setTimeout(() => { - assert.deepEqual(nestedForm.dataValue, submissionWithIdOnly, 'Should not set to defaultValue after restore'); - done(); - }, 350); - }).catch(done); - }); - - it('Should not set the default value if there is only Radio with False value', function(done) { - const formElement = document.createElement('div'); - Formio.createForm(formElement, nestedFormInWizard).then((form) => { - const nestedForm = form.getComponent(['form']); - const submission = { - data: { - radio: false - } - }; - - nestedForm.dataValue = { ...submission }; - - setTimeout(() => { - assert.deepEqual(nestedForm.dataValue, submission, 'Should set submission'); - nestedForm.valueChanged = true; - form.setPage(1); - - setTimeout(() => { - assert.deepEqual(nestedForm.dataValue.data, submission.data, 'Should not set to defaultValue after restore'); - done(); - }, 300); - }, 300); - }).catch(done); - }); - - it('Should add and clear input error classes correctly', function(done) { - const formElement = document.createElement('div'); - const form = new Webform(formElement, { language: 'en' }); + metadata: {}, + }; + const submissionWithOverridenValues2 = { + data: { + dataGrid: [ + { label: 'yes2', value: 'y' }, + { label: 'no', value: 'n' }, + ], + checkbox: false, + submit: false, + }, + metadata: {}, + }; - form.setForm(UpdateErrorClassesWidgets).then(() => { - const checkbox = form.getComponent('showDate'); - checkbox.setValue(true); - setTimeout(() => { - const dateTimeComponent = form.getComponent('condtionalDate'); - const dateComponentElement = dateTimeComponent.element; - assert.equal(!dateComponentElement.className.includes('formio-hidden'), true, 'Should not be hidden'); + it('Should reset all values correctly.', function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement, { language: 'en' }); + form.setForm(calculateValueWithManualOverride) + .then(() => { + const dataGrid = form.getComponent('dataGrid'); + dataGrid.setValue([{ label: 'yes' }, { label: 'no' }]); + setTimeout(() => { + expect(form.submission).to.deep.equal( + initialSubmission, + ); + const row1Value = form.getComponent([ + 'dataGrid', + 0, + 'value', + ]); + const row2Value = form.getComponent([ + 'dataGrid', + 1, + 'value', + ]); + row1Value.setValue('y'); + row2Value.setValue('n'); + + setTimeout(() => { + expect(form.submission).to.deep.equal( + submissionWithOverridenValues, + ); + const row1Label = form.getComponent([ + 'dataGrid', + 0, + 'label', + ]); + row1Label.setValue('yes2'); + setTimeout(() => { + expect(form.submission).to.deep.equal( + submissionWithOverridenValues2, + ); + form.setSubmission( + submissionWithOverridenValues, + ).then(() => { + setTimeout(() => { + const tabs = form.getComponent([ + 'tabs', + ]); + tabs.setTab(1); + setTimeout(() => { + expect( + form.submission, + ).to.deep.equal( + submissionWithOverridenValues, + ); + done(); + }, 250); + }, 150); + }); + }, 250); + }, 250); + }, 250); + }) + .catch(done); + }); - form.submit(); + it('Should apply submission metadata value in calculation.', function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement, { language: 'en' }); + form.setForm(calculateValueWithSubmissionMetadata) + .then(() => { + const textField = form.getComponent('textField'); - setTimeout(() => { - const dateVisibleInput = dateComponentElement.querySelector('.input'); - const flatpickerInput = dateComponentElement.querySelector('.flatpickr-input'); + textField.setValue('test value'); - assert(dateVisibleInput.className.includes('is-invalid'), 'Visible field should have invalid class'); - assert(flatpickerInput.className.includes('is-invalid'), 'Flatpickr field should have invalid class as well'); + form.submit(false, {}); - dateTimeComponent.setValue('2020-12-09T00:00:00'); + setTimeout(() => { + expect(form.submission.metadata).to.exist; + expect(form.submission.metadata.timezone).to.be.not + .empty; + expect(form.submission.data.textField).to.be.not.empty; + expect(form.submission.data.textArea).to.be.not.empty; + expect(form.submission.data.textArea).to.equal( + form.submission.data.textField + + form.submission.metadata.timezone, + ); + + done(); + }, 250); + }) + .catch(done); + }); - setTimeout(() => { - assert.equal(dateTimeComponent.dataValue, '2020-12-09T00:00:00', 'Should set value'); - assert(!dateVisibleInput.className.includes('is-invalid'), 'Invalid class should be removed'); - assert(!flatpickerInput.className.includes('is-invalid'), 'Invalid class should be removed from flatpickr field as well'); + it('Should allow to change value.', function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement, { language: 'en' }); + form.setForm(calculatedSelectboxes) + .then(() => { + const radio = form.getComponent(['radio']); + radio.setValue('a'); + setTimeout(() => { + assert.equal(radio.dataValue, 'a'); + const selectBoxes = form.getComponent(['selectBoxes']); + assert.equal( + selectBoxes.dataValue['a'], + true, + 'Should calculate value and set it to "a"', + ); + selectBoxes.setValue({ + a: true, + b: true, + c: false, + }); + setTimeout(() => { + assert.deepEqual( + selectBoxes.dataValue, + { + a: true, + b: true, + c: false, + }, + 'Should change the value', + ); + done(); + }, 250); + }, 250); + }) + .catch(done); + }); - checkbox.setValue(false); + it('Should recalculate values for components with "allow override" after first and only dataGrid row is removed/reset', function (done) { + const formElement = document.createElement('div'); + Formio.createForm(formElement, formsWithAllowOverride.withDataGrid) + .then((form) => { + const calculatedValues = { + number: 11111, + textField: 'test DataGrid', + textArea: 'test', + }; + + const overridenValues = { + number: 11111222, + textField: 'test DataGrid 222', + textArea: 'test 222', + }; + + const number = form.getComponent('number'); + const textArea = form.getComponent('textArea'); + const textField = form.getComponent('textField'); + const dgRadio = form.getComponent('dgRadio'); + const dataGrid = form.getComponent('dataGrid'); + // check if values are calculated on form load + assert.deepEqual( + dataGrid.dataValue, + [ + { + ...calculatedValues, + textField: textField.emptyValue, + dgRadio: dgRadio.emptyValue, + }, + ], + 1, + ); + + dgRadio.setValue('a'); - setTimeout(() => { - const dateComponentElement = dateTimeComponent.element; - assert.equal(dateComponentElement.className.includes('formio-hidden'), true, 'Should be hidden'); - checkbox.setValue(true); + setTimeout(() => { + // check if values are calculated correctly + assert.deepEqual(dataGrid.dataValue, [ + { + ...calculatedValues, + dgRadio: 'a', + }, + ]); + + // override calculated values + const numberInput = number.refs.input[0]; + const textFieldInput = textField.refs.input[0]; + const textAreaInput = textArea.refs.input[0]; + + numberInput.value = overridenValues.number; + textFieldInput.value = overridenValues.textField; + textAreaInput.value = overridenValues.textArea; + + const inputEvent = new Event('input'); + + numberInput.dispatchEvent(inputEvent); + textFieldInput.dispatchEvent(inputEvent); + textAreaInput.dispatchEvent(inputEvent); + + setTimeout(() => { + // check if values are overriden + assert.deepEqual( + dataGrid.dataValue, + [ + { + ...overridenValues, + dgRadio: 'a', + }, + ], + 2, + ); + + // clear first row + dataGrid.removeRow(0); + + setTimeout(() => { + const dgRadio = form.getComponent('dgRadio'); + // make sure values are reset and recalculated + assert.deepEqual( + dataGrid.dataValue, + [ + { + ...calculatedValues, + textField: textField.emptyValue, + dgRadio: dgRadio.emptyValue, + }, + ], + 3, + ); + + dgRadio.setValue('a'); + setTimeout(() => { + // check if all values are calculated correctly + assert.deepEqual( + dataGrid.dataValue, + [ + { + ...calculatedValues, + dgRadio: 'a', + }, + ], + 4, + ); + + document.body.innerHTML = ''; + done(); + }, 400); + }, 400); + }, 400); + }, 400); + }) + .catch((err) => done(err)); + }); - setTimeout(() => { - const dateComponentElement = dateTimeComponent.element; - assert.equal(!dateComponentElement.className.includes('formio-hidden'), true, 'Should be visible'); - const dateVisibleInput = dateComponentElement.querySelector('.input:not([type="hidden"])'); - const flatpickerInput = dateComponentElement.querySelector('.flatpickr-input'); + it('Should recalculate values for components with "allow override" after the form is reset', function (done) { + const formElement = document.createElement('div'); + Formio.createForm(formElement, formsWithAllowOverride.withResetBtn) + .then((form) => { + const calculatedValues = { + number: 11111, + textField: 'test DataGrid', + textArea: 'test', + radio: 'one', + }; + + const overridenValues = { + number: 11111222, + textField: 'test DataGrid 222', + textArea: 'test 222', + radio: 'two', + }; + const checkbox = form.getComponent('checkbox'); + const number = form.getComponent('number'); + const textArea = form.getComponent('textArea'); + const radio = form.getComponent('radio'); + const textField = form.getComponent('textField'); + const dgRadio = form.getComponent('dgRadio'); + const resetBtn = form.getComponent('reset'); + + dgRadio.setValue('a'); + checkbox.setValue(true); + setTimeout(() => { + // check if values were calculated correctly + assert.equal(number.dataValue, calculatedValues.number); + assert.equal( + textField.dataValue, + calculatedValues.textField, + ); + assert.equal( + textArea.dataValue, + calculatedValues.textArea, + ); + assert.equal(radio.dataValue, calculatedValues.radio); + + // override calculated values + const numberInput = number.refs.input[0]; + const textFieldInput = textField.refs.input[0]; + const textAreaInput = textArea.refs.input[0]; + const radioInput = radio.refs.input[1]; + + numberInput.value = overridenValues.number; + textFieldInput.value = overridenValues.textField; + textAreaInput.value = overridenValues.textArea; + radioInput.checked = true; + const inputEvent = new Event('input'); + const clickEvent = new Event('click'); + + numberInput.dispatchEvent(inputEvent); + textFieldInput.dispatchEvent(inputEvent); + textAreaInput.dispatchEvent(inputEvent); + radioInput.dispatchEvent(clickEvent); + + setTimeout(() => { + // check if values were overriden + assert.equal( + number.getValue(), + overridenValues.number, + ); + assert.equal( + textField.dataValue, + overridenValues.textField, + ); + assert.equal( + textArea.dataValue, + overridenValues.textArea, + ); + assert.equal( + radio.dataValue, + overridenValues.radio, + ); + + checkbox.setValue(false); + + setTimeout(() => { + // reset form + resetBtn.refs.button.dispatchEvent(clickEvent); + + setTimeout(() => { + // make sure that values are reset + assert.equal( + number.dataValue, + calculatedValues.number, + ); + assert.equal( + textArea.dataValue, + calculatedValues.textArea, + ); + assert.equal( + textField.dataValue, + textField.emptyValue, + ); + assert.equal( + radio.dataValue, + radio.emptyValue, + ); + assert.equal( + dgRadio.dataValue, + dgRadio.emptyValue, + ); + assert.equal( + checkbox.dataValue, + checkbox.emptyValue, + ); + + // trigger values calculation + dgRadio.setValue('a'); + checkbox.setValue(true); + + setTimeout(() => { + // make sure that values are recalculated + assert.equal( + number.dataValue, + calculatedValues.number, + ); + assert.equal( + textField.dataValue, + calculatedValues.textField, + ); + assert.equal( + textArea.dataValue, + calculatedValues.textArea, + ); + assert.equal( + radio.dataValue, + calculatedValues.radio, + ); + document.body.innerHTML = ''; + done(); + }, 300); + }, 300); + }, 300); + }, 300); + }, 400); + }) + .catch((err) => done(err)); + }); - assert(dateVisibleInput.className.includes('is-invalid'), 'Visible field should has invalid class'); - assert(flatpickerInput.className.includes('is-invalid'), 'Flatpickr field should has invalid class as well'); + it('Should recalculate values for conditional components with "allow override" and "clear on hide" enabled when components become visible again', function (done) { + const formElement = document.createElement('div'); + Formio.createForm( + formElement, + formsWithAllowOverride.withClearOnHide, + ) + .then((form) => { + const calculatedValues = { + number: 111, + textField: 'test', + textArea: 'test value', + radio: 'a', + }; + + const overridenValues = { + number: 11123, + textField: 'test123', + textArea: 'test123', + radio: 'b', + }; + const checkbox = form.getComponent('checkbox'); + const number = form.getComponent('number'); + const textField = form.getComponent('textField'); + const textArea = form.getComponent('textArea'); + const radio = form.getComponent('radio'); + + assert.equal(number.visible, false); + assert.equal(textField.visible, false); + assert.equal(textArea.visible, false); + assert.equal(radio.visible, false); + + checkbox.setValue(true); + setTimeout(() => { + assert.equal(number.visible, true); + assert.equal(textField.visible, true); + assert.equal(textArea.visible, true); + assert.equal(radio.visible, true); + // check if values were calculated correctly + assert.equal(number.dataValue, calculatedValues.number); + assert.equal( + textField.dataValue, + calculatedValues.textField, + ); + assert.equal( + textArea.dataValue, + calculatedValues.textArea, + ); + assert.equal(radio.dataValue, calculatedValues.radio); + + // override calculated values + const numberInput = number.refs.input[0]; + const textFieldInput = textField.refs.input[0]; + const textAreaInput = textArea.refs.input[0]; + const radioInput = radio.refs.input[1]; + + numberInput.value = overridenValues.number; + textFieldInput.value = overridenValues.textField; + textAreaInput.value = overridenValues.textArea; + radioInput.checked = true; + const inputEvent = new Event('input'); + const clickEvent = new Event('click'); + + numberInput.dispatchEvent(inputEvent); + textFieldInput.dispatchEvent(inputEvent); + textAreaInput.dispatchEvent(inputEvent); + radioInput.dispatchEvent(clickEvent); + + setTimeout(() => { + // check if values were overriden + assert.equal( + number.getValue(), + overridenValues.number, + ); + assert.equal( + textField.dataValue, + overridenValues.textField, + ); + assert.equal( + textArea.dataValue, + overridenValues.textArea, + ); + assert.equal( + radio.dataValue, + overridenValues.radio, + ); + + checkbox.setValue(false); + + setTimeout(() => { + // check if conditional components were hidden + assert.equal(number.visible, false); + assert.equal(textField.visible, false); + assert.equal(textArea.visible, false); + assert.equal(radio.visible, false); + + checkbox.setValue(true); + setTimeout(() => { + // make sure that components appear again and values were recalculated + assert.equal(number.visible, true); + assert.equal(textField.visible, true); + assert.equal(textArea.visible, true); + assert.equal(radio.visible, true); + + assert.equal( + number.dataValue, + calculatedValues.number, + ); + assert.equal( + textField.dataValue, + calculatedValues.textField, + ); + assert.equal( + textArea.dataValue, + calculatedValues.textArea, + ); + assert.equal( + radio.dataValue, + calculatedValues.radio, + ); + document.body.innerHTML = ''; + + done(); + }, 300); + }, 300); + }, 300); + }, 400); + }) + .catch((err) => done(err)); + }); + }); - dateTimeComponent.setValue('2020-10-19T00:00:00'); - setTimeout(() => { - assert.equal(dateTimeComponent.dataValue, '2020-10-19T00:00:00', 'Should set value'); - assert(!dateVisibleInput.className.includes('is-invalid'), 'Invalid class should be removed'); - assert(!flatpickerInput.className.includes('is-invalid'), 'Invalid class should be removed from flatpickr field as well'); - done(); - }, 300); - }, 400); - }, 300); - }, 300); - }, 300); - }, 350); - }).catch(done); - }).timeout(3000); - - it('Should have number and currency fields in empty form submission', function(done) { - const formElement = document.createElement('div'); - const form= new Webform(formElement); - const formJson = { - components: [ - { - label: 'Number', - key: 'number', - type: 'number' - }, - { - label: 'Currency', - key: 'currency', - type: 'currency' - }, - { - type: 'button', - label: 'Submit', - key: 'submit' - }, - ], - }; + describe('Modal Edit', function () { + const submission = { + state: 'submitted', + data: { + checkbox: true, + selectBoxes: { + a: true, + b: true, + }, + textfield: 'My Text', + select: 'f', + submit: true, + }, + }; + const componentsKeys = [ + 'checkbox', + 'selectBoxes', + 'select', + 'textfield', + ]; + const expectedValues = { + checkbox: 'Yes', + selectBoxes: 'a, b', + select: 'f', + textfield: 'My Text', + }; - const emptySubmissionData = { - submit: true - }; + it('Test rendering previews after the submission is set', function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement, { language: 'en' }); + form.setForm(modalEditComponents) + .then(() => { + return form + .setSubmission(submission, { fromSubmission: true }) + .then(() => { + componentsKeys.forEach((key) => { + const comp = form.getComponent([key]); + assert(comp); + const preview = + comp.componentModal.refs.openModal; + assert(preview); + assert.equal( + preview.textContent + .replace(/\n|\t/g, '') + .trim(), + expectedValues[key], + ); + }); + done(); + }); + }) + .catch(done); + }); - form.setForm(formJson).then(() => { - const clickEvent = new Event('click'); - const submitBtn = form.element.querySelector('[name="data[submit]"]'); + it('Test updating previews after aboting changes', function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement, { language: 'en' }); + form.setForm(modalEditComponents) + .then(() => { + return form + .setSubmission(submission, { fromSubmission: true }) + .then(() => { + const comp = form.getComponent(['textfield']); + comp.componentModal.openModal(); + Harness.dispatchEvent( + 'input', + comp.componentModal.refs.modalContents, + '[name="data[textfield]"]', + (el) => { + el.value = 'My Text v2'; + }, + ); + setTimeout(() => { + const fakeEvent = { + preventDefault: () => {}, + }; + comp.componentModal.closeModalHandler( + fakeEvent, + ); + const preview = + comp.componentModal.refs.openModal; + assert.equal( + preview.textContent + .replace(/\n|\t/g, '') + .trim(), + 'My Text', + ); + done(); + }, 100); + }); + }) + .catch(done); + }); + }); - submitBtn.dispatchEvent(clickEvent); + describe('Initially Collapsed Panel', function () { + const formElement = document.createElement('div'); + const form = new Webform(formElement, { language: 'en' }); + form.setForm(initiallyCollapsedPanel) + .then(() => { + it('Should be collapsed', function (done) { + try { + const panelBody = + form.element.querySelector('[ref=nested-panel]'); + assert.equal( + panelBody, + null, + "Should not render the panel's body when initially collapsed", + ); + done(); + } catch (err) { + done(err); + } + }); + it('Should open when an Error occured', function (done) { + form.executeSubmit().catch(() => { + try { + const panelBody = + form.element.querySelector( + '[ref=nested-panel]', + ); + assert( + panelBody, + 'Should open the panel when an error occured', + ); + done(); + } catch (err) { + done(err); + } + }); + }); + }) + .catch((err) => console.error(err)); + }); - setTimeout(() => { - assert.deepEqual(form.data, emptySubmissionData); - done(); - }, 400); - }) - .catch((err) => done(err)); - }); + describe('Calculate Value', function () { + it('Should calculate value when set submission if the component is not persistent', function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement, { + language: 'en', + pdf: true, + }); + form.setForm(calculatedNotPersistentValue) + .then(() => { + form.setSubmission({ + data: { + a: 'testValue', + }, + state: 'submitted', + }); + setTimeout(() => { + const persistentField = form.getComponent(['a']); + assert.equal( + persistentField.dataValue, + 'testValue', + 'Should set the value from the submission', + ); + const notPersistentFieldInput = + form.element.querySelector( + 'input[name="data[textField]"]', + ); + assert.equal( + notPersistentFieldInput.value, + 'testValue', + 'Should calculate the value', + ); + done(); + }, 550); + }) + .catch(done); + }); - it('Test Truncate Multiple Spaces', function(done) { - const formElement = document.createElement('div'); - const form= new Webform(formElement); - - form.setForm(truncateMultipleSpaces).then(() => { - const textFieldRequired = form.getComponent(['textField1']); - const textFieldMinMaxLength = form.getComponent(['textField']); - const textAreaMinMaxLength = form.getComponent(['textArea']); - Harness.dispatchEvent('input', textFieldRequired.element, 'input', (i) => i.value = ' '); - Harness.dispatchEvent( - 'input', - textFieldMinMaxLength.element, - 'input', - (i) => i.value = ' 546 456 ' - ); - Harness.dispatchEvent( - 'input', - textAreaMinMaxLength.element, - 'textarea', - (i) => i.value = ' 546 456 ' - ); - - setTimeout(() => { - assert.equal(textFieldRequired.dataValue, ' ', 'Should set value'); - assert.equal(textFieldMinMaxLength.dataValue, ' 546 456 ', 'Should set value'); - assert.equal(textAreaMinMaxLength.dataValue, ' 546 456 ', 'Should set value'); - - assert.equal(textFieldRequired.errors.length, 1, 'Should be invalid since it does not have a value'); - assert.equal( - textFieldMinMaxLength.errors.length, - 0, - 'Should be valid since it value does not exceed the max length after truncating spaces' - ); - assert.equal( - textAreaMinMaxLength.errors.length, - 0, - 'Should be valid since it value does not exceed the max length after truncating spaces' - ); + it('Should calculate value by datasouce component when editing mode is on', function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement, { + language: 'en', + pdf: true, + }); + form.setForm(calculateValueInEditingMode) + .then(() => { + form.editing = true; + form.setSubmission({ + data: { + select: { label: 'Dummy #1', value: 'dummy1' }, + dataSourceDisplay: 'some value', + }, + state: 'submitted', + }).then(() => { + const dataSource = form.getComponent('datasource'); + dataSource.dataValue = { value: 'some value' }; + form.checkData(null, { + dataSourceInitialLoading: true, + }); + setTimeout(() => { + const dataSourceDisplay = + form.getComponent('dataSourceDisplay'); + assert.equal( + dataSourceDisplay.dataValue, + 'some value', + 'Should set and keep the value', + ); + done(); + }, 1000); + }); + }) + .catch(done); + }); - form.submit(false, {}).finally(() => { - assert.equal(textFieldRequired.dataValue, '', 'Should truncate the value before submit'); - assert.equal(textFieldMinMaxLength.dataValue, '546 456', 'Should truncate the value before submit'); - assert.equal(textAreaMinMaxLength.dataValue, '546 456', 'Should truncate the value before submit'); + it('Should calculate value properly in editing mode', function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement, { + language: 'en', + pdf: true, + }); + form.setForm(calculatedValue) + .then(() => { + form.editing = true; + form.setSubmission({ + data: { + a: 4, + b: 5, + total: 9, + }, + state: 'submitted', + }); + setTimeout(() => { + const total = form.getComponent(['total']); + assert.equal( + total.dataValue, + 9, + 'Should set and keep the value', + ); + + const b = form.getComponent(['b']); + Harness.dispatchEvent( + 'input', + b.element, + 'input', + (i) => (i.value = '6'), + ); + + setTimeout(() => { + assert.equal( + total.dataValue, + 10, + 'Should recalculate the value', + ); + }, 300); + done(); + }, 1000); + }) + .catch(done); + }); - done(); + it('Should not override value which was set from submission', function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement, { + language: 'en', + pdf: true, + }); + form.setForm(calculateValueWithManualOverrideLableValueDataGrid) + .then(() => { + form.editing = true; + form.setSubmission({ + state: 'submitted', + data: { + dataGrid: [ + { label: '1', value: '1a' }, + { label: '2', value: '2a' }, + { label: '3', value: '3a' }, + ], + }, + }); + setTimeout(() => { + const value1 = form.getComponent([ + 'dataGrid', + 0, + 'value', + ]); + assert.equal( + value1.dataValue, + '1a', + 'Should have a value set from submission', + ); + const value2 = form.getComponent([ + 'dataGrid', + 1, + 'value', + ]); + assert.equal( + value2.dataValue, + '2a', + 'Should have a value set from submission', + ); + const value3 = form.getComponent([ + 'dataGrid', + 2, + 'value', + ]); + assert.equal( + value3.dataValue, + '3a', + 'Should have a value set from submission', + ); + done(); + }, 1000); + }) + .catch(done); }); - }, 400); - }).catch(done); - }); - - it('HTML render mode for Webform', function(done) { - const element = document.createElement('div'); - - const originalMakeRequest = Formio.makeRequest; - Formio.makeRequest = function() { - return new Promise(resolve => { - setTimeout(() => { - const values = [ - { - _id: '5a53f8a044398b0001023eab', - modified: '2019-02-01T16:12:06.618Z', - data: { - firstName: 'Bob', - lastName: 'Thompson', - status: 'inactive', - email: 'bob@example.com', - submit: true, - }, - form: '5a53f887c8930000010f8b22', - _fvid: 0, - _vid: 0, - created: '2018-01-08T23:02:56.484Z', - externalIds: [], - access: [], - roles: [], - owner: '553dbfc08d22d5cb1a7024f2', - state: 'submitted', - project: '5692b91fd1028f01000407e3', - }, { - _id: '5a53f8ad0dc919000194ab6b', - modified: '2019-02-01T16:12:01.781Z', - data: { - firstName: 'Sally', - lastName: 'Tanner', - status: 'active', - email: 'sally@example.com', - submit: true, - }, - form: '5a53f887c8930000010f8b22', - _fvid: 0, - _vid: 0, - created: '2018-01-08T23:03:09.730Z', - externalIds: [], - access: [], - roles: [], - owner: '553dbfc08d22d5cb1a7024f2', - state: 'submitted', - project: '5692b91fd1028f01000407e3', - }, { - _id: '5a53f8b744398b0001023eaf', - modified: '2019-02-01T16:11:57.139Z', - data: { - firstName: 'Jane', - lastName: 'Doe', - status: 'active', - email: 'jane@example.com', - submit: true, - }, - form: '5a53f887c8930000010f8b22', - _fvid: 0, - _vid: 0, - created: '2018-01-08T23:03:19.473Z', - externalIds: [], - access: [], - roles: [], - owner: '553dbfc08d22d5cb1a7024f2', - state: 'submitted', - project: '5692b91fd1028f01000407e3', - }, - ]; - resolve(values); - }, 50); - }); - }; + }); - Formio.createForm(element, htmlRenderMode, { - readOnly: true, - renderMode: 'html', - }) - .then(form => { - form.submission = { - data: { - textfieldonPage3: 'test', - signature: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABTIAAACWCAYAAADt5XcLAAAgAElEQVR4Xu3dfWydV30H8NM1dLRdndIOKKJxGHSh0MalgBilpLAJyIsatQgJu4rT8peNiFOEhl0pyR8gxZHq/DGJJAhHCNbEEU5BHVWyJpWYuja0tOPdSctLQVBHWmAwkTi0lEFh+j3h3tqOG9vJvfa59/kcybKT3Ps85/mcE0f+5nfOOe/ZZ3/156QRIECAAAECBAgQIECAAAECBAgQIEAgY4HzBJkZj46uESBAgAABAgQIECBAgAABAgQIECBQCAgyTQQCBAgQIECAAAECBAgQIECAAAECBLIXEGRmP0Q6SIAAAQIECBAgQIAAAQIECBAgQICAINMcIECAAAECBAgQIECAAAECBAgQIEAgewFBZvZDpIMECBAgQIAAAQIECBAgQIAAAQIECAgyzQECBAgQIECAAAECBAgQIECAAAECBLIXEGRmP0Q6SIAAAQIECBAgQIAAAQIECBAgQICAINMcIECAAAECBAgQIECAAAECBAgQIEAgewFBZvZDpIMECBAgQIAAAQIECBAgQIAAAQIECAgyzQECBAgQIECAAAECBAgQIECAAAECBLIXEGRmP0Q6SIAAAQIECBAgQIAAAQIECBAgQICAINMcIECAAAECBAgQIECAAAECBAgQIEAgewFBZvZDpIMECBAgQIAAAQIECBAgQIAAAQIECAgyzQECBAgQIECAAAECBAgQIECAAAECBLIXEGRmP0Q6SIAAAQIECBAgQIAAAQIECBAgQICAINMcIECAAAECBAgQIECAAAECBAgQIEAgewFBZvZDpIMECBAgQIAAAQIECBAgQIAAAQIECAgyzQECBAgQIECAAAECBAgQIECAAAECBLIXEGRmP0Q6SIAAAQIECBAgQIAAAQIECBAgQICAINMcIECAAAECBAgQIECAAAECBAgQIEAgewFBZvZDpIMECBAgQIAAAQIECBAgQIAAAQIECAgyzQECBAgQIECAAAECBAgQIECAAAECBLIXEGRmP0Q6SIAAAQIECBAgQIAAAQIECBAgQICAINMcIECAAAECBAgQIECAAAECBAgQIEAgewFBZvZDpIO1Fjhx4kQaGTlSXHbhwoXFx6WXthSfNQIECBAgQIAAAQIECBAgQIAAgTwFBJl5jotezUIggslnnjmaTpwYS/F1fBw/furrZ54Z/cufnfr9eN2Z2uLFi1Jb29J06vO16d3vvrH4WiNAgAABAgQIECBAgAABAgQIEJhfAUHm/Pq7+ywFIoj8+tcfTbt3D6fR0VMhZT3bggUL0sUXX1yEmjfddGO6+eaVxdcaAQIECBAgQIAAAQIECBAgQIDA3AoIMufW293OUmDfvgfSF7+4Oz3++DeLysrZtNbWRdWqymXLbizeGsvIr7tuYiAZoWhUcEY7dOjRdPz4ifT00z9Nzz///ITbRYXmunUfTevWdc2mG15LgAABAgQIECBAgAABAgQIECBwDgKCzHPA89b6CkSwuGfPcBoa+lJReblkyVVpyZK/T6tXr0oRSFaWfFf2u4zeRMjZ2tpa0z0v494RbEaYun//gepDR2Xm4OA2FZr1nQauToAAAQIECBAgQIAAAQIECBAoBASZJkJWAhFERlgYS8cjPIzW0tKSenq6U2fnbfO+X2WEmhGsbtmytehbVHbu3XtPEaxqBAgQIECAAAECBAgQIECAAAEC9RMQZNbP1pVnIRAB4ZYtA2nfvgNFVWWEl7H0O8LLzs6OWVxpbl4aIWtX1/o0Onpqj84NG3rTxo19c3NzdyFAgAABAgQIECBAgAABAgQIlFBAkFnCQc/pkYeGhtOOHYOpsjw8Asy1azuKADP3Q3UicF2+/JZ0+PCTBenVVy9J9903PO9VozmNr74QIECAAAECBAgQIECAAAECBGolIMislaTrzFggAsAdO3ZW976MN8aBPFHRmGP15ZkeLJ6lt3dTsZdnpe3cua043TyWnWsECBAgQIAAAQIECBAgQIAAAQK1ERBk1sbRVWYgUAkwt28frJ48vmzZu4oAs9H3mPzCF3al9ev/uaoQBxLdffdm1ZkzmBdeQoAAAQIECBAgQIAAAQIECBCYiYAgcyZKXnPOAv39A0UVZoSZ0das6SgCzMrJ4+d8gwwuEPt8trevrS41j2fbsKHxqkwzoNQFAgQIECBAgAABAgQIECBAgMBpAoJMk6KuAvv2PZDuumtTipCvWQPMyYAR2kbV6djYWPFHsVx+YGCzpeZ1nWkuToAAAQIECBAgQIAAAQIECDS7gCCz2Ud4np4vgsvu7vUpTveOFntGDgz0N1UF5ploJ59qHtWZw8O7sj/AaJ6mi9sSIECAAAECBAgQIECAAAECBKYVEGROS+QFsxGIpeNbtmwtKhKjNcsemLMxqLx28kFAcfhPT0932rCh92wu5z0ECBAgQIAAAQIECBAgQIAAgVILCDJLPfy1ffioQowqzKjGXLr0mtTT89GGO4W8tiKnrhahbiw3ryw1dxBQPZRdkwABAgQIECBAgAABAgQIEGh2AUFms4/wHDzfyMiRIqjbv/9Acbc4rXvt2g57Qo6zD6Ourp4JBwEdOHB/aZbaz8E0dAsCBAgQIECAAAECBAgQIECgyQUEmU0+wPV8vPHLyK+88rXpLW9pK9U+mLO1Da+urvXVwDeWmg8OfiZFhaZGgAABAgQIECBAgAABAgQIECBwZgFBphlyVgKxXHrPnuEUlYatrYuKU7kFcjOjHL/U3L6ZMzPzKgIECBAgQIAAAQIECBAgQICAINMcmJVABJexD2Z8jrZmTUfaunWzZeSzUkzFae5RnTk6erR4Z2dnRxoc3DbLq3g5AQIECBAgQIAAAQIECBAgQKA8AoLM8oz1OT9pBG9RhRktqjB37tyWli278ZyvW9YLxKFI3d096dChxwqCsNy79x6hcFknhOcmQIAAAQIECBAgQIAAAQIEziggyDRBphXYt++B1N19Z4o9HqOtW9eVNm7sE7hNKzf9C8I0lppv2bK1ePHixYuSQ4Cmd/MKAgQIECBAgAABAgQIECBAoHwCgszyjfmMnzhCtr6+TWloSBXmjNHO8oURFnd03FENM+Pkd3uOniWmtxEgQIAAAQIECBAgQIAAAQJNKSDIbMphPfeHij0c29vvUIV57pQzvkIsNW9vX5sOH36yqHatVL7O+AJeSIAAAQIECBAgQIAAAQIECBBoYgFBZhMP7tk+Wix1vuuuTcXb7YV5topn976ogu3t3VTdizQOU4q9SDUCBAgQIECAAAECBAgQIECAQNkFBJllnwHjnj9CtNgLM5Y5R1u27F1pcHB7sW+jNrcClaXmL3/5y9OSJVelgwe/ak/SuR0CdyNAgAABAgQIECBAgAABAgQyExBkZjYg89WdCDFXrLg1jYwcKbrgQJ/5GokX7xtjsnz5LcVS82gRZjolfv7HRQ8IECBAgAABAgQIECBAgACB+REQZM6Pe1Z3jf0wu7vXp9ijsaWlJfX0dBenkmt5CPT1bUw7duwsOhPjsmFDbx4d0wsCBAgQIECAAAECBAgQIECAwBwKCDLnEDvHW8US5lhOHtV/EWLee+8uVX8ZDlScHN/buzGNjY0V4xPVmRoBAgQIECBAgAABAgQIECBAoEwCgswyjfakZx1/qM/SpdeknTu3p7a2a0sskvejR+VsV9f6NDp6tNi3dHh4l/HKe8j0jgABAgQIECBAgAABAgQIEKihgCCzhpiNdKn+/oG0ZcvWoss337yyOBl74cKFjfQIpezr+H0zY7wGBz+TVq9eVUoLD02AAAECBAgQIECAAAECBAiUS0CQWa7xLp62vf32tH//geLrONRnYKC/hAqN+8gRZsYYHjr0WPEQ9s1s3LHUcwIECBAgQIAAAQIECBAgQGDmAoLMmVs1xSvf+c73Vk/BvvvuzcXBPlpjCoyvqo2qzKjOVFXbmGOp1wQI1E4gDq6LLTiitbVd4/ti7WhdiQABAgQIECBAgMC8Cwgy530I5qYDIyNH0gc+sDqdPPnb4oZxWEwcGqM1tkAc1tTRcUfxEDGeg4Pbiv0zNQIECJRJIMLLT32qPz300MPpV7/69YRHj72f43ujPaDLNCM8KwECBAgQIECAQLMKCDKbdWTHPVflUJ/zzjsvXXLJJemHP/yOCpUmGvcIqWOpuUOAmmhQPQoBAjMSiADzs58dTLt3D6fYduOl2po1HcVe0BoBAgQIECBAgAABAo0tIMhs7PE7Y+/jh7ru7jtTVO1Fa2lpSceO/bSJn7i8jzZ538yoPurs7CgviCcnQKDpBQ4dejR1d69PEWZO1+wHPZ2QPydAgAABAgQIECDQGAKCzMYYp1n3Mn6wW7nyluoPeELMWRM25Bv6+jamHTt2Fn2PIHPDhj5LzRtyJHWaAIEzCZwpxHzFKy5Nq1atSKOjo+n48ROprW1pcSiabTfMKQIECBAgQIAAAQKNLyDIbPwxPO0JJv+A19q6KD3++EOWkzfhWE/1SFGB29W1Po2NjRV7wg0MbLYfaknG3mMSKINA/BvX3n7HaUvJly17VxFY2v+5DLPAMxIgQIAAAQIECJRVQJDZZCM//vCXeLQIMQ8evF8lSpON83SPExW5UZ25f/+BIsCOZZXxA75GgACBRhaIEPPDH769+I+a8e3uuzennp7uRn40fSdAgAABAgQIECBAYAYCgswZIDXKS4aGhov9wsa3p576jhCzUQawDv2szImWlkvS6163OA0P7zIf6uDskgQI1F9g8pYpccfYNiUO8Vm9elX9O+AOBAgQIECAAAECBAjMu4Agc96HoDYdmBxixg939967yxK72vA29FXih//29rXp8OEni+eIH/pvvnmlrQYaelR1nkD5BGLLjD17hqsPHv9Bc++9u/07V76p4IkJECBAgAABAgRKLCDIbILBj6V2K1bcOuFJhofvUaHSBGNby0fo7x9IEXiPjh4tfvCPk80dflFLYdciQKBeApP/nfOfdfWSdl0CBAgQIECAAAECeQsIMvMen2l7N9Vy8g0beu2HOK1cOV8QYUBv78ZqdWbsmxnzRSNAgECuAlMtKbcnZq6jpV8ECBAgQIAAAQIE6isgyKyvb12vHgf7dHffOeHk1jjUZWCgv673dfHGFjhx4kTavn0wbdmytXiQqMqM6kwn/Tb2uOo9gWYUGBk5kvr6NqX4T5hKi9PJ4xA7jQABAgQIECBAgACB8gkIMht0zOOHu46O21NUqlRa7HsY+x/GKdUagekEYg51dfVUqzM7OzvSwMBm82c6OH9OgMCcCER4edttH0m/+c3x6v1aWxcVIaZtMeZkCNyEAAECBAgQIECAQHYCgszshmT6Dk21zG7p0mvSgw/eL4Sans8rJgnE3pmV6swIwbdu3ZzWrOngVAOBqH49fnysuNKll7b4+1kD02a9RMyVkZEniwr7+Dh27H/SsWP/nS6//PLi18ePx++fmkvRIsiLv6/PP//79Oyzvy1eN1274IK/Tn/84x/SokWvLeZlXHeqFtdM6c/p1a9+9YTXxOvj35+FC1uKt1144YVpwYIF6frr24pfL116bWpru3a6bkz757HaYM+evSk+j2/x79zWrf2qx6cV9AICBAgQIECAAAECzSsgyGywsZ0qxIwKlccff0hI0mBjmVN3Y151d/ekQ4ceK7oVy8w3buwVGMxgkKKy9Yknvpl+/vPRIiT6/vePpPi9lwqJIuiJACqCqMWLW6tfv+xlL0sXXXRR9Y6VQKsShL7461OBVgRbU7W47qWXLqxeN+4VwZOtA2YwmHPwkpgbP/jBj9LTT/8knTx5spgv45dNz0EX6n6LG274h/Tcc8+lWAIebfwqgZjHv//9/6Wf/ezn6eqrlxR/Fh8jI4fTj370dPrxj38y5d+deO0XvzhYk6C07gBuQIAAAQIECBAgQIBA3QQEmXWjrf2Fp1pOHiHm3r27/HBXe+5SXjEqoKJC8/DhJ4vnX716VYpDNSzjnDgdIoyJKtbwGr+9Q86T5oILLigq6KIytLW1NV133bXF5/h1rSrpcn7++ehbfM+O/xyIkC6+jg9t9gIRiMa/c7ZNmb2ddxAgQIAAAQIECBBoNgFBZoOM6FQHHrS0tKTHH/9PIVODjGEjdTMOA4pAc2xsrAgPenq6UxwkJUhIaWhoOHV3r5/X4Yy/+1F1OT5gnjw24ytCo+rvD3/4Q/rd7353xn5HtWh83HTTjend777R95ZZjHJ4P/bYE+nb3/5uOnLkqdOWRc/kUrF0OsY12hvfuKT4+3fVVW8441tPLS1/Nl1++WXT3uLFpeVX/mWp+tRVvfG6kyfH0qte9arimvFslfl1qtq3pagKPnbsl0UV8p/+9KeiqjRC/dHRF/dtnrZD07wgAsx167qL/1DRCBAgQIAAAQIECBAgEAKCzAaYB/EDYn//1gnLDx140AAD1+BdjPCit3dT2rNnuHiSsp9uHh7d3XdOGVBdcsklacmSq9KHP/yhwinCnsoS8so0qASLk6vyKkvEo2LyoosunDBrKuFRfK7VHpuVZe8ROlUqBaMPlSrc8R2IZ4kQKZalR6gkyH5RJ/z27z9QfF8Ox9lU5t5wwzvSxRf/TXr/+/+pWhnbLFXPMb8efPA/0hNP/Fe67LLL0ujo6GnfCa+44op07NixdN11S4sq1bD79a//Ny1YcH56+9vflpYvf18x75rFpMH/KdB9AgQIECBAgAABAlkJCDKzGo7TOxOVcZ/+dH967rkXK6miamfnzu2Wk2c+ds3SvVPVwBur+2eWcbn5VBXRMb4R7m3c2NcU+09G0PrIIxHKndqzsbJf6vh5XKnWLGuwefTo0RTfkytOM/k7Ht+v29qWFlWulYrXmbzPawgQIECAAAECBAgQIEDgdAFBZqazIoKEvr5Np+2p5tTWTAesBN2KJdXbt3+uqNyLSqkNG/pSZ2fzn24ewd6qVR9Mv/nN8eoox9LuCDBjyX0zt/g+FKFd7AU6VcVmJZiLoK6t7ZqmCHSnGs+YA5U9Uacb77e97fqiMjf2IHXA0nRa/pwAAQIECBAgQIAAAQKzExBkzs6r7q+uHCISVT+T2ytecWl64IF/U4lZ91Fwg5cSiPkZczNCnZaWS9LrXrc4DQ/vatoloBFgrVhx64RTlMtaET25YjP23Yw9HMe317zmivTKV/5tsQQ9KhDjc4SdsRVGIy4Tni7AjOeKsHL16pXFCfTxrBoBAgQIECBAgAABAgQI1E9AkFk/21lfOYKCCE1e6mTbgwe/qsJn1qreUA+B2NOuvX1ttUpvYKA/dXa2N90eim9601snHF7y+tf/Xdqz5wsCq79MqvH7bEb15i9+8cv09NM/fckpF2FmnJQeLSoWK3tuxu/H16eCz2uKP5+v/TjjoJ6HH/56Ghr60pTfi2M/1Ntvvy11dt5mHtTjm4trEiBAgAABAgQIECBA4AwCgsyMpsfk0GR81+6+e3PTL2PNaCh0ZYYCsdw8TjePk4qjGm1gYHPThO2TTyePSsy9e3c3ZGXhDIezZi+rnGD9zDOjfznMZbQ40GVyBeeZblip5ozXRNAZFY+VryuhZ/z6/PP/Kr3wwgtxdl1xmnblYKTxYWjloKXjx8eK6tr4OP/889MLL/yp+LpyANKZ9r6M6suY307Qrtk0cSECBAgQIECAAAECBAjMWkCQOWuy+rzhNa95w0v+kD84uK0UexHWR9ZV6y0QAVV3d0/1cJjYO3LDht5637bu13/nO987YV/Ib3zjIRV4NVAff2p6BJ3RKr/3ve8dTidPnqzBXWp3iTjQKaovy7AfbO3UXIkAAQIECBAgQIAAAQL1ERBk1sd1VleNgzQ6Ou447T1xoMi99+5qmgq3WaF4ccMJxN6Zd921qeh3VGc28t6Zk/9OrlnTkXbu3NZwY9KIHY4KyaicHB09VcUZYWd8jl8fP35i1pWdszV485uvThdeeGFxYE9UXzbi3p6zfWavJ0CAAAECBAgQIECAQKMICDIzGKmpgswIMR988H4VYBmMjy7MXCAq69rbby+WmscS36jOXLeua+YXyOSV73nP8vStb32n2hv702YyMOO6EYFnBJzRToWfsWR8rPhc+b3JvR6/J+cFF1yQLrrowmKexvvHL2XP72n1iAABAgQIECBAgAABAgRCQJCZyTwYv7Q8Qsxjx176wIxMuqwbBKYUiFCoq2t92r//QPHncapz7C3YKCc6Rzj25je/tfpsN9zwjvS1r/270SZAgAABAgQIECBAgAABAgTmWUCQOc8DMP72UZkZzWESGQ2Krpy1wORK456e7vSxj3Vnv1R3/BL5ePgdO/4lfeQjnWft4I0ECBAgQIAAAQIECBAgQIBAbQQEmbVxdBUCBKYQmFydGfsNfuhDH0yf/OSdxVLeHNuKFbdUDy6Kk6p/8IMXl5jn2F99IkCAAAECBAgQIECAAAECZREQZJZlpD0ngXkUOHTo0dTbu3HCKeADA/2ps7M9u0Bz/DYPDvmZx0nj1gQIECBAgAABAgQIECBAYJKAINOUIEBgTgSiOnPv3vvSJz7RV71fVGiuW/fRbALNoaHh1N29vtq/wcFtqbOzY0583IQAAQIECBAgQIAAAQIECBA4s4Ag0wwhQGBOBSLQ7O8fSPv2HShON4+WS6C5cuWt6ZFHHq16PPXUd7Lf03NOB8/NCBAgQIAAAQIECBAgQIDAPAoIMucR360JlFkgTgffseNzaceOnVWG2DczDgWKJd0Rbs5lm3xa+bJl70oHD94/l11wLwIECBAgQIAAAQIECBAgQOAMAoJM04MAgXkVmCrQjA6tXr0qrV69sgg156L19W2cEKred9+X0vLl75uLW7sHAQIECBAgQIAAAQIECBAgMAMBQeYMkLyEAIH6C8SS8+3bB4uPsbGx6g2jSnPt2o4i0Gxru7YuHYnDiFasuLV6bdWYdWF2UQIECBAgQIAAAQIECBAgcE4Cgsxz4vNmAgTqIbBv3wNpx47BdOjQYxMuH0Hmxz++Lq1c+f6anXY+MnKkOOAnPlfa8PA9RUWoRoAAAQIECBAgQIAAAQIECOQjIMjMZyz0hACBSQKx7Hxo6EspThOvHAxUeUmcJh5h4803rzwnt5tu+kD69re/W71GXG/v3l3ndE1vJkCAAAECBAgQIECAAAECBGovIMisvakrEiBQB4FY/v2Vr3w1ff7z/3ra1ZctuzFdffWStGDBgnT99W1p6dJrZ7QMPU5P37Jl64QQc+fObTWr9qwDg0sSIECAAAECBAgQIECAAIHSCggySzv0HpxAYwrEXpq7dw8XS88nV2lOfqIIOG+66cYi1Iy9NqP99rfPph//+On0jW88kWIJe6VdeeVr05e/PDSjALQx5fSaAAECBAgQIECAAAECBAg0toAgs7HHT+8JlFog9rWMpef79h2YNtQ8E1Rr66J08OD9afHiRaX29PAECBAgQIAAAQIECBAgQCBnAUFmzqOjbwQIzFjg6NGj6eGHH00jI4eLg3smHxT0UheKE8oHB7cLMWcs7YUECBAgQIAAAQIECBAgQGB+BASZ8+PurgQIzIFA7Kv5yCOPpjg0aHR0tLjjFVdcUXx+3/v+sVhGHh8aAQIECBAgQIAAAQIECBAgkL+AIDP/MdJDAgQIECBAgAABAgQIECBAgAABAqUXEGSWfgoAIECAAAECBAgQIECAAAECBAgQIJC/gCAz/zHSQwIECBAgQIAAAQIECBAgQIAAAQKlFxBkln4KACBAgAABAgQIECBAgAABAgQIECCQv4AgM/8x0kMCBAgQIECAAAECBAgQIECAAAECpRcQZJZ+CgAgQIAAAQIECBAgQIAAAQIECBAgkL+AIDP/MdJDAgQIECBAgAABAgQIECBAgAABAqUXEGSWfgoAIECAAAECBAgQIECAAAECBAgQIJC/gCAz/zHSQwIECBAgQIAAAQIECBAgQIAAAQKlFxBkln4KACBAgAABAgQIECBAgAABAgQIECCQv4AgM/8x0kMCBAgQIECAAAECBAgQIECAAAECpRcQZJZ+CgAgQIAAAQIECBAgQIAAAQIECBAgkL+AIDP/MdJDAgQIECBAgAABAgQIECBAgAABAqUXEGSWfgoAIECAAAECBAgQIECAAAECBAgQIJC/gCAz/zHSQwIECBAgQIAAAQIECBAgQIAAAQKlFxBkln4KACBAgAABAgQIECBAgAABAgQIECCQv4AgM/8x0kMCBAgQIECAAAECBAgQIECAAAECpRcQZJZ+CgAgQIAAAQIECBAgQIAAAQIECBAgkL+AIDP/MdJDAgQIECBAgAABAgQIECBAgAABAqUXEGSWfgoAIECAAAECBAgQIECAAAECBAgQIJC/gCAz/zHSQwIECBAgQIAAAQIECBAgQIAAAQKlFxBkln4KACBAgAABAgQIECBAgAABAgQIECCQv4AgM/8x0kMCBAgQIECAAAECBAgQIECAAAECpRcQZJZ+CgAgQIAAAQIECBAgQIAAAQIECBAgkL+AIDP/MdJDAgQIECBAgAABAgQIECBAgAABAqUXEGSWfgoAIECAAAECBAgQIECAAAECBAgQIJC/gCAz/zHSQwIECBAgQIAAAQIECBAgQIAAAQKlFxBkln4KACBAgAABAgQIECBAgAABAgQIECCQv4AgM/8x0kMCBAgQIECAAAECBAgQIECAAAECpRcQZJZ+CgAgQIAAAQIECBAgQIAAAQIECBAgkL+AIDP/MdJDAgQIECBAgAABAgQIECBAgAABAqUXEGSWfgoAIECAAAECBAgQIECAAAECBAgQIJC/gCAz/zHSQwIECBAgQIAAAQIECBAgQIAAAQKlFxBkln4KACBAgAABAgQIECBAgAABAgQIECCQv4AgM/8x0kMCBAgQIECAAAECBAgQIECAAAECpRcQZJZ+CgAgQIAAAQIECBAgQIAAAQIECBAgkL+AIDP/MdJDAgQIECBAgAABAgQIEL7bi68AAAGrSURBVCBAgAABAqUXEGSWfgoAIECAAAECBAgQIECAAAECBAgQIJC/gCAz/zHSQwIECBAgQIAAAQIECBAgQIAAAQKlFxBkln4KACBAgAABAgQIECBAgAABAgQIECCQv4AgM/8x0kMCBAgQIECAAAECBAgQIECAAAECpRcQZJZ+CgAgQIAAAQIECBAgQIAAAQIECBAgkL+AIDP/MdJDAgQIECBAgAABAgQIECBAgAABAqUXEGSWfgoAIECAAAECBAgQIECAAAECBAgQIJC/gCAz/zHSQwIECBAgQIAAAQIECBAgQIAAAQKlFxBkln4KACBAgAABAgQIECBAgAABAgQIECCQv4AgM/8x0kMCBAgQIECAAAECBAgQIECAAAECpRcQZJZ+CgAgQIAAAQIECBAgQIAAAQIECBAgkL+AIDP/MdJDAgQIECBAgAABAgQIECBAgAABAqUXEGSWfgoAIECAAAECBAgQIECAAAECBAgQIJC/gCAz/zHSQwIECBAgQIAAAQIECBAgQIAAAQKlFxBkln4KACBAgAABAgQIECBAgAABAgQIECCQv8D/A4EQEWXi+YzZAAAAAElFTkSuQmCC', - panelDataGrid: [ - { - panelDataGridD: 'd', - panelDataGridC: 'c', - panelDataGridB: 'b', - panelDataGridA: 'a', - }, { - panelDataGridD: 'h', - panelDataGridC: 'g', - panelDataGridB: 'f', - panelDataGridA: 'e', - }, { - panelDataGridD: 'l', - panelDataGridC: 'k', - panelDataGridB: 'j', - panelDataGridA: 'i', - }, - ], - textfield: 'testing', - page2Customer: 'bob@example.com', - textfieldonPage2: 'test', - numberField: 234, - textfieldonpage1: [ - 'a', 'b', 'c', - ], - panelHtml5Select: 'banana', - page3Iagreetothefollowtherules: true, - panelText: 'hello', - }, - }; + it('Should set different ids for components inside different Table rows', function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement, { language: 'en', pdf: true }); + form.setForm(conditionalDataGridWithTableAndRadio) + .then(() => { + const radioInspection0 = form.getComponent([ + 'inspectionDataGrid', + 0, + 'initialExam', + ]); + Harness.dispatchEvent( + 'click', + radioInspection0.element, + 'input[value="reject"]', + (i) => (i.checked = true), + ); - setTimeout(() => { - const customerSelectEl = form.element.querySelector('.formio-component-page2Customer'); - const customerSelectValueEl = customerSelectEl.querySelector('[ref="value"]'); - const htmlSelectEl = form.element.querySelector('.formio-component-panelHtml5Select'); - const htmlSelectValueEl = htmlSelectEl.querySelector('[ref="value"]'); - const checkboxEl = form.element.querySelector('.formio-component-page3Iagreetothefollowtherules'); - const checkboxValueEl = checkboxEl.querySelector('[ref="value"]'); - - assert.equal(customerSelectValueEl.textContent.trim(), 'Bob Thompson', 'Should render Select value properly'); - assert.equal(htmlSelectValueEl.textContent.trim(), 'Banana', 'Should render HTML5 Select value properly'); - assert.equal(checkboxValueEl.textContent.trim(), 'True', 'Should render Checkbox value properly'); - - Formio.makeRequest = originalMakeRequest; - done(); - }, 400); - }) - .catch(done); - }); - - it('HTML render mode for Wizard', function(done) { - const element = document.createElement('div'); - htmlRenderMode.display = 'wizard'; - - const originalMakeRequest = Formio.makeRequest; - Formio.makeRequest = function() { - return new Promise(resolve => { - setTimeout(() => { - const values = [ - { - _id: '5a53f8a044398b0001023eab', - modified: '2019-02-01T16:12:06.618Z', - data: { - firstName: 'Bob', - lastName: 'Thompson', - status: 'inactive', - email: 'bob@example.com', - submit: true, - }, - form: '5a53f887c8930000010f8b22', - _fvid: 0, - _vid: 0, - created: '2018-01-08T23:02:56.484Z', - externalIds: [], - access: [], - roles: [], - owner: '553dbfc08d22d5cb1a7024f2', - state: 'submitted', - project: '5692b91fd1028f01000407e3', - }, - { - _id: '5a53f8ad0dc919000194ab6b', - modified: '2019-02-01T16:12:01.781Z', - data: { - firstName: 'Sally', - lastName: 'Tanner', - status: 'active', - email: 'sally@example.com', - submit: true, - }, - form: '5a53f887c8930000010f8b22', - _fvid: 0, - _vid: 0, - created: '2018-01-08T23:03:09.730Z', - externalIds: [], - access: [], - roles: [], - owner: '553dbfc08d22d5cb1a7024f2', - state: 'submitted', - project: '5692b91fd1028f01000407e3', - }, - { - _id: '5a53f8b744398b0001023eaf', - modified: '2019-02-01T16:11:57.139Z', - data: { - firstName: 'Jane', - lastName: 'Doe', - status: 'active', - email: 'jane@example.com', - submit: true, - }, - form: '5a53f887c8930000010f8b22', - _fvid: 0, - _vid: 0, - created: '2018-01-08T23:03:19.473Z', - externalIds: [], - access: [], - roles: [], - owner: '553dbfc08d22d5cb1a7024f2', - state: 'submitted', - project: '5692b91fd1028f01000407e3', - }, - ]; - resolve(values); - }, 50); - }); - }; + setTimeout(() => { + const repairDataGrid0 = form.getComponent([ + 'inspectionDataGrid', + 0, + 'repairDataGrid', + ]); + assert.equal( + radioInspection0.dataValue, + 'reject', + 'Should set value', + ); + assert.equal( + repairDataGrid0.visible, + true, + 'Should become visible', + ); + + const radioRepair0 = form.getComponent([ + 'inspectionDataGrid', + 0, + 'repairDataGrid', + 0, + 'repairExam', + ]); + Harness.dispatchEvent( + 'click', + radioRepair0.element, + 'input[value="accept"]', + (i) => (i.checked = true), + ); - Formio.createForm(element, htmlRenderMode, { - readOnly: true, - renderMode: 'html', - }).then(form => { - form.submission = { - data: { - textfieldonPage3: 'test', - signature: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABTIAAACWCAYAAADt5XcLAAAgAElEQVR4Xu3dfWydV30H8NM1dLRdndIOKKJxGHSh0MalgBilpLAJyIsatQgJu4rT8peNiFOEhl0pyR8gxZHq/DGJJAhHCNbEEU5BHVWyJpWYuja0tOPdSctLQVBHWmAwkTi0lEFh+j3h3tqOG9vJvfa59/kcybKT3Ps85/mcE0f+5nfOOe/ZZ3/156QRIECAAAECBAgQIECAAAECBAgQIEAgY4HzBJkZj46uESBAgAABAgQIECBAgAABAgQIECBQCAgyTQQCBAgQIECAAAECBAgQIECAAAECBLIXEGRmP0Q6SIAAAQIECBAgQIAAAQIECBAgQICAINMcIECAAAECBAgQIECAAAECBAgQIEAgewFBZvZDpIMECBAgQIAAAQIECBAgQIAAAQIECAgyzQECBAgQIECAAAECBAgQIECAAAECBLIXEGRmP0Q6SIAAAQIECBAgQIAAAQIECBAgQICAINMcIECAAAECBAgQIECAAAECBAgQIEAgewFBZvZDpIMECBAgQIAAAQIECBAgQIAAAQIECAgyzQECBAgQIECAAAECBAgQIECAAAECBLIXEGRmP0Q6SIAAAQIECBAgQIAAAQIECBAgQICAINMcIECAAAECBAgQIECAAAECBAgQIEAgewFBZvZDpIMECBAgQIAAAQIECBAgQIAAAQIECAgyzQECBAgQIECAAAECBAgQIECAAAECBLIXEGRmP0Q6SIAAAQIECBAgQIAAAQIECBAgQICAINMcIECAAAECBAgQIECAAAECBAgQIEAgewFBZvZDpIMECBAgQIAAAQIECBAgQIAAAQIECAgyzQECBAgQIECAAAECBAgQIECAAAECBLIXEGRmP0Q6SIAAAQIECBAgQIAAAQIECBAgQICAINMcIECAAAECBAgQIECAAAECBAgQIEAgewFBZvZDpIMECBAgQIAAAQIECBAgQIAAAQIECAgyzQECBAgQIECAAAECBAgQIECAAAECBLIXEGRmP0Q6SIAAAQIECBAgQIAAAQIECBAgQICAINMcIECAAAECBAgQIECAAAECBAgQIEAgewFBZvZDpIO1Fjhx4kQaGTlSXHbhwoXFx6WXthSfNQIECBAgQIAAAQIECBAgQIAAgTwFBJl5jotezUIggslnnjmaTpwYS/F1fBw/furrZ54Z/cufnfr9eN2Z2uLFi1Jb29J06vO16d3vvrH4WiNAgAABAgQIECBAgAABAgQIEJhfAUHm/Pq7+ywFIoj8+tcfTbt3D6fR0VMhZT3bggUL0sUXX1yEmjfddGO6+eaVxdcaAQIECBAgQIAAAQIECBAgQIDA3AoIMufW293OUmDfvgfSF7+4Oz3++DeLysrZtNbWRdWqymXLbizeGsvIr7tuYiAZoWhUcEY7dOjRdPz4ifT00z9Nzz///ITbRYXmunUfTevWdc2mG15LgAABAgQIECBAgAABAgQIECBwDgKCzHPA89b6CkSwuGfPcBoa+lJReblkyVVpyZK/T6tXr0oRSFaWfFf2u4zeRMjZ2tpa0z0v494RbEaYun//gepDR2Xm4OA2FZr1nQauToAAAQIECBAgQIAAAQIECBAoBASZJkJWAhFERlgYS8cjPIzW0tKSenq6U2fnbfO+X2WEmhGsbtmytehbVHbu3XtPEaxqBAgQIECAAAECBAgQIECAAAEC9RMQZNbP1pVnIRAB4ZYtA2nfvgNFVWWEl7H0O8LLzs6OWVxpbl4aIWtX1/o0Onpqj84NG3rTxo19c3NzdyFAgAABAgQIECBAgAABAgQIlFBAkFnCQc/pkYeGhtOOHYOpsjw8Asy1azuKADP3Q3UicF2+/JZ0+PCTBenVVy9J9903PO9VozmNr74QIECAAAECBAgQIECAAAECBGolIMislaTrzFggAsAdO3ZW976MN8aBPFHRmGP15ZkeLJ6lt3dTsZdnpe3cua043TyWnWsECBAgQIAAAQIECBAgQIAAAQK1ERBk1sbRVWYgUAkwt28frJ48vmzZu4oAs9H3mPzCF3al9ev/uaoQBxLdffdm1ZkzmBdeQoAAAQIECBAgQIAAAQIECBCYiYAgcyZKXnPOAv39A0UVZoSZ0das6SgCzMrJ4+d8gwwuEPt8trevrS41j2fbsKHxqkwzoNQFAgQIECBAgAABAgQIECBAgMBpAoJMk6KuAvv2PZDuumtTipCvWQPMyYAR2kbV6djYWPFHsVx+YGCzpeZ1nWkuToAAAQIECBAgQIAAAQIECDS7gCCz2Ud4np4vgsvu7vUpTveOFntGDgz0N1UF5ploJ59qHtWZw8O7sj/AaJ6mi9sSIECAAAECBAgQIECAAAECBKYVEGROS+QFsxGIpeNbtmwtKhKjNcsemLMxqLx28kFAcfhPT0932rCh92wu5z0ECBAgQIAAAQIECBAgQIAAgVILCDJLPfy1ffioQowqzKjGXLr0mtTT89GGO4W8tiKnrhahbiw3ryw1dxBQPZRdkwABAgQIECBAgAABAgQIEGh2AUFms4/wHDzfyMiRIqjbv/9Acbc4rXvt2g57Qo6zD6Ourp4JBwEdOHB/aZbaz8E0dAsCBAgQIECAAAECBAgQIECgyQUEmU0+wPV8vPHLyK+88rXpLW9pK9U+mLO1Da+urvXVwDeWmg8OfiZFhaZGgAABAgQIECBAgAABAgQIECBwZgFBphlyVgKxXHrPnuEUlYatrYuKU7kFcjOjHL/U3L6ZMzPzKgIECBAgQIAAAQIECBAgQICAINMcmJVABJexD2Z8jrZmTUfaunWzZeSzUkzFae5RnTk6erR4Z2dnRxoc3DbLq3g5AQIECBAgQIAAAQIECBAgQKA8AoLM8oz1OT9pBG9RhRktqjB37tyWli278ZyvW9YLxKFI3d096dChxwqCsNy79x6hcFknhOcmQIAAAQIECBAgQIAAAQIEziggyDRBphXYt++B1N19Z4o9HqOtW9eVNm7sE7hNKzf9C8I0lppv2bK1ePHixYuSQ4Cmd/MKAgQIECBAgAABAgQIECBAoHwCgszyjfmMnzhCtr6+TWloSBXmjNHO8oURFnd03FENM+Pkd3uOniWmtxEgQIAAAQIECBAgQIAAAQJNKSDIbMphPfeHij0c29vvUIV57pQzvkIsNW9vX5sOH36yqHatVL7O+AJeSIAAAQIECBAgQIAAAQIECBBoYgFBZhMP7tk+Wix1vuuuTcXb7YV5topn976ogu3t3VTdizQOU4q9SDUCBAgQIECAAAECBAgQIECAQNkFBJllnwHjnj9CtNgLM5Y5R1u27F1pcHB7sW+jNrcClaXmL3/5y9OSJVelgwe/ak/SuR0CdyNAgAABAgQIECBAgAABAgQyExBkZjYg89WdCDFXrLg1jYwcKbrgQJ/5GokX7xtjsnz5LcVS82gRZjolfv7HRQ8IECBAgAABAgQIECBAgACB+REQZM6Pe1Z3jf0wu7vXp9ijsaWlJfX0dBenkmt5CPT1bUw7duwsOhPjsmFDbx4d0wsCBAgQIECAAAECBAgQIECAwBwKCDLnEDvHW8US5lhOHtV/EWLee+8uVX8ZDlScHN/buzGNjY0V4xPVmRoBAgQIECBAgAABAgQIECBAoEwCgswyjfakZx1/qM/SpdeknTu3p7a2a0sskvejR+VsV9f6NDp6tNi3dHh4l/HKe8j0jgABAgQIECBAgAABAgQIEKihgCCzhpiNdKn+/oG0ZcvWoss337yyOBl74cKFjfQIpezr+H0zY7wGBz+TVq9eVUoLD02AAAECBAgQIECAAAECBAiUS0CQWa7xLp62vf32tH//geLrONRnYKC/hAqN+8gRZsYYHjr0WPEQ9s1s3LHUcwIECBAgQIAAAQIECBAgQGDmAoLMmVs1xSvf+c73Vk/BvvvuzcXBPlpjCoyvqo2qzKjOVFXbmGOp1wQI1E4gDq6LLTiitbVd4/ti7WhdiQABAgQIECBAgMC8Cwgy530I5qYDIyNH0gc+sDqdPPnb4oZxWEwcGqM1tkAc1tTRcUfxEDGeg4Pbiv0zNQIECJRJIMLLT32qPz300MPpV7/69YRHj72f43ujPaDLNCM8KwECBAgQIECAQLMKCDKbdWTHPVflUJ/zzjsvXXLJJemHP/yOCpUmGvcIqWOpuUOAmmhQPQoBAjMSiADzs58dTLt3D6fYduOl2po1HcVe0BoBAgQIECBAgAABAo0tIMhs7PE7Y+/jh7ru7jtTVO1Fa2lpSceO/bSJn7i8jzZ538yoPurs7CgviCcnQKDpBQ4dejR1d69PEWZO1+wHPZ2QPydAgAABAgQIECDQGAKCzMYYp1n3Mn6wW7nyluoPeELMWRM25Bv6+jamHTt2Fn2PIHPDhj5LzRtyJHWaAIEzCZwpxHzFKy5Nq1atSKOjo+n48ROprW1pcSiabTfMKQIECBAgQIAAAQKNLyDIbPwxPO0JJv+A19q6KD3++EOWkzfhWE/1SFGB29W1Po2NjRV7wg0MbLYfaknG3mMSKINA/BvX3n7HaUvJly17VxFY2v+5DLPAMxIgQIAAAQIECJRVQJDZZCM//vCXeLQIMQ8evF8lSpON83SPExW5UZ25f/+BIsCOZZXxA75GgACBRhaIEPPDH769+I+a8e3uuzennp7uRn40fSdAgAABAgQIECBAYAYCgswZIDXKS4aGhov9wsa3p576jhCzUQawDv2szImWlkvS6163OA0P7zIf6uDskgQI1F9g8pYpccfYNiUO8Vm9elX9O+AOBAgQIECAAAECBAjMu4Agc96HoDYdmBxixg939967yxK72vA29FXih//29rXp8OEni+eIH/pvvnmlrQYaelR1nkD5BGLLjD17hqsPHv9Bc++9u/07V76p4IkJECBAgAABAgRKLCDIbILBj6V2K1bcOuFJhofvUaHSBGNby0fo7x9IEXiPjh4tfvCPk80dflFLYdciQKBeApP/nfOfdfWSdl0CBAgQIECAAAECeQsIMvMen2l7N9Vy8g0beu2HOK1cOV8QYUBv78ZqdWbsmxnzRSNAgECuAlMtKbcnZq6jpV8ECBAgQIAAAQIE6isgyKyvb12vHgf7dHffOeHk1jjUZWCgv673dfHGFjhx4kTavn0wbdmytXiQqMqM6kwn/Tb2uOo9gWYUGBk5kvr6NqX4T5hKi9PJ4xA7jQABAgQIECBAgACB8gkIMht0zOOHu46O21NUqlRa7HsY+x/GKdUagekEYg51dfVUqzM7OzvSwMBm82c6OH9OgMCcCER4edttH0m/+c3x6v1aWxcVIaZtMeZkCNyEAAECBAgQIECAQHYCgszshmT6Dk21zG7p0mvSgw/eL4Sans8rJgnE3pmV6swIwbdu3ZzWrOngVAOBqH49fnysuNKll7b4+1kD02a9RMyVkZEniwr7+Dh27H/SsWP/nS6//PLi18ePx++fmkvRIsiLv6/PP//79Oyzvy1eN1274IK/Tn/84x/SokWvLeZlXHeqFtdM6c/p1a9+9YTXxOvj35+FC1uKt1144YVpwYIF6frr24pfL116bWpru3a6bkz757HaYM+evSk+j2/x79zWrf2qx6cV9AICBAgQIECAAAECzSsgyGywsZ0qxIwKlccff0hI0mBjmVN3Y151d/ekQ4ceK7oVy8w3buwVGMxgkKKy9Yknvpl+/vPRIiT6/vePpPi9lwqJIuiJACqCqMWLW6tfv+xlL0sXXXRR9Y6VQKsShL7461OBVgRbU7W47qWXLqxeN+4VwZOtA2YwmHPwkpgbP/jBj9LTT/8knTx5spgv45dNz0EX6n6LG274h/Tcc8+lWAIebfwqgZjHv//9/6Wf/ezn6eqrlxR/Fh8jI4fTj370dPrxj38y5d+deO0XvzhYk6C07gBuQIAAAQIECBAgQIBA3QQEmXWjrf2Fp1pOHiHm3r27/HBXe+5SXjEqoKJC8/DhJ4vnX716VYpDNSzjnDgdIoyJKtbwGr+9Q86T5oILLigq6KIytLW1NV133bXF5/h1rSrpcn7++ehbfM+O/xyIkC6+jg9t9gIRiMa/c7ZNmb2ddxAgQIAAAQIECBBoNgFBZoOM6FQHHrS0tKTHH/9PIVODjGEjdTMOA4pAc2xsrAgPenq6UxwkJUhIaWhoOHV3r5/X4Yy/+1F1OT5gnjw24ytCo+rvD3/4Q/rd7353xn5HtWh83HTTjend777R95ZZjHJ4P/bYE+nb3/5uOnLkqdOWRc/kUrF0OsY12hvfuKT4+3fVVW8441tPLS1/Nl1++WXT3uLFpeVX/mWp+tRVvfG6kyfH0qte9arimvFslfl1qtq3pagKPnbsl0UV8p/+9KeiqjRC/dHRF/dtnrZD07wgAsx167qL/1DRCBAgQIAAAQIECBAgEAKCzAaYB/EDYn//1gnLDx140AAD1+BdjPCit3dT2rNnuHiSsp9uHh7d3XdOGVBdcsklacmSq9KHP/yhwinCnsoS8so0qASLk6vyKkvEo2LyoosunDBrKuFRfK7VHpuVZe8ROlUqBaMPlSrc8R2IZ4kQKZalR6gkyH5RJ/z27z9QfF8Ox9lU5t5wwzvSxRf/TXr/+/+pWhnbLFXPMb8efPA/0hNP/Fe67LLL0ujo6GnfCa+44op07NixdN11S4sq1bD79a//Ny1YcH56+9vflpYvf18x75rFpMH/KdB9AgQIECBAgAABAlkJCDKzGo7TOxOVcZ/+dH967rkXK6miamfnzu2Wk2c+ds3SvVPVwBur+2eWcbn5VBXRMb4R7m3c2NcU+09G0PrIIxHKndqzsbJf6vh5XKnWLGuwefTo0RTfkytOM/k7Ht+v29qWFlWulYrXmbzPawgQIECAAAECBAgQIEDgdAFBZqazIoKEvr5Np+2p5tTWTAesBN2KJdXbt3+uqNyLSqkNG/pSZ2fzn24ewd6qVR9Mv/nN8eoox9LuCDBjyX0zt/g+FKFd7AU6VcVmJZiLoK6t7ZqmCHSnGs+YA5U9Uacb77e97fqiMjf2IHXA0nRa/pwAAQIECBAgQIAAAQKzExBkzs6r7q+uHCISVT+T2ytecWl64IF/U4lZ91Fwg5cSiPkZczNCnZaWS9LrXrc4DQ/vatoloBFgrVhx64RTlMtaET25YjP23Yw9HMe317zmivTKV/5tsQQ9KhDjc4SdsRVGIy4Tni7AjOeKsHL16pXFCfTxrBoBAgQIECBAgAABAgQI1E9AkFk/21lfOYKCCE1e6mTbgwe/qsJn1qreUA+B2NOuvX1ttUpvYKA/dXa2N90eim9601snHF7y+tf/Xdqz5wsCq79MqvH7bEb15i9+8cv09NM/fckpF2FmnJQeLSoWK3tuxu/H16eCz2uKP5+v/TjjoJ6HH/56Ghr60pTfi2M/1Ntvvy11dt5mHtTjm4trEiBAgAABAgQIECBA4AwCgsyMpsfk0GR81+6+e3PTL2PNaCh0ZYYCsdw8TjePk4qjGm1gYHPThO2TTyePSsy9e3c3ZGXhDIezZi+rnGD9zDOjfznMZbQ40GVyBeeZblip5ozXRNAZFY+VryuhZ/z6/PP/Kr3wwgtxdl1xmnblYKTxYWjloKXjx8eK6tr4OP/889MLL/yp+LpyANKZ9r6M6suY307Qrtk0cSECBAgQIECAAAECBAjMWkCQOWuy+rzhNa95w0v+kD84uK0UexHWR9ZV6y0QAVV3d0/1cJjYO3LDht5637bu13/nO987YV/Ib3zjIRV4NVAff2p6BJ3RKr/3ve8dTidPnqzBXWp3iTjQKaovy7AfbO3UXIkAAQIECBAgQIAAAQL1ERBk1sd1VleNgzQ6Ou447T1xoMi99+5qmgq3WaF4ccMJxN6Zd921qeh3VGc28t6Zk/9OrlnTkXbu3NZwY9KIHY4KyaicHB09VcUZYWd8jl8fP35i1pWdszV485uvThdeeGFxYE9UXzbi3p6zfWavJ0CAAAECBAgQIECAQKMICDIzGKmpgswIMR988H4VYBmMjy7MXCAq69rbby+WmscS36jOXLeua+YXyOSV73nP8vStb32n2hv702YyMOO6EYFnBJzRToWfsWR8rPhc+b3JvR6/J+cFF1yQLrrowmKexvvHL2XP72n1iAABAgQIECBAgAABAgRCQJCZyTwYv7Q8Qsxjx176wIxMuqwbBKYUiFCoq2t92r//QPHncapz7C3YKCc6Rzj25je/tfpsN9zwjvS1r/270SZAgAABAgQIECBAgAABAgTmWUCQOc8DMP72UZkZzWESGQ2Krpy1wORK456e7vSxj3Vnv1R3/BL5ePgdO/4lfeQjnWft4I0ECBAgQIAAAQIECBAgQIBAbQQEmbVxdBUCBKYQmFydGfsNfuhDH0yf/OSdxVLeHNuKFbdUDy6Kk6p/8IMXl5jn2F99IkCAAAECBAgQIECAAAECZREQZJZlpD0ngXkUOHTo0dTbu3HCKeADA/2ps7M9u0Bz/DYPDvmZx0nj1gQIECBAgAABAgQIECBAYJKAINOUIEBgTgSiOnPv3vvSJz7RV71fVGiuW/fRbALNoaHh1N29vtq/wcFtqbOzY0583IQAAQIECBAgQIAAAQIECBA4s4Ag0wwhQGBOBSLQ7O8fSPv2HShON4+WS6C5cuWt6ZFHHq16PPXUd7Lf03NOB8/NCBAgQIAAAQIECBAgQIDAPAoIMucR360JlFkgTgffseNzaceOnVWG2DczDgWKJd0Rbs5lm3xa+bJl70oHD94/l11wLwIECBAgQIAAAQIECBAgQOAMAoJM04MAgXkVmCrQjA6tXr0qrV69sgg156L19W2cEKred9+X0vLl75uLW7sHAQIECBAgQIAAAQIECBAgMAMBQeYMkLyEAIH6C8SS8+3bB4uPsbGx6g2jSnPt2o4i0Gxru7YuHYnDiFasuLV6bdWYdWF2UQIECBAgQIAAAQIECBAgcE4Cgsxz4vNmAgTqIbBv3wNpx47BdOjQYxMuH0Hmxz++Lq1c+f6anXY+MnKkOOAnPlfa8PA9RUWoRoAAAQIECBAgQIAAAQIECOQjIMjMZyz0hACBSQKx7Hxo6EspThOvHAxUeUmcJh5h4803rzwnt5tu+kD69re/W71GXG/v3l3ndE1vJkCAAAECBAgQIECAAAECBGovIMisvakrEiBQB4FY/v2Vr3w1ff7z/3ra1ZctuzFdffWStGDBgnT99W1p6dJrZ7QMPU5P37Jl64QQc+fObTWr9qwDg0sSIECAAAECBAgQIECAAIHSCggySzv0HpxAYwrEXpq7dw8XS88nV2lOfqIIOG+66cYi1Iy9NqP99rfPph//+On0jW88kWIJe6VdeeVr05e/PDSjALQx5fSaAAECBAgQIECAAAECBAg0toAgs7HHT+8JlFog9rWMpef79h2YNtQ8E1Rr66J08OD9afHiRaX29PAECBAgQIAAAQIECBAgQCBnAUFmzqOjbwQIzFjg6NGj6eGHH00jI4eLg3smHxT0UheKE8oHB7cLMWcs7YUECBAgQIAAAQIECBAgQGB+BASZ8+PurgQIzIFA7Kv5yCOPpjg0aHR0tLjjFVdcUXx+3/v+sVhGHh8aAQIECBAgQIAAAQIECBAgkL+AIDP/MdJDAgQIECBAgAABAgQIECBAgAABAqUXEGSWfgoAIECAAAECBAgQIECAAAECBAgQIJC/gCAz/zHSQwIECBAgQIAAAQIECBAgQIAAAQKlFxBkln4KACBAgAABAgQIECBAgAABAgQIECCQv4AgM/8x0kMCBAgQIECAAAECBAgQIECAAAECpRcQZJZ+CgAgQIAAAQIECBAgQIAAAQIECBAgkL+AIDP/MdJDAgQIECBAgAABAgQIECBAgAABAqUXEGSWfgoAIECAAAECBAgQIECAAAECBAgQIJC/gCAz/zHSQwIECBAgQIAAAQIECBAgQIAAAQKlFxBkln4KACBAgAABAgQIECBAgAABAgQIECCQv4AgM/8x0kMCBAgQIECAAAECBAgQIECAAAECpRcQZJZ+CgAgQIAAAQIECBAgQIAAAQIECBAgkL+AIDP/MdJDAgQIECBAgAABAgQIECBAgAABAqUXEGSWfgoAIECAAAECBAgQIECAAAECBAgQIJC/gCAz/zHSQwIECBAgQIAAAQIECBAgQIAAAQKlFxBkln4KACBAgAABAgQIECBAgAABAgQIECCQv4AgM/8x0kMCBAgQIECAAAECBAgQIECAAAECpRcQZJZ+CgAgQIAAAQIECBAgQIAAAQIECBAgkL+AIDP/MdJDAgQIECBAgAABAgQIECBAgAABAqUXEGSWfgoAIECAAAECBAgQIECAAAECBAgQIJC/gCAz/zHSQwIECBAgQIAAAQIECBAgQIAAAQKlFxBkln4KACBAgAABAgQIECBAgAABAgQIECCQv4AgM/8x0kMCBAgQIECAAAECBAgQIECAAAECpRcQZJZ+CgAgQIAAAQIECBAgQIAAAQIECBAgkL+AIDP/MdJDAgQIECBAgAABAgQIECBAgAABAqUXEGSWfgoAIECAAAECBAgQIECAAAECBAgQIJC/gCAz/zHSQwIECBAgQIAAAQIECBAgQIAAAQKlFxBkln4KACBAgAABAgQIECBAgAABAgQIECCQv4AgM/8x0kMCBAgQIECAAAECBAgQIECAAAECpRcQZJZ+CgAgQIAAAQIECBAgQIAAAQIECBAgkL+AIDP/MdJDAgQIECBAgAABAgQIECBAgAABAqUXEGSWfgoAIECAAAECBAgQIECAAAECBAgQIJC/gCAz/zHSQwIECBAgQIAAAQIECBAgQIAAAQKlFxBkln4KACBAgAABAgQIECBAgAABAgQIECCQv4AgM/8x0kMCBAgQIECAAAECBAgQIECAAAECpRcQZJZ+CgAgQIAAAQIECBAgQIAAAQIECBAgkL+AIDP/MdJDAgQIECBAgAABAgQIEL7bi68AAAGrSURBVCBAgAABAqUXEGSWfgoAIECAAAECBAgQIECAAAECBAgQIJC/gCAz/zHSQwIECBAgQIAAAQIECBAgQIAAAQKlFxBkln4KACBAgAABAgQIECBAgAABAgQIECCQv4AgM/8x0kMCBAgQIECAAAECBAgQIECAAAECpRcQZJZ+CgAgQIAAAQIECBAgQIAAAQIECBAgkL+AIDP/MdJDAgQIECBAgAABAgQIECBAgAABAqUXEGSWfgoAIECAAAECBAgQIECAAAECBAgQIJC/gCAz/zHSQwIECBAgQIAAAQIECBAgQIAAAQKlFxBkln4KACBAgAABAgQIECBAgAABAgQIECCQv4AgM/8x0kMCBAgQIECAAAECBAgQIECAAAECpRcQZJZ+CgAgQIAAAQIECBAgQIAAAQIECBAgkL+AIDP/MdJDAgQIECBAgAABAgQIECBAgAABAqUXEGSWfgoAIECAAAECBAgQIECAAAECBAgQIJC/gCAz/zHSQwIECBAgQIAAAQIECBAgQIAAAQKlFxBkln4KACBAgAABAgQIECBAgAABAgQIECCQv8D/A4EQEWXi+YzZAAAAAElFTkSuQmCC', - panelDataGrid: [ - { - panelDataGridD: 'd', - panelDataGridC: 'c', - panelDataGridB: 'b', - panelDataGridA: 'a' - }, - { - panelDataGridD: 'h', - panelDataGridC: 'g', - panelDataGridB: 'f', - panelDataGridA: 'e' - }, - { - panelDataGridD: 'l', - panelDataGridC: 'k', - panelDataGridB: 'j', - panelDataGridA: 'i' - } - ], - textfield: 'testing', - page2Customer: 'bob@example.com', - textfieldonPage2: 'test', - numberField: 234, - textfieldonpage1: [ - 'a', - 'b', - 'c' - ], - panelHtml5Select: 'banana', - page3Iagreetothefollowtherules: true, - panelText: 'hello' - }, - }; - - setTimeout(() => { - form.setPage(1); - - setTimeout(() => { - const customerSelectEl = form.element.querySelector('.formio-component-page2Customer'); - const customerSelectValueEl = customerSelectEl.querySelector('[ref="value"]'); - - assert.equal(customerSelectValueEl.textContent.trim(), 'Bob Thompson', 'Should render Select value properly'); - - form.setPage(2); - - setTimeout(() => { - const htmlSelectEl = form.element.querySelector('.formio-component-panelHtml5Select'); - const htmlSelectValueEl = htmlSelectEl.querySelector('[ref="value"]'); - - assert.equal(htmlSelectValueEl.textContent.trim(), 'Banana', 'Should render HTML5 Select value properly'); + setTimeout(() => { + assert.equal( + radioRepair0.dataValue, + 'accept', + 'Should set value', + ); + const inspectionDataGrid = form.getComponent([ + 'inspectionDataGrid', + ]); + inspectionDataGrid.addRow(); + + setTimeout(() => { + assert.equal( + inspectionDataGrid.rows.length, + 2, + 'Should add a row', + ); + + const radioInspection1 = form.getComponent([ + 'inspectionDataGrid', + 1, + 'initialExam', + ]); + Harness.dispatchEvent( + 'click', + radioInspection1.element, + 'input[value="reject"]', + (i) => (i.checked = true), + ); + + setTimeout(() => { + const repairDataGrid1 = form.getComponent([ + 'inspectionDataGrid', + 1, + 'repairDataGrid', + ]); + assert.equal( + radioInspection1.dataValue, + 'reject', + 'Should set value', + ); + assert.equal( + repairDataGrid1.visible, + true, + 'Should become visible', + ); + + const radioRepair1 = form.getComponent([ + 'inspectionDataGrid', + 1, + 'repairDataGrid', + 0, + 'repairExam', + ]); + Harness.dispatchEvent( + 'click', + form.element, + form.element.querySelector( + `#${radioRepair1.root.id}-${radioRepair1.id}-${radioRepair1.row}-accept`, + ), + (i) => (i.checked = true), + ); + + setTimeout(() => { + assert.equal( + radioRepair1.dataValue, + 'accept', + 'Should set value of the clicked radio', + ); + assert.equal( + radioRepair0.dataValue, + 'accept', + 'Value of the radio inside another row should stay the same', + ); + + done(); + }, 300); + }, 350); + }, 300); + }, 250); + }, 350); + }) + .catch(done); + }); - Formio.makeRequest = originalMakeRequest; + it('Should render components properly', function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement, { language: 'en' }); + form.setForm(multipleTextareaInsideConditionalComponent) + .then(() => { + form.setSubmission({ + data: { + textArea2: ['test'], + didAnyBehavioralIssuesOccurOnYourShift: 'yes', + submit: false, + }, + }); + setTimeout(() => { + const textarea = form.getComponent(['textArea2']); + const panel = form.getComponent(['behavioralIssues']); + assert.equal(panel.visible, true, 'Should be visible'); + assert.deepEqual( + textarea.dataValue, + ['test'], + 'Should set the value from the submission', + ); + const inputRows = + textarea.element.querySelectorAll('[ref="input"]'); + assert.equal( + inputRows.length, + 1, + 'Should render all the rows of the Textarea', + ); + done(); + }, 750); + }) + .catch(done); + }); - done(); - }, 400); - }, 400); - }, 300); - }).catch(done); - }); - - it('Test optional sanitize', function(done) { - const element = document.createElement('div'); - - Formio.createForm(element, optionalSanitize, { - sanitize: false, - }).then(form => { - const sanitize = sinon.spy(FormioUtils, 'sanitize'); - form.redraw(); - setTimeout(() => { - assert.equal(sanitize.callCount, 0, 'Should not sanitize templates when sanitize in not turned on'); - element.innerHTML = ''; - Formio.createForm(element, optionalSanitize, { - sanitize: true, - }).then(form => { - sanitize.resetHistory(); - form.redraw(); - setTimeout(() => { - assert.equal(sanitize.callCount, 1, 'Should sanitize templates when sanitize in turned on'); - done(); - }, 250); - }, 250); - }); - }).catch(done); - }); + it('Should disable all the components inside Nested Form if it is disabled', function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement, { language: 'en' }); + form.setForm(disabledNestedForm) + .then(() => { + assert.equal( + form.components[0].disabled, + false, + 'Component that is outside of disabled Nested Form should be editable', + ); + const subFormComponents = form.components[1].subForm.components; + assert.deepEqual( + [ + subFormComponents[0].disabled, + subFormComponents[1].disabled, + ], + [true, true], + 'Components that are inside of disabled Nested Form should be disabled', + ); + done(); + }) + .catch(done); + }); - it('Should execute clearOnHide if visibility of the component inside an EditGrid has changed', function(done) { - const formElement = document.createElement('div'); - const form = new Webform(formElement, { language: 'en' }); - - form.setForm(testClearOnHideInsideEditGrid).then(() => { - form.submission = { - state: 'submitted', - data: { - subsidiaryEditGrid: [ - { - subsidiaryEntityContainer: { - entityFullName: 'test', - divisionNum: '', - entityType: 'otherEntity', - ifOtherEntityPleaseExplain: 'test', - }, - }, - ], - }, - }; - - setTimeout(() => { - const clearOnHideField = form.getComponent([ - 'subsidiaryEditGrid', - 0, - 'subsidiaryEntityContainer', - 'ifOtherEntityPleaseExplain', - ]); - const radioTrigger = form.getComponent(['subsidiaryEditGrid', 0, 'subsidiaryEntityContainer', 'entityType']); - assert.equal(form.rootPristine, true, 'Should not change this prop after setting a submission'); - assert.equal(clearOnHideField.visible, true, 'Should become visible'); - assert.equal(clearOnHideField.dataValue, 'test', 'Should set a value from the submission'); - - radioTrigger.setValue('subsidiary', { modified: true }); - setTimeout(() => { - assert.equal(clearOnHideField.visible, false, 'Should become invisible'); - - radioTrigger.setValue('otherEntity', { modified: true }); - setTimeout(() => { - assert.equal(clearOnHideField.visible, true, 'Should become visible'); - assert.equal(clearOnHideField.dataValue, '', 'Should clear a value due to the clearOnHide'); + it('Should restore value correctly if NestedForm is saved as reference', function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement, { language: 'en' }); + form.setForm(nestedFormInsideDataGrid) + .then(() => { + const nestedForm = form.getComponent(['dataGrid', 0, 'form1']); + const submissionWithIdOnly = { _id: '1232', data: {} }; + nestedForm.dataValue = { ...submissionWithIdOnly }; + nestedForm.restoreValue(); - done(); - }, 250); - }, 250); - }, 250); - }).catch(done); - }); + setTimeout(() => { + assert.deepEqual( + nestedForm.dataValue, + submissionWithIdOnly, + 'Should not set to defaultValue after restore', + ); + done(); + }, 350); + }) + .catch(done); + }); - it('Should show values in editGrid rows with nested dataGrid when viewing submission with initEmpty option', function(done) { - const formElement = document.createElement('div'); - const formWithNestedDataGridInitEmptyOption = new Webform(formElement); + it('Should not set the default value if there is only Radio with False value', function (done) { + const formElement = document.createElement('div'); + Formio.createForm(formElement, nestedFormInWizard) + .then((form) => { + const nestedForm = form.getComponent(['form']); + const submission = { + data: { + radio: false, + }, + }; - formWithNestedDataGridInitEmptyOption.setForm(formWithNestedDataGridInitEmpty.form).then(() => { - formWithNestedDataGridInitEmptyOption.setSubmission(formWithNestedDataGridInitEmpty.submission); + nestedForm.dataValue = { ...submission }; - setTimeout(() => { - const nestedDataGridFirstRowComponentValue = formWithNestedDataGridInitEmptyOption.element.querySelector( - '[ref="editgrid-editGrid-row"]').querySelectorAll('.col-sm-2'); + setTimeout(() => { + assert.deepEqual( + nestedForm.dataValue, + submission, + 'Should set submission', + ); + nestedForm.valueChanged = true; + form.setPage(1); - assert.equal(nestedDataGridFirstRowComponentValue[1].textContent.trim(), 'email'); - assert.equal(nestedDataGridFirstRowComponentValue[2].textContent.trim(), 'hhh@gmail.com'); + setTimeout(() => { + assert.deepEqual( + nestedForm.dataValue.data, + submission.data, + 'Should not set to defaultValue after restore', + ); + done(); + }, 300); + }, 300); + }) + .catch(done); + }); - done(); - }, 200); - }) - .catch((err) => done(err)); - }); + it('Should add and clear input error classes correctly', function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement, { language: 'en' }); - it('Should not refetch options for Select if there was an error', function(done) { - const formElement = document.createElement('div'); - const form= new Webform(formElement); - const formJson = { - components: [ - { - label: 'Select', - widget: 'html5', - tableView: true, - dataSrc: 'url', - data: { - url: 'http://example.com', - headers: [ - { - key: '', - value: '', - }, + form.setForm(UpdateErrorClassesWidgets) + .then(() => { + const checkbox = form.getComponent('showDate'); + checkbox.setValue(true); + setTimeout(() => { + const dateTimeComponent = + form.getComponent('condtionalDate'); + const dateComponentElement = dateTimeComponent.element; + assert.equal( + !dateComponentElement.className.includes( + 'formio-hidden', + ), + true, + 'Should not be hidden', + ); + + form.submit(); + + setTimeout(() => { + const dateVisibleInput = + dateComponentElement.querySelector('.input'); + const flatpickerInput = + dateComponentElement.querySelector( + '.flatpickr-input', + ); + + assert( + dateVisibleInput.className.includes('is-invalid'), + 'Visible field should have invalid class', + ); + assert( + flatpickerInput.className.includes('is-invalid'), + 'Flatpickr field should have invalid class as well', + ); + + dateTimeComponent.setValue('2020-12-09T00:00:00'); + + setTimeout(() => { + assert.equal( + dateTimeComponent.dataValue, + '2020-12-09T00:00:00', + 'Should set value', + ); + assert( + !dateVisibleInput.className.includes( + 'is-invalid', + ), + 'Invalid class should be removed', + ); + assert( + !flatpickerInput.className.includes( + 'is-invalid', + ), + 'Invalid class should be removed from flatpickr field as well', + ); + + checkbox.setValue(false); + + setTimeout(() => { + const dateComponentElement = + dateTimeComponent.element; + assert.equal( + dateComponentElement.className.includes( + 'formio-hidden', + ), + true, + 'Should be hidden', + ); + checkbox.setValue(true); + + setTimeout(() => { + const dateComponentElement = + dateTimeComponent.element; + assert.equal( + !dateComponentElement.className.includes( + 'formio-hidden', + ), + true, + 'Should be visible', + ); + const dateVisibleInput = + dateComponentElement.querySelector( + '.input:not([type="hidden"])', + ); + const flatpickerInput = + dateComponentElement.querySelector( + '.flatpickr-input', + ); + + assert( + dateVisibleInput.className.includes( + 'is-invalid', + ), + 'Visible field should has invalid class', + ); + assert( + flatpickerInput.className.includes( + 'is-invalid', + ), + 'Flatpickr field should has invalid class as well', + ); + + dateTimeComponent.setValue( + '2020-10-19T00:00:00', + ); + setTimeout(() => { + assert.equal( + dateTimeComponent.dataValue, + '2020-10-19T00:00:00', + 'Should set value', + ); + assert( + !dateVisibleInput.className.includes( + 'is-invalid', + ), + 'Invalid class should be removed', + ); + assert( + !flatpickerInput.className.includes( + 'is-invalid', + ), + 'Invalid class should be removed from flatpickr field as well', + ); + done(); + }, 300); + }, 400); + }, 300); + }, 300); + }, 300); + }, 350); + }) + .catch(done); + }).timeout(3000); + + it('Should have number and currency fields in empty form submission', function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement); + const formJson = { + components: [ + { + label: 'Number', + key: 'number', + type: 'number', + }, + { + label: 'Currency', + key: 'currency', + type: 'currency', + }, + { + type: 'button', + label: 'Submit', + key: 'submit', + }, ], - }, - key: 'select', - hidden: true, - type: 'select', - input: true, - disableLimit: false, - }, - ], - }; + }; - let counter = 0; - const originalMakeRequest = Formio.makeRequest; - Formio.makeRequest = function() { - return new Promise((_, reject) => { - setTimeout(() => { - counter++; - const err = new Error('Failed to fetch'); - err.networkError = true; - reject(err); - }, 50); - }); - }; + const emptySubmissionData = { + submit: true, + }; - form.setForm(formJson).then(() => { - const select = form.getComponent('select'); + form.setForm(formJson) + .then(() => { + const clickEvent = new Event('click'); + const submitBtn = form.element.querySelector( + '[name="data[submit]"]', + ); - select.visible = true; + submitBtn.dispatchEvent(clickEvent); - setTimeout(() => { - setTimeout(() => { - select.visible = false; + setTimeout(() => { + assert.deepEqual(form.data, emptySubmissionData); + done(); + }, 400); + }) + .catch((err) => done(err)); + }); - setTimeout(() => { - select.visible = true; + it('Test Truncate Multiple Spaces', function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement); + + form.setForm(truncateMultipleSpaces) + .then(() => { + const textFieldRequired = form.getComponent(['textField1']); + const textFieldMinMaxLength = form.getComponent(['textField']); + const textAreaMinMaxLength = form.getComponent(['textArea']); + Harness.dispatchEvent( + 'input', + textFieldRequired.element, + 'input', + (i) => (i.value = ' '), + ); + Harness.dispatchEvent( + 'input', + textFieldMinMaxLength.element, + 'input', + (i) => (i.value = ' 546 456 '), + ); + Harness.dispatchEvent( + 'input', + textAreaMinMaxLength.element, + 'textarea', + (i) => (i.value = ' 546 456 '), + ); - setTimeout(() => { - expect(select.networkError).to.be.true; - expect(select.loadingError).to.be.true; - expect(counter).to.equal(1); - Formio.makeRequest = originalMakeRequest; - done(); - }, 200); - }, 200); - }, 200); - }, 200); - }) - .catch((err) => done(err)); - }); - - it('Should show only one custom error when submitting empty required field with multiple validation', function(done) { - const formJson = { - components: [ - { - label: 'This line', - tableView: false, - storage: 'base64', - webcam: false, - fileTypes: [{ label: '', value: '' }], - multiple: true, - validate: { required: true, customMessage: 'will be showed once' }, - key: 'file', - type: 'file', - input: true, - }, - { - label: 'Submit', - showValidations: false, - tableView: false, - key: 'submit', - type: 'button', - input: true, - saveOnEnter: false, - } - ] - }; - const element = document.createElement('div'); - const form = new Webform(element); - - form.setForm(formJson).then(() => { - Harness.clickElement(form, form.element.querySelector('[name="data[submit]"]')); - - setTimeout(() => { - assert.equal(form.errors[0].messages.length, 1); - assert.equal(form.errors[0].messages[0].message, 'will be showed once'); - assert.equal(form.element.querySelector('[ref="errorRef"]').textContent.trim().includes('will be showed once'), true); - done(); - }, 200); - }) - .catch((err) => done(err)); - }); - - it('Should show validation error when submitting number with just "-" sign and required validation', function(done) { - const formJson = { - components: [ - { - label: 'Number', - mask: false, - tableView: false, - delimiter: false, - requireDecimal: false, - inputFormat: 'plain', - truncateMultipleSpaces: false, - validate: { - required: true - }, - key: 'number', - type: 'number', - input: true - }, - { - label: 'Submit', - showValidations: false, - tableView: false, - key: 'submit', - type: 'button', - input: true, - saveOnEnter: false, - } - ] - }; - const element = document.createElement('div'); - const form = new Webform(element); - - form.setForm(formJson).then(() => { - Harness.setInputValue(form, 'data[number]', '-_'); - Harness.clickElement(form, form.element.querySelector('[name="data[submit]"]')); - - setTimeout(() => { - assert.equal(form.errors[0].messages.length, 1); - assert.equal(form.errors[0].messages[0].message, 'Number is required'); - assert.equal(form.element.querySelector('[ref="errorRef"]').textContent.trim().includes('Number is required'), true); - done(); - }, 200); - }) - .catch((err) => done(err)); - }); - - describe('Test sanitizeConfig', function() { - it('Should sanitize components using default sanitizeConfig', function(done) { - const formElement = document.createElement('div'); - const form = new Webform(formElement); - const testForm = fastCloneDeep(formWithNotAllowedTags); - - form.setForm(testForm).then(() => { - const textFieldWithScript = form.getComponent('textFieldWithScript'); - const textAreaWithIframe = form.getComponent('textAreaWithIframe'); - - assert.equal(textFieldWithScript.element?.getElementsByTagName('script').length, 0, 'Should not render srcipt tag'); - assert.equal(textAreaWithIframe.element?.getElementsByTagName('iframe').length, 0, 'Should not render iframe tag'); - - done(); - }).catch((err) => done(err)); + setTimeout(() => { + assert.equal( + textFieldRequired.dataValue, + ' ', + 'Should set value', + ); + assert.equal( + textFieldMinMaxLength.dataValue, + ' 546 456 ', + 'Should set value', + ); + assert.equal( + textAreaMinMaxLength.dataValue, + ' 546 456 ', + 'Should set value', + ); + + assert.equal( + textFieldRequired.errors.length, + 1, + 'Should be invalid since it does not have a value', + ); + assert.equal( + textFieldMinMaxLength.errors.length, + 0, + 'Should be valid since it value does not exceed the max length after truncating spaces', + ); + assert.equal( + textAreaMinMaxLength.errors.length, + 0, + 'Should be valid since it value does not exceed the max length after truncating spaces', + ); + + form.submit(false, {}).finally(() => { + assert.equal( + textFieldRequired.dataValue, + '', + 'Should truncate the value before submit', + ); + assert.equal( + textFieldMinMaxLength.dataValue, + '546 456', + 'Should truncate the value before submit', + ); + assert.equal( + textAreaMinMaxLength.dataValue, + '546 456', + 'Should truncate the value before submit', + ); + + done(); + }); + }, 400); + }) + .catch(done); }); - it('Should sanitize components using sanitizeConfig from form settings', function(done) { - const formElement = document.createElement('div'); - const form = new Webform(formElement); - const testForm = fastCloneDeep(formWithNotAllowedTags); - testForm.settings.sanitizeConfig = { - addTags: ['iframe', 'script'], - }, + it('HTML render mode for Webform', function (done) { + const element = document.createElement('div'); - form.setForm(testForm).then(() => { - const textFieldWithScript = form.getComponent('textFieldWithScript'); - const textAreaWithIframe = form.getComponent('textAreaWithIframe'); + const originalMakeRequest = Formio.makeRequest; + Formio.makeRequest = function () { + return new Promise((resolve) => { + setTimeout(() => { + const values = [ + { + _id: '5a53f8a044398b0001023eab', + modified: '2019-02-01T16:12:06.618Z', + data: { + firstName: 'Bob', + lastName: 'Thompson', + status: 'inactive', + email: 'bob@example.com', + submit: true, + }, + form: '5a53f887c8930000010f8b22', + _fvid: 0, + _vid: 0, + created: '2018-01-08T23:02:56.484Z', + externalIds: [], + access: [], + roles: [], + owner: '553dbfc08d22d5cb1a7024f2', + state: 'submitted', + project: '5692b91fd1028f01000407e3', + }, + { + _id: '5a53f8ad0dc919000194ab6b', + modified: '2019-02-01T16:12:01.781Z', + data: { + firstName: 'Sally', + lastName: 'Tanner', + status: 'active', + email: 'sally@example.com', + submit: true, + }, + form: '5a53f887c8930000010f8b22', + _fvid: 0, + _vid: 0, + created: '2018-01-08T23:03:09.730Z', + externalIds: [], + access: [], + roles: [], + owner: '553dbfc08d22d5cb1a7024f2', + state: 'submitted', + project: '5692b91fd1028f01000407e3', + }, + { + _id: '5a53f8b744398b0001023eaf', + modified: '2019-02-01T16:11:57.139Z', + data: { + firstName: 'Jane', + lastName: 'Doe', + status: 'active', + email: 'jane@example.com', + submit: true, + }, + form: '5a53f887c8930000010f8b22', + _fvid: 0, + _vid: 0, + created: '2018-01-08T23:03:19.473Z', + externalIds: [], + access: [], + roles: [], + owner: '553dbfc08d22d5cb1a7024f2', + state: 'submitted', + project: '5692b91fd1028f01000407e3', + }, + ]; + resolve(values); + }, 50); + }); + }; - assert.equal(textFieldWithScript.element?.getElementsByTagName('script').length, 1, 'Should render srcipt tag'); - assert.equal(textAreaWithIframe.element?.getElementsByTagName('iframe').length, 1, 'Should render iframe tag'); + Formio.createForm(element, htmlRenderMode, { + readOnly: true, + renderMode: 'html', + }) + .then((form) => { + form.submission = { + data: { + textfieldonPage3: 'test', + signature: + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABTIAAACWCAYAAADt5XcLAAAgAElEQVR4Xu3dfWydV30H8NM1dLRdndIOKKJxGHSh0MalgBilpLAJyIsatQgJu4rT8peNiFOEhl0pyR8gxZHq/DGJJAhHCNbEEU5BHVWyJpWYuja0tOPdSctLQVBHWmAwkTi0lEFh+j3h3tqOG9vJvfa59/kcybKT3Ps85/mcE0f+5nfOOe/ZZ3/156QRIECAAAECBAgQIECAAAECBAgQIEAgY4HzBJkZj46uESBAgAABAgQIECBAgAABAgQIECBQCAgyTQQCBAgQIECAAAECBAgQIECAAAECBLIXEGRmP0Q6SIAAAQIECBAgQIAAAQIECBAgQICAINMcIECAAAECBAgQIECAAAECBAgQIEAgewFBZvZDpIMECBAgQIAAAQIECBAgQIAAAQIECAgyzQECBAgQIECAAAECBAgQIECAAAECBLIXEGRmP0Q6SIAAAQIECBAgQIAAAQIECBAgQICAINMcIECAAAECBAgQIECAAAECBAgQIEAgewFBZvZDpIMECBAgQIAAAQIECBAgQIAAAQIECAgyzQECBAgQIECAAAECBAgQIECAAAECBLIXEGRmP0Q6SIAAAQIECBAgQIAAAQIECBAgQICAINMcIECAAAECBAgQIECAAAECBAgQIEAgewFBZvZDpIMECBAgQIAAAQIECBAgQIAAAQIECAgyzQECBAgQIECAAAECBAgQIECAAAECBLIXEGRmP0Q6SIAAAQIECBAgQIAAAQIECBAgQICAINMcIECAAAECBAgQIECAAAECBAgQIEAgewFBZvZDpIMECBAgQIAAAQIECBAgQIAAAQIECAgyzQECBAgQIECAAAECBAgQIECAAAECBLIXEGRmP0Q6SIAAAQIECBAgQIAAAQIECBAgQICAINMcIECAAAECBAgQIECAAAECBAgQIEAgewFBZvZDpIMECBAgQIAAAQIECBAgQIAAAQIECAgyzQECBAgQIECAAAECBAgQIECAAAECBLIXEGRmP0Q6SIAAAQIECBAgQIAAAQIECBAgQICAINMcIECAAAECBAgQIECAAAECBAgQIEAgewFBZvZDpIO1Fjhx4kQaGTlSXHbhwoXFx6WXthSfNQIECBAgQIAAAQIECBAgQIAAgTwFBJl5jotezUIggslnnjmaTpwYS/F1fBw/furrZ54Z/cufnfr9eN2Z2uLFi1Jb29J06vO16d3vvrH4WiNAgAABAgQIECBAgAABAgQIEJhfAUHm/Pq7+ywFIoj8+tcfTbt3D6fR0VMhZT3bggUL0sUXX1yEmjfddGO6+eaVxdcaAQIECBAgQIAAAQIECBAgQIDA3AoIMufW293OUmDfvgfSF7+4Oz3++DeLysrZtNbWRdWqymXLbizeGsvIr7tuYiAZoWhUcEY7dOjRdPz4ifT00z9Nzz///ITbRYXmunUfTevWdc2mG15LgAABAgQIECBAgAABAgQIECBwDgKCzHPA89b6CkSwuGfPcBoa+lJReblkyVVpyZK/T6tXr0oRSFaWfFf2u4zeRMjZ2tpa0z0v494RbEaYun//gepDR2Xm4OA2FZr1nQauToAAAQIECBAgQIAAAQIECBAoBASZJkJWAhFERlgYS8cjPIzW0tKSenq6U2fnbfO+X2WEmhGsbtmytehbVHbu3XtPEaxqBAgQIECAAAECBAgQIECAAAEC9RMQZNbP1pVnIRAB4ZYtA2nfvgNFVWWEl7H0O8LLzs6OWVxpbl4aIWtX1/o0Onpqj84NG3rTxo19c3NzdyFAgAABAgQIECBAgAABAgQIlFBAkFnCQc/pkYeGhtOOHYOpsjw8Asy1azuKADP3Q3UicF2+/JZ0+PCTBenVVy9J9903PO9VozmNr74QIECAAAECBAgQIECAAAECBGolIMislaTrzFggAsAdO3ZW976MN8aBPFHRmGP15ZkeLJ6lt3dTsZdnpe3cua043TyWnWsECBAgQIAAAQIECBAgQIAAAQK1ERBk1sbRVWYgUAkwt28frJ48vmzZu4oAs9H3mPzCF3al9ev/uaoQBxLdffdm1ZkzmBdeQoAAAQIECBAgQIAAAQIECBCYiYAgcyZKXnPOAv39A0UVZoSZ0das6SgCzMrJ4+d8gwwuEPt8trevrS41j2fbsKHxqkwzoNQFAgQIECBAgAABAgQIECBAgMBpAoJMk6KuAvv2PZDuumtTipCvWQPMyYAR2kbV6djYWPFHsVx+YGCzpeZ1nWkuToAAAQIECBAgQIAAAQIECDS7gCCz2Ud4np4vgsvu7vUpTveOFntGDgz0N1UF5ploJ59qHtWZw8O7sj/AaJ6mi9sSIECAAAECBAgQIECAAAECBKYVEGROS+QFsxGIpeNbtmwtKhKjNcsemLMxqLx28kFAcfhPT0932rCh92wu5z0ECBAgQIAAAQIECBAgQIAAgVILCDJLPfy1ffioQowqzKjGXLr0mtTT89GGO4W8tiKnrhahbiw3ryw1dxBQPZRdkwABAgQIECBAgAABAgQIEGh2AUFms4/wHDzfyMiRIqjbv/9Acbc4rXvt2g57Qo6zD6Ourp4JBwEdOHB/aZbaz8E0dAsCBAgQIECAAAECBAgQIECgyQUEmU0+wPV8vPHLyK+88rXpLW9pK9U+mLO1Da+urvXVwDeWmg8OfiZFhaZGgAABAgQIECBAgAABAgQIECBwZgFBphlyVgKxXHrPnuEUlYatrYuKU7kFcjOjHL/U3L6ZMzPzKgIECBAgQIAAAQIECBAgQICAINMcmJVABJexD2Z8jrZmTUfaunWzZeSzUkzFae5RnTk6erR4Z2dnRxoc3DbLq3g5AQIECBAgQIAAAQIECBAgQKA8AoLM8oz1OT9pBG9RhRktqjB37tyWli278ZyvW9YLxKFI3d096dChxwqCsNy79x6hcFknhOcmQIAAAQIECBAgQIAAAQIEziggyDRBphXYt++B1N19Z4o9HqOtW9eVNm7sE7hNKzf9C8I0lppv2bK1ePHixYuSQ4Cmd/MKAgQIECBAgAABAgQIECBAoHwCgszyjfmMnzhCtr6+TWloSBXmjNHO8oURFnd03FENM+Pkd3uOniWmtxEgQIAAAQIECBAgQIAAAQJNKSDIbMphPfeHij0c29vvUIV57pQzvkIsNW9vX5sOH36yqHatVL7O+AJeSIAAAQIECBAgQIAAAQIECBBoYgFBZhMP7tk+Wix1vuuuTcXb7YV5topn976ogu3t3VTdizQOU4q9SDUCBAgQIECAAAECBAgQIECAQNkFBJllnwHjnj9CtNgLM5Y5R1u27F1pcHB7sW+jNrcClaXmL3/5y9OSJVelgwe/ak/SuR0CdyNAgAABAgQIECBAgAABAgQyExBkZjYg89WdCDFXrLg1jYwcKbrgQJ/5GokX7xtjsnz5LcVS82gRZjolfv7HRQ8IECBAgAABAgQIECBAgACB+REQZM6Pe1Z3jf0wu7vXp9ijsaWlJfX0dBenkmt5CPT1bUw7duwsOhPjsmFDbx4d0wsCBAgQIECAAAECBAgQIECAwBwKCDLnEDvHW8US5lhOHtV/EWLee+8uVX8ZDlScHN/buzGNjY0V4xPVmRoBAgQIECBAgAABAgQIECBAoEwCgswyjfakZx1/qM/SpdeknTu3p7a2a0sskvejR+VsV9f6NDp6tNi3dHh4l/HKe8j0jgABAgQIECBAgAABAgQIEKihgCCzhpiNdKn+/oG0ZcvWoss337yyOBl74cKFjfQIpezr+H0zY7wGBz+TVq9eVUoLD02AAAECBAgQIECAAAECBAiUS0CQWa7xLp62vf32tH//geLrONRnYKC/hAqN+8gRZsYYHjr0WPEQ9s1s3LHUcwIECBAgQIAAAQIECBAgQGDmAoLMmVs1xSvf+c73Vk/BvvvuzcXBPlpjCoyvqo2qzKjOVFXbmGOp1wQI1E4gDq6LLTiitbVd4/ti7WhdiQABAgQIECBAgMC8Cwgy530I5qYDIyNH0gc+sDqdPPnb4oZxWEwcGqM1tkAc1tTRcUfxEDGeg4Pbiv0zNQIECJRJIMLLT32qPz300MPpV7/69YRHj72f43ujPaDLNCM8KwECBAgQIECAQLMKCDKbdWTHPVflUJ/zzjsvXXLJJemHP/yOCpUmGvcIqWOpuUOAmmhQPQoBAjMSiADzs58dTLt3D6fYduOl2po1HcVe0BoBAgQIECBAgAABAo0tIMhs7PE7Y+/jh7ru7jtTVO1Fa2lpSceO/bSJn7i8jzZ538yoPurs7CgviCcnQKDpBQ4dejR1d69PEWZO1+wHPZ2QPydAgAABAgQIECDQGAKCzMYYp1n3Mn6wW7nyluoPeELMWRM25Bv6+jamHTt2Fn2PIHPDhj5LzRtyJHWaAIEzCZwpxHzFKy5Nq1atSKOjo+n48ROprW1pcSiabTfMKQIECBAgQIAAAQKNLyDIbPwxPO0JJv+A19q6KD3++EOWkzfhWE/1SFGB29W1Po2NjRV7wg0MbLYfaknG3mMSKINA/BvX3n7HaUvJly17VxFY2v+5DLPAMxIgQIAAAQIECJRVQJDZZCM//vCXeLQIMQ8evF8lSpON83SPExW5UZ25f/+BIsCOZZXxA75GgACBRhaIEPPDH769+I+a8e3uuzennp7uRn40fSdAgAABAgQIECBAYAYCgswZIDXKS4aGhov9wsa3p576jhCzUQawDv2szImWlkvS6163OA0P7zIf6uDskgQI1F9g8pYpccfYNiUO8Vm9elX9O+AOBAgQIECAAAECBAjMu4Agc96HoDYdmBxixg939967yxK72vA29FXih//29rXp8OEni+eIH/pvvnmlrQYaelR1nkD5BGLLjD17hqsPHv9Bc++9u/07V76p4IkJECBAgAABAgRKLCDIbILBj6V2K1bcOuFJhofvUaHSBGNby0fo7x9IEXiPjh4tfvCPk80dflFLYdciQKBeApP/nfOfdfWSdl0CBAgQIECAAAECeQsIMvMen2l7N9Vy8g0beu2HOK1cOV8QYUBv78ZqdWbsmxnzRSNAgECuAlMtKbcnZq6jpV8ECBAgQIAAAQIE6isgyKyvb12vHgf7dHffOeHk1jjUZWCgv673dfHGFjhx4kTavn0wbdmytXiQqMqM6kwn/Tb2uOo9gWYUGBk5kvr6NqX4T5hKi9PJ4xA7jQABAgQIECBAgACB8gkIMht0zOOHu46O21NUqlRa7HsY+x/GKdUagekEYg51dfVUqzM7OzvSwMBm82c6OH9OgMCcCER4edttH0m/+c3x6v1aWxcVIaZtMeZkCNyEAAECBAgQIECAQHYCgszshmT6Dk21zG7p0mvSgw/eL4Sans8rJgnE3pmV6swIwbdu3ZzWrOngVAOBqH49fnysuNKll7b4+1kD02a9RMyVkZEniwr7+Dh27H/SsWP/nS6//PLi18ePx++fmkvRIsiLv6/PP//79Oyzvy1eN1274IK/Tn/84x/SokWvLeZlXHeqFtdM6c/p1a9+9YTXxOvj35+FC1uKt1144YVpwYIF6frr24pfL116bWpru3a6bkz757HaYM+evSk+j2/x79zWrf2qx6cV9AICBAgQIECAAAECzSsgyGywsZ0qxIwKlccff0hI0mBjmVN3Y151d/ekQ4ceK7oVy8w3buwVGMxgkKKy9Yknvpl+/vPRIiT6/vePpPi9lwqJIuiJACqCqMWLW6tfv+xlL0sXXXRR9Y6VQKsShL7461OBVgRbU7W47qWXLqxeN+4VwZOtA2YwmHPwkpgbP/jBj9LTT/8knTx5spgv45dNz0EX6n6LG274h/Tcc8+lWAIebfwqgZjHv//9/6Wf/ezn6eqrlxR/Fh8jI4fTj370dPrxj38y5d+deO0XvzhYk6C07gBuQIAAAQIECBAgQIBA3QQEmXWjrf2Fp1pOHiHm3r27/HBXe+5SXjEqoKJC8/DhJ4vnX716VYpDNSzjnDgdIoyJKtbwGr+9Q86T5oILLigq6KIytLW1NV133bXF5/h1rSrpcn7++ehbfM+O/xyIkC6+jg9t9gIRiMa/c7ZNmb2ddxAgQIAAAQIECBBoNgFBZoOM6FQHHrS0tKTHH/9PIVODjGEjdTMOA4pAc2xsrAgPenq6UxwkJUhIaWhoOHV3r5/X4Yy/+1F1OT5gnjw24ytCo+rvD3/4Q/rd7353xn5HtWh83HTTjend777R95ZZjHJ4P/bYE+nb3/5uOnLkqdOWRc/kUrF0OsY12hvfuKT4+3fVVW8441tPLS1/Nl1++WXT3uLFpeVX/mWp+tRVvfG6kyfH0qte9arimvFslfl1qtq3pagKPnbsl0UV8p/+9KeiqjRC/dHRF/dtnrZD07wgAsx167qL/1DRCBAgQIAAAQIECBAgEAKCzAaYB/EDYn//1gnLDx140AAD1+BdjPCit3dT2rNnuHiSsp9uHh7d3XdOGVBdcsklacmSq9KHP/yhwinCnsoS8so0qASLk6vyKkvEo2LyoosunDBrKuFRfK7VHpuVZe8ROlUqBaMPlSrc8R2IZ4kQKZalR6gkyH5RJ/z27z9QfF8Ox9lU5t5wwzvSxRf/TXr/+/+pWhnbLFXPMb8efPA/0hNP/Fe67LLL0ujo6GnfCa+44op07NixdN11S4sq1bD79a//Ny1YcH56+9vflpYvf18x75rFpMH/KdB9AgQIECBAgAABAlkJCDKzGo7TOxOVcZ/+dH967rkXK6miamfnzu2Wk2c+ds3SvVPVwBur+2eWcbn5VBXRMb4R7m3c2NcU+09G0PrIIxHKndqzsbJf6vh5XKnWLGuwefTo0RTfkytOM/k7Ht+v29qWFlWulYrXmbzPawgQIECAAAECBAgQIEDgdAFBZqazIoKEvr5Np+2p5tTWTAesBN2KJdXbt3+uqNyLSqkNG/pSZ2fzn24ewd6qVR9Mv/nN8eoox9LuCDBjyX0zt/g+FKFd7AU6VcVmJZiLoK6t7ZqmCHSnGs+YA5U9Uacb77e97fqiMjf2IHXA0nRa/pwAAQIECBAgQIAAAQKzExBkzs6r7q+uHCISVT+T2ytecWl64IF/U4lZ91Fwg5cSiPkZczNCnZaWS9LrXrc4DQ/vatoloBFgrVhx64RTlMtaET25YjP23Yw9HMe317zmivTKV/5tsQQ9KhDjc4SdsRVGIy4Tni7AjOeKsHL16pXFCfTxrBoBAgQIECBAgAABAgQI1E9AkFk/21lfOYKCCE1e6mTbgwe/qsJn1qreUA+B2NOuvX1ttUpvYKA/dXa2N90eim9601snHF7y+tf/Xdqz5wsCq79MqvH7bEb15i9+8cv09NM/fckpF2FmnJQeLSoWK3tuxu/H16eCz2uKP5+v/TjjoJ6HH/56Ghr60pTfi2M/1Ntvvy11dt5mHtTjm4trEiBAgAABAgQIECBA4AwCgsyMpsfk0GR81+6+e3PTL2PNaCh0ZYYCsdw8TjePk4qjGm1gYHPThO2TTyePSsy9e3c3ZGXhDIezZi+rnGD9zDOjfznMZbQ40GVyBeeZblip5ozXRNAZFY+VryuhZ/z6/PP/Kr3wwgtxdl1xmnblYKTxYWjloKXjx8eK6tr4OP/889MLL/yp+LpyANKZ9r6M6suY307Qrtk0cSECBAgQIECAAAECBAjMWkCQOWuy+rzhNa95w0v+kD84uK0UexHWR9ZV6y0QAVV3d0/1cJjYO3LDht5637bu13/nO987YV/Ib3zjIRV4NVAff2p6BJ3RKr/3ve8dTidPnqzBXWp3iTjQKaovy7AfbO3UXIkAAQIECBAgQIAAAQL1ERBk1sd1VleNgzQ6Ou447T1xoMi99+5qmgq3WaF4ccMJxN6Zd921qeh3VGc28t6Zk/9OrlnTkXbu3NZwY9KIHY4KyaicHB09VcUZYWd8jl8fP35i1pWdszV485uvThdeeGFxYE9UXzbi3p6zfWavJ0CAAAECBAgQIECAQKMICDIzGKmpgswIMR988H4VYBmMjy7MXCAq69rbby+WmscS36jOXLeua+YXyOSV73nP8vStb32n2hv702YyMOO6EYFnBJzRToWfsWR8rPhc+b3JvR6/J+cFF1yQLrrowmKexvvHL2XP72n1iAABAgQIECBAgAABAgRCQJCZyTwYv7Q8Qsxjx176wIxMuqwbBKYUiFCoq2t92r//QPHncapz7C3YKCc6Rzj25je/tfpsN9zwjvS1r/270SZAgAABAgQIECBAgAABAgTmWUCQOc8DMP72UZkZzWESGQ2Krpy1wORK456e7vSxj3Vnv1R3/BL5ePgdO/4lfeQjnWft4I0ECBAgQIAAAQIECBAgQIBAbQQEmbVxdBUCBKYQmFydGfsNfuhDH0yf/OSdxVLeHNuKFbdUDy6Kk6p/8IMXl5jn2F99IkCAAAECBAgQIECAAAECZREQZJZlpD0ngXkUOHTo0dTbu3HCKeADA/2ps7M9u0Bz/DYPDvmZx0nj1gQIECBAgAABAgQIECBAYJKAINOUIEBgTgSiOnPv3vvSJz7RV71fVGiuW/fRbALNoaHh1N29vtq/wcFtqbOzY0583IQAAQIECBAgQIAAAQIECBA4s4Ag0wwhQGBOBSLQ7O8fSPv2HShON4+WS6C5cuWt6ZFHHq16PPXUd7Lf03NOB8/NCBAgQIAAAQIECBAgQIDAPAoIMucR360JlFkgTgffseNzaceOnVWG2DczDgWKJd0Rbs5lm3xa+bJl70oHD94/l11wLwIECBAgQIAAAQIECBAgQOAMAoJM04MAgXkVmCrQjA6tXr0qrV69sgg156L19W2cEKred9+X0vLl75uLW7sHAQIECBAgQIAAAQIECBAgMAMBQeYMkLyEAIH6C8SS8+3bB4uPsbGx6g2jSnPt2o4i0Gxru7YuHYnDiFasuLV6bdWYdWF2UQIECBAgQIAAAQIECBAgcE4Cgsxz4vNmAgTqIbBv3wNpx47BdOjQYxMuH0Hmxz++Lq1c+f6anXY+MnKkOOAnPlfa8PA9RUWoRoAAAQIECBAgQIAAAQIECOQjIMjMZyz0hACBSQKx7Hxo6EspThOvHAxUeUmcJh5h4803rzwnt5tu+kD69re/W71GXG/v3l3ndE1vJkCAAAECBAgQIECAAAECBGovIMisvakrEiBQB4FY/v2Vr3w1ff7z/3ra1ZctuzFdffWStGDBgnT99W1p6dJrZ7QMPU5P37Jl64QQc+fObTWr9qwDg0sSIECAAAECBAgQIECAAIHSCggySzv0HpxAYwrEXpq7dw8XS88nV2lOfqIIOG+66cYi1Iy9NqP99rfPph//+On0jW88kWIJe6VdeeVr05e/PDSjALQx5fSaAAECBAgQIECAAAECBAg0toAgs7HHT+8JlFog9rWMpef79h2YNtQ8E1Rr66J08OD9afHiRaX29PAECBAgQIAAAQIECBAgQCBnAUFmzqOjbwQIzFjg6NGj6eGHH00jI4eLg3smHxT0UheKE8oHB7cLMWcs7YUECBAgQIAAAQIECBAgQGB+BASZ8+PurgQIzIFA7Kv5yCOPpjg0aHR0tLjjFVdcUXx+3/v+sVhGHh8aAQIECBAgQIAAAQIECBAgkL+AIDP/MdJDAgQIECBAgAABAgQIECBAgAABAqUXEGSWfgoAIECAAAECBAgQIECAAAECBAgQIJC/gCAz/zHSQwIECBAgQIAAAQIECBAgQIAAAQKlFxBkln4KACBAgAABAgQIECBAgAABAgQIECCQv4AgM/8x0kMCBAgQIECAAAECBAgQIECAAAECpRcQZJZ+CgAgQIAAAQIECBAgQIAAAQIECBAgkL+AIDP/MdJDAgQIECBAgAABAgQIECBAgAABAqUXEGSWfgoAIECAAAECBAgQIECAAAECBAgQIJC/gCAz/zHSQwIECBAgQIAAAQIECBAgQIAAAQKlFxBkln4KACBAgAABAgQIECBAgAABAgQIECCQv4AgM/8x0kMCBAgQIECAAAECBAgQIECAAAECpRcQZJZ+CgAgQIAAAQIECBAgQIAAAQIECBAgkL+AIDP/MdJDAgQIECBAgAABAgQIECBAgAABAqUXEGSWfgoAIECAAAECBAgQIECAAAECBAgQIJC/gCAz/zHSQwIECBAgQIAAAQIECBAgQIAAAQKlFxBkln4KACBAgAABAgQIECBAgAABAgQIECCQv4AgM/8x0kMCBAgQIECAAAECBAgQIECAAAECpRcQZJZ+CgAgQIAAAQIECBAgQIAAAQIECBAgkL+AIDP/MdJDAgQIECBAgAABAgQIECBAgAABAqUXEGSWfgoAIECAAAECBAgQIECAAAECBAgQIJC/gCAz/zHSQwIECBAgQIAAAQIECBAgQIAAAQKlFxBkln4KACBAgAABAgQIECBAgAABAgQIECCQv4AgM/8x0kMCBAgQIECAAAECBAgQIECAAAECpRcQZJZ+CgAgQIAAAQIECBAgQIAAAQIECBAgkL+AIDP/MdJDAgQIECBAgAABAgQIECBAgAABAqUXEGSWfgoAIECAAAECBAgQIECAAAECBAgQIJC/gCAz/zHSQwIECBAgQIAAAQIECBAgQIAAAQKlFxBkln4KACBAgAABAgQIECBAgAABAgQIECCQv4AgM/8x0kMCBAgQIECAAAECBAgQIECAAAECpRcQZJZ+CgAgQIAAAQIECBAgQIAAAQIECBAgkL+AIDP/MdJDAgQIECBAgAABAgQIECBAgAABAqUXEGSWfgoAIECAAAECBAgQIECAAAECBAgQIJC/gCAz/zHSQwIECBAgQIAAAQIECBAgQIAAAQKlFxBkln4KACBAgAABAgQIECBAgAABAgQIECCQv4AgM/8x0kMCBAgQIECAAAECBAgQIECAAAECpRcQZJZ+CgAgQIAAAQIECBAgQIAAAQIECBAgkL+AIDP/MdJDAgQIECBAgAABAgQIEL7bi68AAAGrSURBVCBAgAABAqUXEGSWfgoAIECAAAECBAgQIECAAAECBAgQIJC/gCAz/zHSQwIECBAgQIAAAQIECBAgQIAAAQKlFxBkln4KACBAgAABAgQIECBAgAABAgQIECCQv4AgM/8x0kMCBAgQIECAAAECBAgQIECAAAECpRcQZJZ+CgAgQIAAAQIECBAgQIAAAQIECBAgkL+AIDP/MdJDAgQIECBAgAABAgQIECBAgAABAqUXEGSWfgoAIECAAAECBAgQIECAAAECBAgQIJC/gCAz/zHSQwIECBAgQIAAAQIECBAgQIAAAQKlFxBkln4KACBAgAABAgQIECBAgAABAgQIECCQv4AgM/8x0kMCBAgQIECAAAECBAgQIECAAAECpRcQZJZ+CgAgQIAAAQIECBAgQIAAAQIECBAgkL+AIDP/MdJDAgQIECBAgAABAgQIECBAgAABAqUXEGSWfgoAIECAAAECBAgQIECAAAECBAgQIJC/gCAz/zHSQwIECBAgQIAAAQIECBAgQIAAAQKlFxBkln4KACBAgAABAgQIECBAgAABAgQIECCQv8D/A4EQEWXi+YzZAAAAAElFTkSuQmCC', + panelDataGrid: [ + { + panelDataGridD: 'd', + panelDataGridC: 'c', + panelDataGridB: 'b', + panelDataGridA: 'a', + }, + { + panelDataGridD: 'h', + panelDataGridC: 'g', + panelDataGridB: 'f', + panelDataGridA: 'e', + }, + { + panelDataGridD: 'l', + panelDataGridC: 'k', + panelDataGridB: 'j', + panelDataGridA: 'i', + }, + ], + textfield: 'testing', + page2Customer: 'bob@example.com', + textfieldonPage2: 'test', + numberField: 234, + textfieldonpage1: ['a', 'b', 'c'], + panelHtml5Select: 'banana', + page3Iagreetothefollowtherules: true, + panelText: 'hello', + }, + }; - done(); - }).catch((err) => done(err)); + setTimeout(() => { + const customerSelectEl = form.element.querySelector( + '.formio-component-page2Customer', + ); + const customerSelectValueEl = + customerSelectEl.querySelector('[ref="value"]'); + const htmlSelectEl = form.element.querySelector( + '.formio-component-panelHtml5Select', + ); + const htmlSelectValueEl = + htmlSelectEl.querySelector('[ref="value"]'); + const checkboxEl = form.element.querySelector( + '.formio-component-page3Iagreetothefollowtherules', + ); + const checkboxValueEl = + checkboxEl.querySelector('[ref="value"]'); + + assert.equal( + customerSelectValueEl.textContent.trim(), + 'Bob Thompson', + 'Should render Select value properly', + ); + assert.equal( + htmlSelectValueEl.textContent.trim(), + 'Banana', + 'Should render HTML5 Select value properly', + ); + assert.equal( + checkboxValueEl.textContent.trim(), + 'True', + 'Should render Checkbox value properly', + ); + + Formio.makeRequest = originalMakeRequest; + done(); + }, 400); + }) + .catch(done); }); - it('Should sanitize components using sanitizeConfig from global settings', function(done) { - const formElement = document.createElement('div'); - const form = new Webform(formElement); - const testForm = fastCloneDeep(formWithNotAllowedTags); - testForm.globalSettings.sanitizeConfig = { - addTags: ['iframe', 'script'], - }, + it('HTML render mode for Wizard', function (done) { + const element = document.createElement('div'); + htmlRenderMode.display = 'wizard'; + + const originalMakeRequest = Formio.makeRequest; + Formio.makeRequest = function () { + return new Promise((resolve) => { + setTimeout(() => { + const values = [ + { + _id: '5a53f8a044398b0001023eab', + modified: '2019-02-01T16:12:06.618Z', + data: { + firstName: 'Bob', + lastName: 'Thompson', + status: 'inactive', + email: 'bob@example.com', + submit: true, + }, + form: '5a53f887c8930000010f8b22', + _fvid: 0, + _vid: 0, + created: '2018-01-08T23:02:56.484Z', + externalIds: [], + access: [], + roles: [], + owner: '553dbfc08d22d5cb1a7024f2', + state: 'submitted', + project: '5692b91fd1028f01000407e3', + }, + { + _id: '5a53f8ad0dc919000194ab6b', + modified: '2019-02-01T16:12:01.781Z', + data: { + firstName: 'Sally', + lastName: 'Tanner', + status: 'active', + email: 'sally@example.com', + submit: true, + }, + form: '5a53f887c8930000010f8b22', + _fvid: 0, + _vid: 0, + created: '2018-01-08T23:03:09.730Z', + externalIds: [], + access: [], + roles: [], + owner: '553dbfc08d22d5cb1a7024f2', + state: 'submitted', + project: '5692b91fd1028f01000407e3', + }, + { + _id: '5a53f8b744398b0001023eaf', + modified: '2019-02-01T16:11:57.139Z', + data: { + firstName: 'Jane', + lastName: 'Doe', + status: 'active', + email: 'jane@example.com', + submit: true, + }, + form: '5a53f887c8930000010f8b22', + _fvid: 0, + _vid: 0, + created: '2018-01-08T23:03:19.473Z', + externalIds: [], + access: [], + roles: [], + owner: '553dbfc08d22d5cb1a7024f2', + state: 'submitted', + project: '5692b91fd1028f01000407e3', + }, + ]; + resolve(values); + }, 50); + }); + }; - form.setForm(testForm).then(() => { - const textFieldWithScript = form.getComponent('textFieldWithScript'); - const textAreaWithIframe = form.getComponent('textAreaWithIframe'); + Formio.createForm(element, htmlRenderMode, { + readOnly: true, + renderMode: 'html', + }) + .then((form) => { + form.submission = { + data: { + textfieldonPage3: 'test', + signature: + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABTIAAACWCAYAAADt5XcLAAAgAElEQVR4Xu3dfWydV30H8NM1dLRdndIOKKJxGHSh0MalgBilpLAJyIsatQgJu4rT8peNiFOEhl0pyR8gxZHq/DGJJAhHCNbEEU5BHVWyJpWYuja0tOPdSctLQVBHWmAwkTi0lEFh+j3h3tqOG9vJvfa59/kcybKT3Ps85/mcE0f+5nfOOe/ZZ3/156QRIECAAAECBAgQIECAAAECBAgQIEAgY4HzBJkZj46uESBAgAABAgQIECBAgAABAgQIECBQCAgyTQQCBAgQIECAAAECBAgQIECAAAECBLIXEGRmP0Q6SIAAAQIECBAgQIAAAQIECBAgQICAINMcIECAAAECBAgQIECAAAECBAgQIEAgewFBZvZDpIMECBAgQIAAAQIECBAgQIAAAQIECAgyzQECBAgQIECAAAECBAgQIECAAAECBLIXEGRmP0Q6SIAAAQIECBAgQIAAAQIECBAgQICAINMcIECAAAECBAgQIECAAAECBAgQIEAgewFBZvZDpIMECBAgQIAAAQIECBAgQIAAAQIECAgyzQECBAgQIECAAAECBAgQIECAAAECBLIXEGRmP0Q6SIAAAQIECBAgQIAAAQIECBAgQICAINMcIECAAAECBAgQIECAAAECBAgQIEAgewFBZvZDpIMECBAgQIAAAQIECBAgQIAAAQIECAgyzQECBAgQIECAAAECBAgQIECAAAECBLIXEGRmP0Q6SIAAAQIECBAgQIAAAQIECBAgQICAINMcIECAAAECBAgQIECAAAECBAgQIEAgewFBZvZDpIMECBAgQIAAAQIECBAgQIAAAQIECAgyzQECBAgQIECAAAECBAgQIECAAAECBLIXEGRmP0Q6SIAAAQIECBAgQIAAAQIECBAgQICAINMcIECAAAECBAgQIECAAAECBAgQIEAgewFBZvZDpIMECBAgQIAAAQIECBAgQIAAAQIECAgyzQECBAgQIECAAAECBAgQIECAAAECBLIXEGRmP0Q6SIAAAQIECBAgQIAAAQIECBAgQICAINMcIECAAAECBAgQIECAAAECBAgQIEAgewFBZvZDpIO1Fjhx4kQaGTlSXHbhwoXFx6WXthSfNQIECBAgQIAAAQIECBAgQIAAgTwFBJl5jotezUIggslnnjmaTpwYS/F1fBw/furrZ54Z/cufnfr9eN2Z2uLFi1Jb29J06vO16d3vvrH4WiNAgAABAgQIECBAgAABAgQIEJhfAUHm/Pq7+ywFIoj8+tcfTbt3D6fR0VMhZT3bggUL0sUXX1yEmjfddGO6+eaVxdcaAQIECBAgQIAAAQIECBAgQIDA3AoIMufW293OUmDfvgfSF7+4Oz3++DeLysrZtNbWRdWqymXLbizeGsvIr7tuYiAZoWhUcEY7dOjRdPz4ifT00z9Nzz///ITbRYXmunUfTevWdc2mG15LgAABAgQIECBAgAABAgQIECBwDgKCzHPA89b6CkSwuGfPcBoa+lJReblkyVVpyZK/T6tXr0oRSFaWfFf2u4zeRMjZ2tpa0z0v494RbEaYun//gepDR2Xm4OA2FZr1nQauToAAAQIECBAgQIAAAQIECBAoBASZJkJWAhFERlgYS8cjPIzW0tKSenq6U2fnbfO+X2WEmhGsbtmytehbVHbu3XtPEaxqBAgQIECAAAECBAgQIECAAAEC9RMQZNbP1pVnIRAB4ZYtA2nfvgNFVWWEl7H0O8LLzs6OWVxpbl4aIWtX1/o0Onpqj84NG3rTxo19c3NzdyFAgAABAgQIECBAgAABAgQIlFBAkFnCQc/pkYeGhtOOHYOpsjw8Asy1azuKADP3Q3UicF2+/JZ0+PCTBenVVy9J9903PO9VozmNr74QIECAAAECBAgQIECAAAECBGolIMislaTrzFggAsAdO3ZW976MN8aBPFHRmGP15ZkeLJ6lt3dTsZdnpe3cua043TyWnWsECBAgQIAAAQIECBAgQIAAAQK1ERBk1sbRVWYgUAkwt28frJ48vmzZu4oAs9H3mPzCF3al9ev/uaoQBxLdffdm1ZkzmBdeQoAAAQIECBAgQIAAAQIECBCYiYAgcyZKXnPOAv39A0UVZoSZ0das6SgCzMrJ4+d8gwwuEPt8trevrS41j2fbsKHxqkwzoNQFAgQIECBAgAABAgQIECBAgMBpAoJMk6KuAvv2PZDuumtTipCvWQPMyYAR2kbV6djYWPFHsVx+YGCzpeZ1nWkuToAAAQIECBAgQIAAAQIECDS7gCCz2Ud4np4vgsvu7vUpTveOFntGDgz0N1UF5ploJ59qHtWZw8O7sj/AaJ6mi9sSIECAAAECBAgQIECAAAECBKYVEGROS+QFsxGIpeNbtmwtKhKjNcsemLMxqLx28kFAcfhPT0932rCh92wu5z0ECBAgQIAAAQIECBAgQIAAgVILCDJLPfy1ffioQowqzKjGXLr0mtTT89GGO4W8tiKnrhahbiw3ryw1dxBQPZRdkwABAgQIECBAgAABAgQIEGh2AUFms4/wHDzfyMiRIqjbv/9Acbc4rXvt2g57Qo6zD6Ourp4JBwEdOHB/aZbaz8E0dAsCBAgQIECAAAECBAgQIECgyQUEmU0+wPV8vPHLyK+88rXpLW9pK9U+mLO1Da+urvXVwDeWmg8OfiZFhaZGgAABAgQIECBAgAABAgQIECBwZgFBphlyVgKxXHrPnuEUlYatrYuKU7kFcjOjHL/U3L6ZMzPzKgIECBAgQIAAAQIECBAgQICAINMcmJVABJexD2Z8jrZmTUfaunWzZeSzUkzFae5RnTk6erR4Z2dnRxoc3DbLq3g5AQIECBAgQIAAAQIECBAgQKA8AoLM8oz1OT9pBG9RhRktqjB37tyWli278ZyvW9YLxKFI3d096dChxwqCsNy79x6hcFknhOcmQIAAAQIECBAgQIAAAQIEziggyDRBphXYt++B1N19Z4o9HqOtW9eVNm7sE7hNKzf9C8I0lppv2bK1ePHixYuSQ4Cmd/MKAgQIECBAgAABAgQIECBAoHwCgszyjfmMnzhCtr6+TWloSBXmjNHO8oURFnd03FENM+Pkd3uOniWmtxEgQIAAAQIECBAgQIAAAQJNKSDIbMphPfeHij0c29vvUIV57pQzvkIsNW9vX5sOH36yqHatVL7O+AJeSIAAAQIECBAgQIAAAQIECBBoYgFBZhMP7tk+Wix1vuuuTcXb7YV5topn976ogu3t3VTdizQOU4q9SDUCBAgQIECAAAECBAgQIECAQNkFBJllnwHjnj9CtNgLM5Y5R1u27F1pcHB7sW+jNrcClaXmL3/5y9OSJVelgwe/ak/SuR0CdyNAgAABAgQIECBAgAABAgQyExBkZjYg89WdCDFXrLg1jYwcKbrgQJ/5GokX7xtjsnz5LcVS82gRZjolfv7HRQ8IECBAgAABAgQIECBAgACB+REQZM6Pe1Z3jf0wu7vXp9ijsaWlJfX0dBenkmt5CPT1bUw7duwsOhPjsmFDbx4d0wsCBAgQIECAAAECBAgQIECAwBwKCDLnEDvHW8US5lhOHtV/EWLee+8uVX8ZDlScHN/buzGNjY0V4xPVmRoBAgQIECBAgAABAgQIECBAoEwCgswyjfakZx1/qM/SpdeknTu3p7a2a0sskvejR+VsV9f6NDp6tNi3dHh4l/HKe8j0jgABAgQIECBAgAABAgQIEKihgCCzhpiNdKn+/oG0ZcvWoss337yyOBl74cKFjfQIpezr+H0zY7wGBz+TVq9eVUoLD02AAAECBAgQIECAAAECBAiUS0CQWa7xLp62vf32tH//geLrONRnYKC/hAqN+8gRZsYYHjr0WPEQ9s1s3LHUcwIECBAgQIAAAQIECBAgQGDmAoLMmVs1xSvf+c73Vk/BvvvuzcXBPlpjCoyvqo2qzKjOVFXbmGOp1wQI1E4gDq6LLTiitbVd4/ti7WhdiQABAgQIECBAgMC8Cwgy530I5qYDIyNH0gc+sDqdPPnb4oZxWEwcGqM1tkAc1tTRcUfxEDGeg4Pbiv0zNQIECJRJIMLLT32qPz300MPpV7/69YRHj72f43ujPaDLNCM8KwECBAgQIECAQLMKCDKbdWTHPVflUJ/zzjsvXXLJJemHP/yOCpUmGvcIqWOpuUOAmmhQPQoBAjMSiADzs58dTLt3D6fYduOl2po1HcVe0BoBAgQIECBAgAABAo0tIMhs7PE7Y+/jh7ru7jtTVO1Fa2lpSceO/bSJn7i8jzZ538yoPurs7CgviCcnQKDpBQ4dejR1d69PEWZO1+wHPZ2QPydAgAABAgQIECDQGAKCzMYYp1n3Mn6wW7nyluoPeELMWRM25Bv6+jamHTt2Fn2PIHPDhj5LzRtyJHWaAIEzCZwpxHzFKy5Nq1atSKOjo+n48ROprW1pcSiabTfMKQIECBAgQIAAAQKNLyDIbPwxPO0JJv+A19q6KD3++EOWkzfhWE/1SFGB29W1Po2NjRV7wg0MbLYfaknG3mMSKINA/BvX3n7HaUvJly17VxFY2v+5DLPAMxIgQIAAAQIECJRVQJDZZCM//vCXeLQIMQ8evF8lSpON83SPExW5UZ25f/+BIsCOZZXxA75GgACBRhaIEPPDH769+I+a8e3uuzennp7uRn40fSdAgAABAgQIECBAYAYCgswZIDXKS4aGhov9wsa3p576jhCzUQawDv2szImWlkvS6163OA0P7zIf6uDskgQI1F9g8pYpccfYNiUO8Vm9elX9O+AOBAgQIECAAAECBAjMu4Agc96HoDYdmBxixg939967yxK72vA29FXih//29rXp8OEni+eIH/pvvnmlrQYaelR1nkD5BGLLjD17hqsPHv9Bc++9u/07V76p4IkJECBAgAABAgRKLCDIbILBj6V2K1bcOuFJhofvUaHSBGNby0fo7x9IEXiPjh4tfvCPk80dflFLYdciQKBeApP/nfOfdfWSdl0CBAgQIECAAAECeQsIMvMen2l7N9Vy8g0beu2HOK1cOV8QYUBv78ZqdWbsmxnzRSNAgECuAlMtKbcnZq6jpV8ECBAgQIAAAQIE6isgyKyvb12vHgf7dHffOeHk1jjUZWCgv673dfHGFjhx4kTavn0wbdmytXiQqMqM6kwn/Tb2uOo9gWYUGBk5kvr6NqX4T5hKi9PJ4xA7jQABAgQIECBAgACB8gkIMht0zOOHu46O21NUqlRa7HsY+x/GKdUagekEYg51dfVUqzM7OzvSwMBm82c6OH9OgMCcCER4edttH0m/+c3x6v1aWxcVIaZtMeZkCNyEAAECBAgQIECAQHYCgszshmT6Dk21zG7p0mvSgw/eL4Sans8rJgnE3pmV6swIwbdu3ZzWrOngVAOBqH49fnysuNKll7b4+1kD02a9RMyVkZEniwr7+Dh27H/SsWP/nS6//PLi18ePx++fmkvRIsiLv6/PP//79Oyzvy1eN1274IK/Tn/84x/SokWvLeZlXHeqFtdM6c/p1a9+9YTXxOvj35+FC1uKt1144YVpwYIF6frr24pfL116bWpru3a6bkz757HaYM+evSk+j2/x79zWrf2qx6cV9AICBAgQIECAAAECzSsgyGywsZ0qxIwKlccff0hI0mBjmVN3Y151d/ekQ4ceK7oVy8w3buwVGMxgkKKy9Yknvpl+/vPRIiT6/vePpPi9lwqJIuiJACqCqMWLW6tfv+xlL0sXXXRR9Y6VQKsShL7461OBVgRbU7W47qWXLqxeN+4VwZOtA2YwmHPwkpgbP/jBj9LTT/8knTx5spgv45dNz0EX6n6LG274h/Tcc8+lWAIebfwqgZjHv//9/6Wf/ezn6eqrlxR/Fh8jI4fTj370dPrxj38y5d+deO0XvzhYk6C07gBuQIAAAQIECBAgQIBA3QQEmXWjrf2Fp1pOHiHm3r27/HBXe+5SXjEqoKJC8/DhJ4vnX716VYpDNSzjnDgdIoyJKtbwGr+9Q86T5oILLigq6KIytLW1NV133bXF5/h1rSrpcn7++ehbfM+O/xyIkC6+jg9t9gIRiMa/c7ZNmb2ddxAgQIAAAQIECBBoNgFBZoOM6FQHHrS0tKTHH/9PIVODjGEjdTMOA4pAc2xsrAgPenq6UxwkJUhIaWhoOHV3r5/X4Yy/+1F1OT5gnjw24ytCo+rvD3/4Q/rd7353xn5HtWh83HTTjend777R95ZZjHJ4P/bYE+nb3/5uOnLkqdOWRc/kUrF0OsY12hvfuKT4+3fVVW8441tPLS1/Nl1++WXT3uLFpeVX/mWp+tRVvfG6kyfH0qte9arimvFslfl1qtq3pagKPnbsl0UV8p/+9KeiqjRC/dHRF/dtnrZD07wgAsx167qL/1DRCBAgQIAAAQIECBAgEAKCzAaYB/EDYn//1gnLDx140AAD1+BdjPCit3dT2rNnuHiSsp9uHh7d3XdOGVBdcsklacmSq9KHP/yhwinCnsoS8so0qASLk6vyKkvEo2LyoosunDBrKuFRfK7VHpuVZe8ROlUqBaMPlSrc8R2IZ4kQKZalR6gkyH5RJ/z27z9QfF8Ox9lU5t5wwzvSxRf/TXr/+/+pWhnbLFXPMb8efPA/0hNP/Fe67LLL0ujo6GnfCa+44op07NixdN11S4sq1bD79a//Ny1YcH56+9vflpYvf18x75rFpMH/KdB9AgQIECBAgAABAlkJCDKzGo7TOxOVcZ/+dH967rkXK6miamfnzu2Wk2c+ds3SvVPVwBur+2eWcbn5VBXRMb4R7m3c2NcU+09G0PrIIxHKndqzsbJf6vh5XKnWLGuwefTo0RTfkytOM/k7Ht+v29qWFlWulYrXmbzPawgQIECAAAECBAgQIEDgdAFBZqazIoKEvr5Np+2p5tTWTAesBN2KJdXbt3+uqNyLSqkNG/pSZ2fzn24ewd6qVR9Mv/nN8eoox9LuCDBjyX0zt/g+FKFd7AU6VcVmJZiLoK6t7ZqmCHSnGs+YA5U9Uacb77e97fqiMjf2IHXA0nRa/pwAAQIECBAgQIAAAQKzExBkzs6r7q+uHCISVT+T2ytecWl64IF/U4lZ91Fwg5cSiPkZczNCnZaWS9LrXrc4DQ/vatoloBFgrVhx64RTlMtaET25YjP23Yw9HMe317zmivTKV/5tsQQ9KhDjc4SdsRVGIy4Tni7AjOeKsHL16pXFCfTxrBoBAgQIECBAgAABAgQI1E9AkFk/21lfOYKCCE1e6mTbgwe/qsJn1qreUA+B2NOuvX1ttUpvYKA/dXa2N90eim9601snHF7y+tf/Xdqz5wsCq79MqvH7bEb15i9+8cv09NM/fckpF2FmnJQeLSoWK3tuxu/H16eCz2uKP5+v/TjjoJ6HH/56Ghr60pTfi2M/1Ntvvy11dt5mHtTjm4trEiBAgAABAgQIECBA4AwCgsyMpsfk0GR81+6+e3PTL2PNaCh0ZYYCsdw8TjePk4qjGm1gYHPThO2TTyePSsy9e3c3ZGXhDIezZi+rnGD9zDOjfznMZbQ40GVyBeeZblip5ozXRNAZFY+VryuhZ/z6/PP/Kr3wwgtxdl1xmnblYKTxYWjloKXjx8eK6tr4OP/889MLL/yp+LpyANKZ9r6M6suY307Qrtk0cSECBAgQIECAAAECBAjMWkCQOWuy+rzhNa95w0v+kD84uK0UexHWR9ZV6y0QAVV3d0/1cJjYO3LDht5637bu13/nO987YV/Ib3zjIRV4NVAff2p6BJ3RKr/3ve8dTidPnqzBXWp3iTjQKaovy7AfbO3UXIkAAQIECBAgQIAAAQL1ERBk1sd1VleNgzQ6Ou447T1xoMi99+5qmgq3WaF4ccMJxN6Zd921qeh3VGc28t6Zk/9OrlnTkXbu3NZwY9KIHY4KyaicHB09VcUZYWd8jl8fP35i1pWdszV485uvThdeeGFxYE9UXzbi3p6zfWavJ0CAAAECBAgQIECAQKMICDIzGKmpgswIMR988H4VYBmMjy7MXCAq69rbby+WmscS36jOXLeua+YXyOSV73nP8vStb32n2hv702YyMOO6EYFnBJzRToWfsWR8rPhc+b3JvR6/J+cFF1yQLrrowmKexvvHL2XP72n1iAABAgQIECBAgAABAgRCQJCZyTwYv7Q8Qsxjx176wIxMuqwbBKYUiFCoq2t92r//QPHncapz7C3YKCc6Rzj25je/tfpsN9zwjvS1r/270SZAgAABAgQIECBAgAABAgTmWUCQOc8DMP72UZkZzWESGQ2Krpy1wORK456e7vSxj3Vnv1R3/BL5ePgdO/4lfeQjnWft4I0ECBAgQIAAAQIECBAgQIBAbQQEmbVxdBUCBKYQmFydGfsNfuhDH0yf/OSdxVLeHNuKFbdUDy6Kk6p/8IMXl5jn2F99IkCAAAECBAgQIECAAAECZREQZJZlpD0ngXkUOHTo0dTbu3HCKeADA/2ps7M9u0Bz/DYPDvmZx0nj1gQIECBAgAABAgQIECBAYJKAINOUIEBgTgSiOnPv3vvSJz7RV71fVGiuW/fRbALNoaHh1N29vtq/wcFtqbOzY0583IQAAQIECBAgQIAAAQIECBA4s4Ag0wwhQGBOBSLQ7O8fSPv2HShON4+WS6C5cuWt6ZFHHq16PPXUd7Lf03NOB8/NCBAgQIAAAQIECBAgQIDAPAoIMucR360JlFkgTgffseNzaceOnVWG2DczDgWKJd0Rbs5lm3xa+bJl70oHD94/l11wLwIECBAgQIAAAQIECBAgQOAMAoJM04MAgXkVmCrQjA6tXr0qrV69sgg156L19W2cEKred9+X0vLl75uLW7sHAQIECBAgQIAAAQIECBAgMAMBQeYMkLyEAIH6C8SS8+3bB4uPsbGx6g2jSnPt2o4i0Gxru7YuHYnDiFasuLV6bdWYdWF2UQIECBAgQIAAAQIECBAgcE4Cgsxz4vNmAgTqIbBv3wNpx47BdOjQYxMuH0Hmxz++Lq1c+f6anXY+MnKkOOAnPlfa8PA9RUWoRoAAAQIECBAgQIAAAQIECOQjIMjMZyz0hACBSQKx7Hxo6EspThOvHAxUeUmcJh5h4803rzwnt5tu+kD69re/W71GXG/v3l3ndE1vJkCAAAECBAgQIECAAAECBGovIMisvakrEiBQB4FY/v2Vr3w1ff7z/3ra1ZctuzFdffWStGDBgnT99W1p6dJrZ7QMPU5P37Jl64QQc+fObTWr9qwDg0sSIECAAAECBAgQIECAAIHSCggySzv0HpxAYwrEXpq7dw8XS88nV2lOfqIIOG+66cYi1Iy9NqP99rfPph//+On0jW88kWIJe6VdeeVr05e/PDSjALQx5fSaAAECBAgQIECAAAECBAg0toAgs7HHT+8JlFog9rWMpef79h2YNtQ8E1Rr66J08OD9afHiRaX29PAECBAgQIAAAQIECBAgQCBnAUFmzqOjbwQIzFjg6NGj6eGHH00jI4eLg3smHxT0UheKE8oHB7cLMWcs7YUECBAgQIAAAQIECBAgQGB+BASZ8+PurgQIzIFA7Kv5yCOPpjg0aHR0tLjjFVdcUXx+3/v+sVhGHh8aAQIECBAgQIAAAQIECBAgkL+AIDP/MdJDAgQIECBAgAABAgQIECBAgAABAqUXEGSWfgoAIECAAAECBAgQIECAAAECBAgQIJC/gCAz/zHSQwIECBAgQIAAAQIECBAgQIAAAQKlFxBkln4KACBAgAABAgQIECBAgAABAgQIECCQv4AgM/8x0kMCBAgQIECAAAECBAgQIECAAAECpRcQZJZ+CgAgQIAAAQIECBAgQIAAAQIECBAgkL+AIDP/MdJDAgQIECBAgAABAgQIECBAgAABAqUXEGSWfgoAIECAAAECBAgQIECAAAECBAgQIJC/gCAz/zHSQwIECBAgQIAAAQIECBAgQIAAAQKlFxBkln4KACBAgAABAgQIECBAgAABAgQIECCQv4AgM/8x0kMCBAgQIECAAAECBAgQIECAAAECpRcQZJZ+CgAgQIAAAQIECBAgQIAAAQIECBAgkL+AIDP/MdJDAgQIECBAgAABAgQIECBAgAABAqUXEGSWfgoAIECAAAECBAgQIECAAAECBAgQIJC/gCAz/zHSQwIECBAgQIAAAQIECBAgQIAAAQKlFxBkln4KACBAgAABAgQIECBAgAABAgQIECCQv4AgM/8x0kMCBAgQIECAAAECBAgQIECAAAECpRcQZJZ+CgAgQIAAAQIECBAgQIAAAQIECBAgkL+AIDP/MdJDAgQIECBAgAABAgQIECBAgAABAqUXEGSWfgoAIECAAAECBAgQIECAAAECBAgQIJC/gCAz/zHSQwIECBAgQIAAAQIECBAgQIAAAQKlFxBkln4KACBAgAABAgQIECBAgAABAgQIECCQv4AgM/8x0kMCBAgQIECAAAECBAgQIECAAAECpRcQZJZ+CgAgQIAAAQIECBAgQIAAAQIECBAgkL+AIDP/MdJDAgQIECBAgAABAgQIECBAgAABAqUXEGSWfgoAIECAAAECBAgQIECAAAECBAgQIJC/gCAz/zHSQwIECBAgQIAAAQIECBAgQIAAAQKlFxBkln4KACBAgAABAgQIECBAgAABAgQIECCQv4AgM/8x0kMCBAgQIECAAAECBAgQIECAAAECpRcQZJZ+CgAgQIAAAQIECBAgQIAAAQIECBAgkL+AIDP/MdJDAgQIECBAgAABAgQIECBAgAABAqUXEGSWfgoAIECAAAECBAgQIECAAAECBAgQIJC/gCAz/zHSQwIECBAgQIAAAQIECBAgQIAAAQKlFxBkln4KACBAgAABAgQIECBAgAABAgQIECCQv4AgM/8x0kMCBAgQIECAAAECBAgQIECAAAECpRcQZJZ+CgAgQIAAAQIECBAgQIAAAQIECBAgkL+AIDP/MdJDAgQIECBAgAABAgQIEL7bi68AAAGrSURBVCBAgAABAqUXEGSWfgoAIECAAAECBAgQIECAAAECBAgQIJC/gCAz/zHSQwIECBAgQIAAAQIECBAgQIAAAQKlFxBkln4KACBAgAABAgQIECBAgAABAgQIECCQv4AgM/8x0kMCBAgQIECAAAECBAgQIECAAAECpRcQZJZ+CgAgQIAAAQIECBAgQIAAAQIECBAgkL+AIDP/MdJDAgQIECBAgAABAgQIECBAgAABAqUXEGSWfgoAIECAAAECBAgQIECAAAECBAgQIJC/gCAz/zHSQwIECBAgQIAAAQIECBAgQIAAAQKlFxBkln4KACBAgAABAgQIECBAgAABAgQIECCQv4AgM/8x0kMCBAgQIECAAAECBAgQIECAAAECpRcQZJZ+CgAgQIAAAQIECBAgQIAAAQIECBAgkL+AIDP/MdJDAgQIECBAgAABAgQIECBAgAABAqUXEGSWfgoAIECAAAECBAgQIECAAAECBAgQIJC/gCAz/zHSQwIECBAgQIAAAQIECBAgQIAAAQKlFxBkln4KACBAgAABAgQIECBAgAABAgQIECCQv8D/A4EQEWXi+YzZAAAAAElFTkSuQmCC', + panelDataGrid: [ + { + panelDataGridD: 'd', + panelDataGridC: 'c', + panelDataGridB: 'b', + panelDataGridA: 'a', + }, + { + panelDataGridD: 'h', + panelDataGridC: 'g', + panelDataGridB: 'f', + panelDataGridA: 'e', + }, + { + panelDataGridD: 'l', + panelDataGridC: 'k', + panelDataGridB: 'j', + panelDataGridA: 'i', + }, + ], + textfield: 'testing', + page2Customer: 'bob@example.com', + textfieldonPage2: 'test', + numberField: 234, + textfieldonpage1: ['a', 'b', 'c'], + panelHtml5Select: 'banana', + page3Iagreetothefollowtherules: true, + panelText: 'hello', + }, + }; - assert.equal(textFieldWithScript.element?.getElementsByTagName('script').length, 1, 'Should render srcipt tag'); - assert.equal(textAreaWithIframe.element?.getElementsByTagName('iframe').length, 1, 'Should render iframe tag'); + setTimeout(() => { + form.setPage(1); - done(); - }).catch((err) => done(err)); + setTimeout(() => { + const customerSelectEl = form.element.querySelector( + '.formio-component-page2Customer', + ); + const customerSelectValueEl = + customerSelectEl.querySelector('[ref="value"]'); + + assert.equal( + customerSelectValueEl.textContent.trim(), + 'Bob Thompson', + 'Should render Select value properly', + ); + + form.setPage(2); + + setTimeout(() => { + const htmlSelectEl = form.element.querySelector( + '.formio-component-panelHtml5Select', + ); + const htmlSelectValueEl = + htmlSelectEl.querySelector('[ref="value"]'); + + assert.equal( + htmlSelectValueEl.textContent.trim(), + 'Banana', + 'Should render HTML5 Select value properly', + ); + + Formio.makeRequest = originalMakeRequest; + + done(); + }, 400); + }, 400); + }, 300); + }) + .catch(done); }); - it('sanitizeConfig from form options must not be overriden by sanitizeConfig from global settings', function(done) { - const formElement = document.createElement('div'); - const form = new Webform(formElement, { - sanitizeConfig: { - addTags: ['iframe'], - } - }); - const testForm = fastCloneDeep(formWithNotAllowedTags); - testForm.globalSettings.sanitizeConfig = { - addTags: ['script'], - }, + it('Test optional sanitize', function (done) { + const element = document.createElement('div'); - form.setForm(testForm).then(() => { - const textFieldWithScript = form.getComponent('textFieldWithScript'); - const textAreaWithIframe = form.getComponent('textAreaWithIframe'); + Formio.createForm(element, optionalSanitize, { + sanitize: false, + }) + .then((form) => { + const sanitize = sinon.spy(FormioUtils, 'sanitize'); + form.redraw(); + setTimeout(() => { + assert.equal( + sanitize.callCount, + 0, + 'Should not sanitize templates when sanitize in not turned on', + ); + element.innerHTML = ''; + Formio.createForm(element, optionalSanitize, { + sanitize: true, + }).then((form) => { + sanitize.resetHistory(); + form.redraw(); + setTimeout(() => { + assert.equal( + sanitize.callCount, + 1, + 'Should sanitize templates when sanitize in turned on', + ); + done(); + }, 250); + }, 250); + }); + }) + .catch(done); + }); - assert.equal(textFieldWithScript.element?.getElementsByTagName('script').length, 0, 'Should not render srcipt tag'); - assert.equal(textAreaWithIframe.element?.getElementsByTagName('iframe').length, 1, 'Should render iframe tag'); + it('Should execute clearOnHide if visibility of the component inside an EditGrid has changed', function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement, { language: 'en' }); + + form.setForm(testClearOnHideInsideEditGrid) + .then(() => { + form.submission = { + state: 'submitted', + data: { + subsidiaryEditGrid: [ + { + subsidiaryEntityContainer: { + entityFullName: 'test', + divisionNum: '', + entityType: 'otherEntity', + ifOtherEntityPleaseExplain: 'test', + }, + }, + ], + }, + }; - done(); - }).catch((err) => done(err)); + setTimeout(() => { + const clearOnHideField = form.getComponent([ + 'subsidiaryEditGrid', + 0, + 'subsidiaryEntityContainer', + 'ifOtherEntityPleaseExplain', + ]); + const radioTrigger = form.getComponent([ + 'subsidiaryEditGrid', + 0, + 'subsidiaryEntityContainer', + 'entityType', + ]); + assert.equal( + form.rootPristine, + true, + 'Should not change this prop after setting a submission', + ); + assert.equal( + clearOnHideField.visible, + true, + 'Should become visible', + ); + assert.equal( + clearOnHideField.dataValue, + 'test', + 'Should set a value from the submission', + ); + + radioTrigger.setValue('subsidiary', { modified: true }); + setTimeout(() => { + assert.equal( + clearOnHideField.visible, + false, + 'Should become invisible', + ); + + radioTrigger.setValue('otherEntity', { + modified: true, + }); + setTimeout(() => { + assert.equal( + clearOnHideField.visible, + true, + 'Should become visible', + ); + assert.equal( + clearOnHideField.dataValue, + '', + 'Should clear a value due to the clearOnHide', + ); + + done(); + }, 250); + }, 250); + }, 250); + }) + .catch(done); }); - it('sanitizeConfig from form options must not be overriden by sanitizeConfig from form settings', function(done) { - const formElement = document.createElement('div'); - const form = new Webform(formElement, { - sanitizeConfig: { - addTags: ['iframe'], - } - }); - const testForm = fastCloneDeep(formWithNotAllowedTags); - testForm.settings.sanitizeConfig = { - addTags: ['script'], - }, + it('Should show values in editGrid rows with nested dataGrid when viewing submission with initEmpty option', function (done) { + const formElement = document.createElement('div'); + const formWithNestedDataGridInitEmptyOption = new Webform(formElement); - form.setForm(testForm).then(() => { - const textFieldWithScript = form.getComponent('textFieldWithScript'); - const textAreaWithIframe = form.getComponent('textAreaWithIframe'); + formWithNestedDataGridInitEmptyOption + .setForm(formWithNestedDataGridInitEmpty.form) + .then(() => { + formWithNestedDataGridInitEmptyOption.setSubmission( + formWithNestedDataGridInitEmpty.submission, + ); - assert.equal(textFieldWithScript.element?.getElementsByTagName('script').length, 0, 'Should not render srcipt tag'); - assert.equal(textAreaWithIframe.element?.getElementsByTagName('iframe').length, 1, 'Should render iframe tag'); + setTimeout(() => { + const nestedDataGridFirstRowComponentValue = + formWithNestedDataGridInitEmptyOption.element + .querySelector('[ref="editgrid-editGrid-row"]') + .querySelectorAll('.col-sm-2'); + + assert.equal( + nestedDataGridFirstRowComponentValue[1].textContent.trim(), + 'email', + ); + assert.equal( + nestedDataGridFirstRowComponentValue[2].textContent.trim(), + 'hhh@gmail.com', + ); - done(); - }).catch((err) => done(err)); + done(); + }, 200); + }) + .catch((err) => done(err)); }); - it('sanitizeConfig from form settings must not be overriden by sanitizeConfig from global settings', function(done) { - const formElement = document.createElement('div'); - const form = new Webform(formElement); - const testForm = fastCloneDeep(formWithNotAllowedTags); - testForm.settings.sanitizeConfig = { - addTags: ['iframe'], - }, + it('Should not refetch options for Select if there was an error', function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement); + const formJson = { + components: [ + { + label: 'Select', + widget: 'html5', + tableView: true, + dataSrc: 'url', + data: { + url: 'http://example.com', + headers: [ + { + key: '', + value: '', + }, + ], + }, + key: 'select', + hidden: true, + type: 'select', + input: true, + disableLimit: false, + }, + ], + }; - testForm.globalSettings.sanitizeConfig = { - addTags: ['script'], - }, + let counter = 0; + const originalMakeRequest = Formio.makeRequest; + Formio.makeRequest = function () { + return new Promise((_, reject) => { + setTimeout(() => { + counter++; + const err = new Error('Failed to fetch'); + err.networkError = true; + reject(err); + }, 50); + }); + }; - form.setForm(testForm).then(() => { - const textFieldWithScript = form.getComponent('textFieldWithScript'); - const textAreaWithIframe = form.getComponent('textAreaWithIframe'); + form.setForm(formJson) + .then(() => { + const select = form.getComponent('select'); - assert.equal(textFieldWithScript.element?.getElementsByTagName('script').length, 0, 'Should not render srcipt tag'); - assert.equal(textAreaWithIframe.element?.getElementsByTagName('iframe').length, 1, 'Should render iframe tag'); + select.visible = true; - done(); - }).catch((err) => done(err)); + setTimeout(() => { + setTimeout(() => { + select.visible = false; + + setTimeout(() => { + select.visible = true; + + setTimeout(() => { + expect(select.networkError).to.be.true; + expect(select.loadingError).to.be.true; + expect(counter).to.equal(1); + Formio.makeRequest = originalMakeRequest; + done(); + }, 200); + }, 200); + }, 200); + }, 200); + }) + .catch((err) => done(err)); }); - }); - - describe('SaveDraft functionality', function() { - const originalMakeRequest = Formio.makeRequest; - let saveDraftCalls = 0; - let restoreDraftCalls = 0; - const scenario = { - restoreDraftError: false, - saveDraftError: false, - }; - const restoredDraftData = { - textField: 'test', - number: 1234, - textArea: 'test', - submit: false, - }; - before(function(done) { - Formio.setUser({ - _id: '123' - }); - - Formio.makeRequest = (formio, type, url, method, data) => { - if (type === 'submission' && method === 'put') { - saveDraftCalls = ++saveDraftCalls; - return scenario.saveDraftError - ? Promise.reject('Save Draft Error') - : Promise.resolve(fastCloneDeep(data)); - } - if (type === 'form' && method === 'get') { - return Promise.resolve(fastCloneDeep({ - _id: '65cdd69efb1b9683c216fa1d', - title: 'test draft errors', - name: 'testDraftErrors', - path: 'testdrafterrors', - type: 'form', - display: 'form', + it('Should show only one custom error when submitting empty required field with multiple validation', function (done) { + const formJson = { components: [ - { - label: 'Text Field', - applyMaskOn: 'change', - tableView: true, - validate: { - required: true, + { + label: 'This line', + tableView: false, + storage: 'base64', + webcam: false, + fileTypes: [{ label: '', value: '' }], + multiple: true, + validate: { + required: true, + customMessage: 'will be showed once', + }, + key: 'file', + type: 'file', + input: true, }, - key: 'textField', - type: 'textfield', - input: true, - }, - { - label: 'Number', - applyMaskOn: 'change', - mask: false, - tableView: false, - delimiter: false, - requireDecimal: false, - inputFormat: 'plain', - truncateMultipleSpaces: false, - validate: { - min: 800, + { + label: 'Submit', + showValidations: false, + tableView: false, + key: 'submit', + type: 'button', + input: true, + saveOnEnter: false, }, - key: 'number', - type: 'number', - input: true, - }, - { - label: 'Text Area', - applyMaskOn: 'change', - autoExpand: false, - tableView: true, - key: 'textArea', - type: 'textarea', - input: true, - }, - { - label: 'Submit', - disableOnInvalid: true, - tableView: false, - key: 'submit', - type: 'button', - input: true, - saveOnEnter: false, - }, ], - project: '65b0ccbaf019a907ac01a869', - machineName: 'zarbzxibjafpcjb:testDraftErrors', - })); - } + }; + const element = document.createElement('div'); + const form = new Webform(element); - if (type === 'submissions' && method === 'get') { - restoreDraftCalls = ++restoreDraftCalls; - return scenario.restoreDraftError - ? Promise.reject('Restore Draft Error') - : Promise.resolve([ - fastCloneDeep({ - _id: '65d31f8da08cff1b9fc35966', - form: '65cdd69efb1b9683c216fa1d', - owner: '637b2e6b48c1227e60b1f910', - data: restoredDraftData, - project: '65b0ccbaf019a907ac01a869', - state: 'draft', - }), - ]); - } - }; + form.setForm(formJson) + .then(() => { + Harness.clickElement( + form, + form.element.querySelector('[name="data[submit]"]'), + ); - done(); + setTimeout(() => { + assert.equal(form.errors[0].messages.length, 1); + assert.equal( + form.errors[0].messages[0].message, + 'will be showed once', + ); + assert.equal( + form.element + .querySelector('[ref="errorRef"]') + .textContent.trim() + .includes('will be showed once'), + true, + ); + done(); + }, 200); + }) + .catch((err) => done(err)); }); - afterEach(function() { - saveDraftCalls = 0; - restoreDraftCalls = 0; - scenario.restoreDraftError = false; - scenario.saveDraftError = false; - }); + it('Should show validation error when submitting number with just "-" sign and required validation', function (done) { + const formJson = { + components: [ + { + label: 'Number', + mask: false, + tableView: false, + delimiter: false, + requireDecimal: false, + inputFormat: 'plain', + truncateMultipleSpaces: false, + validate: { + required: true, + }, + key: 'number', + type: 'number', + input: true, + }, + { + label: 'Submit', + showValidations: false, + tableView: false, + key: 'submit', + type: 'button', + input: true, + saveOnEnter: false, + }, + ], + }; + const element = document.createElement('div'); + const form = new Webform(element); - after(function(done) { - Formio.makeRequest = originalMakeRequest; - Formio.setUser(); - done(); + form.setForm(formJson) + .then(() => { + Harness.setInputValue(form, 'data[number]', '-_'); + Harness.clickElement( + form, + form.element.querySelector('[name="data[submit]"]'), + ); + + setTimeout(() => { + assert.equal(form.errors[0].messages.length, 1); + assert.equal( + form.errors[0].messages[0].message, + 'Number is required', + ); + assert.equal( + form.element + .querySelector('[ref="errorRef"]') + .textContent.trim() + .includes('Number is required'), + true, + ); + done(); + }, 200); + }) + .catch((err) => done(err)); }); - it('Should restore draft', function(done) { - const formElement = document.createElement('div'); - Formio.createForm( - formElement, - 'http://localhost:3000/zarbzxibjafpcjb/testdrafterrors', - { - saveDraft: true - } - ).then((form) => { - setTimeout(() => { - assert.equal(restoreDraftCalls, 1); - assert.equal(saveDraftCalls, 0); - assert.equal(form.submission.state, 'draft'); - assert.deepEqual(form.data, restoredDraftData); - done(); - }, 200); - }).catch((err) => done(err)); + describe('Test sanitizeConfig', function () { + it('Should sanitize components using default sanitizeConfig', function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement); + const testForm = fastCloneDeep(formWithNotAllowedTags); + + form.setForm(testForm) + .then(() => { + const textFieldWithScript = form.getComponent( + 'textFieldWithScript', + ); + const textAreaWithIframe = + form.getComponent('textAreaWithIframe'); + + assert.equal( + textFieldWithScript.element?.getElementsByTagName( + 'script', + ).length, + 0, + 'Should not render srcipt tag', + ); + assert.equal( + textAreaWithIframe.element?.getElementsByTagName( + 'iframe', + ).length, + 0, + 'Should not render iframe tag', + ); + + done(); + }) + .catch((err) => done(err)); + }); + + it('Should sanitize components using sanitizeConfig from form settings', function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement); + const testForm = fastCloneDeep(formWithNotAllowedTags); + (testForm.settings.sanitizeConfig = { + addTags: ['iframe', 'script'], + }), + form + .setForm(testForm) + .then(() => { + const textFieldWithScript = form.getComponent( + 'textFieldWithScript', + ); + const textAreaWithIframe = + form.getComponent('textAreaWithIframe'); + + assert.equal( + textFieldWithScript.element?.getElementsByTagName( + 'script', + ).length, + 1, + 'Should render srcipt tag', + ); + assert.equal( + textAreaWithIframe.element?.getElementsByTagName( + 'iframe', + ).length, + 1, + 'Should render iframe tag', + ); + + done(); + }) + .catch((err) => done(err)); + }); + + it('Should sanitize components using sanitizeConfig from global settings', function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement); + const testForm = fastCloneDeep(formWithNotAllowedTags); + (testForm.globalSettings.sanitizeConfig = { + addTags: ['iframe', 'script'], + }), + form + .setForm(testForm) + .then(() => { + const textFieldWithScript = form.getComponent( + 'textFieldWithScript', + ); + const textAreaWithIframe = + form.getComponent('textAreaWithIframe'); + + assert.equal( + textFieldWithScript.element?.getElementsByTagName( + 'script', + ).length, + 1, + 'Should render srcipt tag', + ); + assert.equal( + textAreaWithIframe.element?.getElementsByTagName( + 'iframe', + ).length, + 1, + 'Should render iframe tag', + ); + + done(); + }) + .catch((err) => done(err)); + }); + + it('sanitizeConfig from form options must not be overriden by sanitizeConfig from global settings', function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement, { + sanitizeConfig: { + addTags: ['iframe'], + }, + }); + const testForm = fastCloneDeep(formWithNotAllowedTags); + (testForm.globalSettings.sanitizeConfig = { + addTags: ['script'], + }), + form + .setForm(testForm) + .then(() => { + const textFieldWithScript = form.getComponent( + 'textFieldWithScript', + ); + const textAreaWithIframe = + form.getComponent('textAreaWithIframe'); + + assert.equal( + textFieldWithScript.element?.getElementsByTagName( + 'script', + ).length, + 0, + 'Should not render srcipt tag', + ); + assert.equal( + textAreaWithIframe.element?.getElementsByTagName( + 'iframe', + ).length, + 1, + 'Should render iframe tag', + ); + + done(); + }) + .catch((err) => done(err)); + }); + + it('sanitizeConfig from form options must not be overriden by sanitizeConfig from form settings', function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement, { + sanitizeConfig: { + addTags: ['iframe'], + }, + }); + const testForm = fastCloneDeep(formWithNotAllowedTags); + (testForm.settings.sanitizeConfig = { + addTags: ['script'], + }), + form + .setForm(testForm) + .then(() => { + const textFieldWithScript = form.getComponent( + 'textFieldWithScript', + ); + const textAreaWithIframe = + form.getComponent('textAreaWithIframe'); + + assert.equal( + textFieldWithScript.element?.getElementsByTagName( + 'script', + ).length, + 0, + 'Should not render srcipt tag', + ); + assert.equal( + textAreaWithIframe.element?.getElementsByTagName( + 'iframe', + ).length, + 1, + 'Should render iframe tag', + ); + + done(); + }) + .catch((err) => done(err)); + }); + + it('sanitizeConfig from form settings must not be overriden by sanitizeConfig from global settings', function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement); + const testForm = fastCloneDeep(formWithNotAllowedTags); + (testForm.settings.sanitizeConfig = { + addTags: ['iframe'], + }), + (testForm.globalSettings.sanitizeConfig = { + addTags: ['script'], + }), + form + .setForm(testForm) + .then(() => { + const textFieldWithScript = form.getComponent( + 'textFieldWithScript', + ); + const textAreaWithIframe = + form.getComponent('textAreaWithIframe'); + + assert.equal( + textFieldWithScript.element?.getElementsByTagName( + 'script', + ).length, + 0, + 'Should not render srcipt tag', + ); + assert.equal( + textAreaWithIframe.element?.getElementsByTagName( + 'iframe', + ).length, + 1, + 'Should render iframe tag', + ); + + done(); + }) + .catch((err) => done(err)); + }); }); - it('Should save draft after data is changed', function(done) { - const formElement = document.createElement('div'); - Formio.createForm( - formElement, - 'http://localhost:3000/zarbzxibjafpcjb/testdrafterrors', - { - saveDraft: true - } - ).then((form) => { - setTimeout(() => { - assert.equal(restoreDraftCalls, 1); - assert.equal(saveDraftCalls, 0); - assert.equal(form.submission.state, 'draft'); - const tfInput = form.getComponent('textField').refs.input[0]; - tfInput.value = 'test resaved'; - const inputEvent = new Event('input'); - tfInput.dispatchEvent(inputEvent); - setTimeout(() => { - assert.equal(restoreDraftCalls, 1); - assert.equal(saveDraftCalls, 1); - assert.equal(form.submission.state, 'draft'); + describe('SaveDraft functionality', function () { + const originalMakeRequest = Formio.makeRequest; + let saveDraftCalls = 0; + let restoreDraftCalls = 0; + const scenario = { + restoreDraftError: false, + saveDraftError: false, + }; + const restoredDraftData = { + textField: 'test', + number: 1234, + textArea: 'test', + submit: false, + }; + + before(function (done) { + Formio.setUser({ + _id: '123', + }); + + Formio.makeRequest = (formio, type, url, method, data) => { + if (type === 'submission' && method === 'put') { + saveDraftCalls = ++saveDraftCalls; + return scenario.saveDraftError + ? Promise.reject('Save Draft Error') + : Promise.resolve(fastCloneDeep(data)); + } + if (type === 'form' && method === 'get') { + return Promise.resolve( + fastCloneDeep({ + _id: '65cdd69efb1b9683c216fa1d', + title: 'test draft errors', + name: 'testDraftErrors', + path: 'testdrafterrors', + type: 'form', + display: 'form', + components: [ + { + label: 'Text Field', + applyMaskOn: 'change', + tableView: true, + validate: { + required: true, + }, + key: 'textField', + type: 'textfield', + input: true, + }, + { + label: 'Number', + applyMaskOn: 'change', + mask: false, + tableView: false, + delimiter: false, + requireDecimal: false, + inputFormat: 'plain', + truncateMultipleSpaces: false, + validate: { + min: 800, + }, + key: 'number', + type: 'number', + input: true, + }, + { + label: 'Text Area', + applyMaskOn: 'change', + autoExpand: false, + tableView: true, + key: 'textArea', + type: 'textarea', + input: true, + }, + { + label: 'Submit', + disableOnInvalid: true, + tableView: false, + key: 'submit', + type: 'button', + input: true, + saveOnEnter: false, + }, + ], + project: '65b0ccbaf019a907ac01a869', + machineName: 'zarbzxibjafpcjb:testDraftErrors', + }), + ); + } + + if (type === 'submissions' && method === 'get') { + restoreDraftCalls = ++restoreDraftCalls; + return scenario.restoreDraftError + ? Promise.reject('Restore Draft Error') + : Promise.resolve([ + fastCloneDeep({ + _id: '65d31f8da08cff1b9fc35966', + form: '65cdd69efb1b9683c216fa1d', + owner: '637b2e6b48c1227e60b1f910', + data: restoredDraftData, + project: '65b0ccbaf019a907ac01a869', + state: 'draft', + }), + ]); + } + }; + done(); - }, 300); - }, 200); - }).catch((err) => done(err)); - }); + }); - it('Should emit restoreDraftEvent on the restore draft error', function(done) { - const formElement = document.createElement('div'); - Formio.createForm( - formElement, - 'http://localhost:3000/zarbzxibjafpcjb/testdrafterrors', - { - saveDraft: true - } - ).then((form) => { - scenario.restoreDraftError = true; - form.on('restoreDraftError', (err) => { - assert.equal(err, 'Restore Draft Error'); - assert.equal(restoreDraftCalls, 1); - assert.equal(saveDraftCalls, 0); - done(); + afterEach(function () { + saveDraftCalls = 0; + restoreDraftCalls = 0; + scenario.restoreDraftError = false; + scenario.saveDraftError = false; }); - }).catch((err) => done(err)); - }); - it('Should emit saveDraftEvent on the save draft error', function(done) { - const formElement = document.createElement('div'); - Formio.createForm( - formElement, - 'http://localhost:3000/zarbzxibjafpcjb/testdrafterrors', - { - saveDraft: true - } - ).then((form) => { - scenario.saveDraftError = true; - form.on('saveDraftError', (err) => { - assert.equal(err, 'Save Draft Error'); - assert.equal(saveDraftCalls, 1); - assert.equal(restoreDraftCalls, 1); - assert.equal(form.submission.state, 'draft'); - done(); + after(function (done) { + Formio.makeRequest = originalMakeRequest; + Formio.setUser(); + done(); }); - setTimeout(() => { - assert.equal(saveDraftCalls, 0); - assert.equal(restoreDraftCalls, 1); - const tfInput = form.getComponent('textField').refs.input[0]; - tfInput.value = 'test resaved'; - const inputEvent = new Event('input'); - tfInput.dispatchEvent(inputEvent); - }, 200); - }).catch((err) => done(err)); - }); - }); + it('Should restore draft', function (done) { + const formElement = document.createElement('div'); + Formio.createForm( + formElement, + 'http://localhost:3000/zarbzxibjafpcjb/testdrafterrors', + { + saveDraft: true, + }, + ) + .then((form) => { + setTimeout(() => { + assert.equal(restoreDraftCalls, 1); + assert.equal(saveDraftCalls, 0); + assert.equal(form.submission.state, 'draft'); + assert.deepEqual(form.data, restoredDraftData); + done(); + }, 200); + }) + .catch((err) => done(err)); + }); + + it('Should save draft after data is changed', function (done) { + const formElement = document.createElement('div'); + Formio.createForm( + formElement, + 'http://localhost:3000/zarbzxibjafpcjb/testdrafterrors', + { + saveDraft: true, + }, + ) + .then((form) => { + setTimeout(() => { + assert.equal(restoreDraftCalls, 1); + assert.equal(saveDraftCalls, 0); + assert.equal(form.submission.state, 'draft'); + const tfInput = + form.getComponent('textField').refs.input[0]; + tfInput.value = 'test resaved'; + const inputEvent = new Event('input'); + tfInput.dispatchEvent(inputEvent); + setTimeout(() => { + assert.equal(restoreDraftCalls, 1); + assert.equal(saveDraftCalls, 1); + assert.equal(form.submission.state, 'draft'); + done(); + }, 300); + }, 200); + }) + .catch((err) => done(err)); + }); - for (const formTest of FormTests) { - const useDoneInsteadOfPromise = formTest.useDone; + it('Should emit restoreDraftEvent on the restore draft error', function (done) { + const formElement = document.createElement('div'); + Formio.createForm( + formElement, + 'http://localhost:3000/zarbzxibjafpcjb/testdrafterrors', + { + saveDraft: true, + }, + ) + .then((form) => { + scenario.restoreDraftError = true; + form.on('restoreDraftError', (err) => { + assert.equal(err, 'Restore Draft Error'); + assert.equal(restoreDraftCalls, 1); + assert.equal(saveDraftCalls, 0); + done(); + }); + }) + .catch((err) => done(err)); + }); - if (useDoneInsteadOfPromise) { - describe(formTest.title || '', function() { - for (const title in formTest.tests) { - const formTestTest = formTest.tests[title]; - it(title, function(done) { - const self = this; + it('Should emit saveDraftEvent on the save draft error', function (done) { const formElement = document.createElement('div'); - let form = new Webform(formElement, _.cloneDeep(formTest.formOptions || {})); - form.setForm(formTest.form).then(function() { - formTestTest(form, function(error) { - form = null; - formElement.innerHTML = ''; - if (error) { - return done(error); + Formio.createForm( + formElement, + 'http://localhost:3000/zarbzxibjafpcjb/testdrafterrors', + { + saveDraft: true, + }, + ) + .then((form) => { + scenario.saveDraftError = true; + form.on('saveDraftError', (err) => { + assert.equal(err, 'Save Draft Error'); + assert.equal(saveDraftCalls, 1); + assert.equal(restoreDraftCalls, 1); + assert.equal(form.submission.state, 'draft'); + done(); + }); + + setTimeout(() => { + assert.equal(saveDraftCalls, 0); + assert.equal(restoreDraftCalls, 1); + const tfInput = + form.getComponent('textField').refs.input[0]; + tfInput.value = 'test resaved'; + const inputEvent = new Event('input'); + tfInput.dispatchEvent(inputEvent); + }, 200); + }) + .catch((err) => done(err)); + }); + }); + + for (const formTest of FormTests) { + const useDoneInsteadOfPromise = formTest.useDone; + + if (useDoneInsteadOfPromise) { + describe(formTest.title || '', function () { + for (const title in formTest.tests) { + const formTestTest = formTest.tests[title]; + it(title, function (done) { + const self = this; + const formElement = document.createElement('div'); + let form = new Webform( + formElement, + _.cloneDeep(formTest.formOptions || {}), + ); + form.setForm(formTest.form).then(function () { + formTestTest( + form, + function (error) { + form = null; + formElement.innerHTML = ''; + if (error) { + return done(error); + } + done(); + }, + self, + ); + }); + }); } - done(); - }, self); }); - }); - } - }); - } - else { - describe(formTest.title || '', function() { - for (const title in formTest.tests) { - const formTestTest = formTest.tests[title]; - it(title, function() { - const formElement = document.createElement('div'); - const form = new Webform(formElement, { language: 'en' }); - return form.setForm(formTest.form).then(function() { - formTestTest(form, function(error) { - form.destroy(); - if (error) { - throw new Error(error); + } else { + describe(formTest.title || '', function () { + for (const title in formTest.tests) { + const formTestTest = formTest.tests[title]; + it(title, function () { + const formElement = document.createElement('div'); + const form = new Webform(formElement, { + language: 'en', + }); + return form.setForm(formTest.form).then(function () { + formTestTest(form, function (error) { + form.destroy(); + if (error) { + throw new Error(error); + } + }); + }); + }); } - }); }); - }); } - }); } - } }); // describe('Test the saveDraft and restoreDraft feature', () => { diff --git a/src/WebformBuilder.js b/src/WebformBuilder.js index 467009fba9..3478d0a4fc 100644 --- a/src/WebformBuilder.js +++ b/src/WebformBuilder.js @@ -3,7 +3,12 @@ import Component from './components/_classes/component/Component'; import tippy from 'tippy.js'; import Components from './components/Components'; import { Formio } from './Formio'; -import { fastCloneDeep, bootstrapVersion, getArrayFromComponentPath, getStringFromComponentPath } from './utils/utils'; +import { + fastCloneDeep, + bootstrapVersion, + getArrayFromComponentPath, + getStringFromComponentPath, +} from './utils/utils'; import { eachComponent, getComponent } from './utils/formUtils'; import BuilderUtils from './utils/builder'; import _ from 'lodash'; @@ -13,1868 +18,2354 @@ import './components/builder'; // We need this here because dragula pulls in CustomEvent class that requires global to exist. if (typeof window !== 'undefined' && typeof window.global === 'undefined') { - window.global = window; + window.global = window; } import dragula from 'dragula/dist/dragula.min.js'; export default class WebformBuilder extends Component { - // eslint-disable-next-line max-statements - constructor() { - let element, options; - if (arguments[0] instanceof HTMLElement || arguments[1]) { - element = arguments[0]; - options = arguments[1]; - } - else { - options = arguments[0]; - } - // Reset skipInit in case PDFBuilder has set it. - options.skipInit = false; - options.display = options.display || 'form'; - - super(null, options); - - this.setElement(element); - this.dragulaLib = dragula; - - this.builderHeight = 0; - this.schemas = {}; - this.repeatablePaths = []; - - this.sideBarScroll = _.get(this.options, 'sideBarScroll', true); - this.sideBarScrollOffset = _.get(this.options, 'sideBarScrollOffset', 0); - this.dragDropEnabled = true; - - // Setup the builder options. - this.builder = _.defaultsDeep({}, this.options.builder, this.defaultGroups); - - // Turn off if explicitely said to do so... - _.each(this.defaultGroups, (config, key) => { - if (config === false) { - this.builder[key] = false; - } - }); - - // Add the groups.//// - this.groups = {}; - this.groupOrder = []; - for (const group in this.builder) { - if (this.builder[group]) { - this.builder[group].key = group; - this.groups[group] = this.builder[group]; - this.groups[group].components = this.groups[group].components || {}; - this.groups[group].componentOrder = this.groups[group].componentOrder || []; - this.groups[group].subgroups = Object.keys(this.groups[group].groups || {}).map((groupKey) => { - this.groups[group].groups[groupKey].componentOrder = Object.keys(this.groups[group].groups[groupKey].components).map((key) => key); - return this.groups[group].groups[groupKey]; - }); - this.groupOrder.push(this.groups[group]); - } - } + // eslint-disable-next-line max-statements + constructor() { + let element, options; + if (arguments[0] instanceof HTMLElement || arguments[1]) { + element = arguments[0]; + options = arguments[1]; + } else { + options = arguments[0]; + } + // Reset skipInit in case PDFBuilder has set it. + options.skipInit = false; + options.display = options.display || 'form'; - this.groupOrder = this.groupOrder - .filter(group => group && !group.ignore) - .sort((a, b) => a.weight - b.weight) - .map(group => group.key); - - for (const type in Components.components) { - const component = Components.components[type]; - if (component.builderInfo && component.builderInfo.schema) { - this.schemas[type] = component.builderInfo.schema; - component.type = type; - const builderInfo = component.builderInfo; - builderInfo.key = component.type; - this.addBuilderComponentInfo(builderInfo); - } - } + super(null, options); - // Filter out any extra components. - // Add the components in each group. - for (const group in this.groups) { - const info = this.groups[group]; - for (const key in info.components) { - const compKey = group === 'resource' ? `component-${key}` : key; - let comp = info.components[compKey]; - if ( - comp === true && - Components.components[key] && - Components.components[key].builderInfo - ) { - comp = Components.components[key].builderInfo; + this.setElement(element); + this.dragulaLib = dragula; + + this.builderHeight = 0; + this.schemas = {}; + this.repeatablePaths = []; + + this.sideBarScroll = _.get(this.options, 'sideBarScroll', true); + this.sideBarScrollOffset = _.get( + this.options, + 'sideBarScrollOffset', + 0, + ); + this.dragDropEnabled = true; + + // Setup the builder options. + this.builder = _.defaultsDeep( + {}, + this.options.builder, + this.defaultGroups, + ); + + // Turn off if explicitely said to do so... + _.each(this.defaultGroups, (config, key) => { + if (config === false) { + this.builder[key] = false; + } + }); + + // Add the groups.//// + this.groups = {}; + this.groupOrder = []; + for (const group in this.builder) { + if (this.builder[group]) { + this.builder[group].key = group; + this.groups[group] = this.builder[group]; + this.groups[group].components = + this.groups[group].components || {}; + this.groups[group].componentOrder = + this.groups[group].componentOrder || []; + this.groups[group].subgroups = Object.keys( + this.groups[group].groups || {}, + ).map((groupKey) => { + this.groups[group].groups[groupKey].componentOrder = + Object.keys( + this.groups[group].groups[groupKey].components, + ).map((key) => key); + return this.groups[group].groups[groupKey]; + }); + this.groupOrder.push(this.groups[group]); + } } - if (comp && comp.schema) { - this.schemas[key] = comp.schema; - info.components[compKey] = comp; - info.components[compKey].key = key; + + this.groupOrder = this.groupOrder + .filter((group) => group && !group.ignore) + .sort((a, b) => a.weight - b.weight) + .map((group) => group.key); + + for (const type in Components.components) { + const component = Components.components[type]; + if (component.builderInfo && component.builderInfo.schema) { + this.schemas[type] = component.builderInfo.schema; + component.type = type; + const builderInfo = component.builderInfo; + builderInfo.key = component.type; + this.addBuilderComponentInfo(builderInfo); + } } - else { - // Do not include this component in the components array. - delete info.components[compKey]; + + // Filter out any extra components. + // Add the components in each group. + for (const group in this.groups) { + const info = this.groups[group]; + for (const key in info.components) { + const compKey = group === 'resource' ? `component-${key}` : key; + let comp = info.components[compKey]; + if ( + comp === true && + Components.components[key] && + Components.components[key].builderInfo + ) { + comp = Components.components[key].builderInfo; + } + if (comp && comp.schema) { + this.schemas[key] = comp.schema; + info.components[compKey] = comp; + info.components[compKey].key = key; + } else { + // Do not include this component in the components array. + delete info.components[compKey]; + } + } + + // Order the components. + this.orderComponents(info); } - } - // Order the components. - this.orderComponents(info); - } + this.options.hooks = this.options.hooks || {}; - this.options.hooks = this.options.hooks || {}; + this.options.hooks.renderComponent = (html, { component, self }) => { + if (self.type === 'form' && !self.key) { + const template = this.hook( + 'renderComponentFormTemplate', + html.replace('formio-component-form', ''), + ); + // The main webform shouldn't have this class as it adds extra styles. + return template; + } - this.options.hooks.renderComponent = (html, { component, self }) => { - if (self.type === 'form' && !self.key) { - const template = this.hook('renderComponentFormTemplate', html.replace('formio-component-form', '')); - // The main webform shouldn't have this class as it adds extra styles. - return template; - } + if ( + (this.options.disabled && + this.options.disabled.includes(self.key)) || + self.parent.noDragDrop + ) { + return html; + } - if (this.options.disabled && this.options.disabled.includes(self.key) || self.parent.noDragDrop) { - return html; - } + return this.renderTemplate('builderComponent', { + html, + disableBuilderActions: self?.component?.disableBuilderActions, + childComponent: component, + }); + }; - return this.renderTemplate('builderComponent', { - html, - disableBuilderActions: self?.component?.disableBuilderActions, - childComponent: component, - }); - }; + this.options.hooks.renderComponents = (html, { components, self }) => { + // if Datagrid and already has a component, don't make it droppable. + if ( + (self.type === 'datagrid' && components.length > 0) || + self.noDragDrop + ) { + return html; + } - this.options.hooks.renderComponents = (html, { components, self }) => { - // if Datagrid and already has a component, don't make it droppable. - if (self.type === 'datagrid' && components.length > 0 || self.noDragDrop) { - return html; - } - - if (!components || - (!components.length && !components.nodrop) || - (self.type === 'form' && components.length <= 1 && (components.length === 0 || components[0].type === 'button')) - ) { - html = this.renderTemplate('builderPlaceholder', { - position: 0 - }) + html; - } - return this.renderTemplate('builderComponents', { - key: self.key, - type: self.type, - html, - }); - }; + if ( + !components || + (!components.length && !components.nodrop) || + (self.type === 'form' && + components.length <= 1 && + (components.length === 0 || + components[0].type === 'button')) + ) { + html = + this.renderTemplate('builderPlaceholder', { + position: 0, + }) + html; + } + return this.renderTemplate('builderComponents', { + key: self.key, + type: self.type, + html, + }); + }; - this.options.hooks.renderInput = (html, { self }) => { - if (self.type === 'hidden') { - return html + self.name; - } - return html; - }; + this.options.hooks.renderInput = (html, { self }) => { + if (self.type === 'hidden') { + return html + self.name; + } + return html; + }; - this.options.hooks.renderLoading = (html, { self }) => { - if (self.type === 'form' && self.key) { - return self.name; - } - return html; - }; + this.options.hooks.renderLoading = (html, { self }) => { + if (self.type === 'form' && self.key) { + return self.name; + } + return html; + }; - this.options.hooks.attachComponents = (element, components, container, component) => { - // Don't attach if no element was found or component doesn't participate in drag'n'drop. - if (!element) { - return; - } - if (component.noDragDrop) { - return element; - } - // Attach container and component to element for later reference. - const containerElement = element.querySelector(`[ref="${component.component.key}-container"]`) || element; - containerElement.formioContainer = container; - containerElement.formioComponent = component; - - // Add container to draggable list. - if (this.dragula && this.allowDrop(element)) { - this.dragula.containers.push(containerElement); - } - - // If this is an existing datagrid element, don't make it draggable. - if ((component.type === 'datagrid' || component.type === 'datamap') && components.length > 0) { - return element; - } + this.options.hooks.attachComponents = ( + element, + components, + container, + component, + ) => { + // Don't attach if no element was found or component doesn't participate in drag'n'drop. + if (!element) { + return; + } + if (component.noDragDrop) { + return element; + } + // Attach container and component to element for later reference. + const containerElement = + element.querySelector( + `[ref="${component.component.key}-container"]`, + ) || element; + containerElement.formioContainer = container; + containerElement.formioComponent = component; + + // Add container to draggable list. + if (this.dragula && this.allowDrop(element)) { + this.dragula.containers.push(containerElement); + } - // Since we added a wrapper, need to return the original element so that we can find the components inside it. - return element.children[0]; - }; + // If this is an existing datagrid element, don't make it draggable. + if ( + (component.type === 'datagrid' || + component.type === 'datamap') && + components.length > 0 + ) { + return element; + } - this.options.hooks.attachDatagrid = (element, component) => { - component.loadRefs(element, { - [`${component.key}-container`]: 'single', - }); + // Since we added a wrapper, need to return the original element so that we can find the components inside it. + return element.children[0]; + }; - const dataGridContainer = component.refs[`${component.key}-container`]; + this.options.hooks.attachDatagrid = (element, component) => { + component.loadRefs(element, { + [`${component.key}-container`]: 'single', + }); - if (dataGridContainer) { - component.attachComponents(dataGridContainer.parentNode, [], component.component.components); - } - // Need to set up horizontal rearrangement of fields. - }; + const dataGridContainer = + component.refs[`${component.key}-container`]; - this.options.hooks.attachComponent = this.attachComponent.bind(this); + if (dataGridContainer) { + component.attachComponents( + dataGridContainer.parentNode, + [], + component.component.components, + ); + } + // Need to set up horizontal rearrangement of fields. + }; - // Load resources tagged as 'builder' - const query = { - params: { - type: 'resource', - limit: 1000000, - select: '_id,title,name,components' - } - }; - if (this.options && this.options.resourceTag) { - query.params.tags = [this.options.resourceTag]; + this.options.hooks.attachComponent = this.attachComponent.bind(this); + + // Load resources tagged as 'builder' + const query = { + params: { + type: 'resource', + limit: 1000000, + select: '_id,title,name,components', + }, + }; + if (this.options && this.options.resourceTag) { + query.params.tags = [this.options.resourceTag]; + } else if ( + !this.options || + !Object.prototype.hasOwnProperty.call(this.options, 'resourceTag') + ) { + query.params.tags = ['builder']; + } + const formio = new Formio(Formio.projectUrl); + const isResourcesDisabled = + this.options.builder && this.options.builder.resource === false; + + formio + .loadProject() + .then((project) => { + if ( + project && + (_.get(project, 'settings.addConfigToForms', false) || + _.get(project, 'addConfigToForms', false)) + ) { + const config = project.config || {}; + this.options.formConfig = config; + + const pathToFormConfig = 'webform._form.config'; + const webformConfig = _.get(this, pathToFormConfig); + + if (this.webform && !webformConfig) { + _.set(this, pathToFormConfig, config); + } + } + }) + .catch((err) => { + console.warn( + `Could not load project settings: ${err.message || err}`, + ); + }); + + if (!formio.noProject && !isResourcesDisabled && formio.formsUrl) { + const resourceOptions = + this.options.builder && this.options.builder.resource; + formio.loadForms(query).then((resources) => { + if (resources.length) { + this.builder.resource = { + title: resourceOptions + ? resourceOptions.title + : 'Existing Resource Fields', + key: 'resource', + weight: resourceOptions ? resourceOptions.weight : 50, + subgroups: [], + components: [], + componentOrder: [], + }; + this.groups.resource = { + title: resourceOptions + ? resourceOptions.title + : 'Existing Resource Fields', + key: 'resource', + weight: resourceOptions ? resourceOptions.weight : 50, + subgroups: [], + components: [], + componentOrder: [], + }; + if (!this.groupOrder.includes('resource')) { + this.groupOrder.push('resource'); + } + this.addExistingResourceFields(resources); + } + }); + } + + // Notify components if they need to modify their render. + this.options.attachMode = 'builder'; + this.webform = this.webform || this.createForm(this.options); + + this.pathComponentsMapping = {}; + this.arrayDataComponentPaths = []; + this.nestedDataComponents = []; + this.arrayDataComponents = []; } - else if (!this.options || !Object.prototype.hasOwnProperty.call(this.options, 'resourceTag')) { - query.params.tags = ['builder']; + + allowDrop() { + return true; } - const formio = new Formio(Formio.projectUrl); - const isResourcesDisabled = this.options.builder && this.options.builder.resource === false; - - formio.loadProject().then((project) => { - if (project && (_.get(project, 'settings.addConfigToForms', false) || _.get(project, 'addConfigToForms', false))) { - const config = project.config || {}; - this.options.formConfig = config; - - const pathToFormConfig = 'webform._form.config'; - const webformConfig = _.get(this, pathToFormConfig); - - if (this.webform && !webformConfig) { - _.set(this, pathToFormConfig, config); - } - } - }).catch((err) => { - console.warn(`Could not load project settings: ${err.message || err}`); - }); - - if (!formio.noProject && !isResourcesDisabled && formio.formsUrl) { - const resourceOptions = this.options.builder && this.options.builder.resource; - formio.loadForms(query) - .then((resources) => { - if (resources.length) { - this.builder.resource = { - title: resourceOptions ? resourceOptions.title : 'Existing Resource Fields', - key: 'resource', - weight: resourceOptions ? resourceOptions.weight : 50, - subgroups: [], - components: [], - componentOrder: [] - }; - this.groups.resource = { - title: resourceOptions ? resourceOptions.title : 'Existing Resource Fields', - key: 'resource', - weight: resourceOptions ? resourceOptions.weight : 50, - subgroups: [], - components: [], - componentOrder: [] + + addExistingResourceFields(resources) { + _.each(resources, (resource, index) => { + const resourceKey = `resource-${resource.name}`; + const subgroup = { + key: resourceKey, + title: resource.title, + components: [], + componentOrder: [], + default: index === 0, }; - if (!this.groupOrder.includes('resource')) { - this.groupOrder.push('resource'); - } - this.addExistingResourceFields(resources); - } + + eachComponent( + resource.components, + (component) => { + if (component.type === 'button') return; + if ( + this.options && + this.options.resourceFilter && + (!component.tags || + component.tags.indexOf( + this.options.resourceFilter, + ) === -1) + ) + return; + + let componentName = component.label; + if (!componentName && component.key) { + componentName = _.upperFirst(component.key); + } + + subgroup.componentOrder.push(`component-${component.key}`); + subgroup.components[`component-${component.key}`] = _.merge( + fastCloneDeep( + Components.components[component.type] + ? Components.components[component.type] + .builderInfo + : Components.components['unknown'].builderInfo, + ), + { + key: component.key, + title: componentName, + group: 'resource', + subgroup: resourceKey, + }, + { + schema: { + ...component, + label: component.label, + key: component.key, + lockKey: true, + source: !this.options.noSource + ? resource._id + : undefined, + isNew: true, + }, + }, + ); + }, + true, + ); + + this.groups.resource.subgroups.push(subgroup); }); - } - // Notify components if they need to modify their render. - this.options.attachMode = 'builder'; - this.webform = this.webform || this.createForm(this.options); - - this.pathComponentsMapping = {}; - this.arrayDataComponentPaths = []; - this.nestedDataComponents = []; - this.arrayDataComponents = []; - } - - allowDrop() { - return true; - } - - addExistingResourceFields(resources) { - _.each(resources, (resource, index) => { - const resourceKey = `resource-${resource.name}`; - const subgroup = { - key: resourceKey, - title: resource.title, - components: [], - componentOrder: [], - default: index === 0, - }; - - eachComponent(resource.components, (component) => { - if (component.type === 'button') return; - if ( - this.options && - this.options.resourceFilter && - (!component.tags || component.tags.indexOf(this.options.resourceFilter) === -1) - ) return; - - let componentName = component.label; - if (!componentName && component.key) { - componentName = _.upperFirst(component.key); - } - - subgroup.componentOrder.push(`component-${component.key}`); - subgroup.components[`component-${component.key}`] = _.merge( - fastCloneDeep(Components.components[component.type] - ? Components.components[component.type].builderInfo - : Components.components['unknown'].builderInfo), - { - key: component.key, - title: componentName, - group: 'resource', - subgroup: resourceKey, - }, - { - schema: { - ...component, - label: component.label, - key: component.key, - lockKey: true, - source: (!this.options.noSource ? resource._id : undefined), - isNew: true - } - } - ); - }, true); - - this.groups.resource.subgroups.push(subgroup); - }); - - this.triggerRedraw(); - } - - attachTooltip(component, title) { - return tippy(component, { - allowHTML: true, - trigger: 'mouseenter focus', - placement: 'top', - delay: [200, 0], - zIndex: 10000, - content: title - }); - } - - attachComponent(element, component) { - if (component instanceof WebformBuilder) { - return; + this.triggerRedraw(); } - // Add component to element for later reference. - element.formioComponent = component; + attachTooltip(component, title) { + return tippy(component, { + allowHTML: true, + trigger: 'mouseenter focus', + placement: 'top', + delay: [200, 0], + zIndex: 10000, + content: title, + }); + } - component.loadRefs(element, { - removeComponent: 'single', - editComponent: 'single', - moveComponent: 'single', - copyComponent: 'single', - pasteComponent: 'single', - editJson: 'single' - }); + attachComponent(element, component) { + if (component instanceof WebformBuilder) { + return; + } - if (component.refs.copyComponent) { - this.attachTooltip(component.refs.copyComponent, this.t('Copy')); + // Add component to element for later reference. + element.formioComponent = component; - component.addEventListener(component.refs.copyComponent, 'click', () => - this.copyComponent(component)); - } + component.loadRefs(element, { + removeComponent: 'single', + editComponent: 'single', + moveComponent: 'single', + copyComponent: 'single', + pasteComponent: 'single', + editJson: 'single', + }); - if (component.refs.pasteComponent) { - const pasteToolTip = this.attachTooltip(component.refs.pasteComponent, this.t('Paste below')); + if (component.refs.copyComponent) { + this.attachTooltip(component.refs.copyComponent, this.t('Copy')); - component.addEventListener(component.refs.pasteComponent, 'click', () => { - pasteToolTip.hide(); - this.pasteComponent(component); - }); - } + component.addEventListener( + component.refs.copyComponent, + 'click', + () => this.copyComponent(component), + ); + } - if (component.refs.moveComponent) { - this.attachTooltip(component.refs.moveComponent, this.t('Move')); - if (this.keyboardActionsEnabled) { - component.addEventListener(component.refs.moveComponent, 'click', () => { - this.moveComponent(component); - }); - } - } + if (component.refs.pasteComponent) { + const pasteToolTip = this.attachTooltip( + component.refs.pasteComponent, + this.t('Paste below'), + ); + + component.addEventListener( + component.refs.pasteComponent, + 'click', + () => { + pasteToolTip.hide(); + this.pasteComponent(component); + }, + ); + } - const parent = this.getParentElement(element); + if (component.refs.moveComponent) { + this.attachTooltip(component.refs.moveComponent, this.t('Move')); + if (this.keyboardActionsEnabled) { + component.addEventListener( + component.refs.moveComponent, + 'click', + () => { + this.moveComponent(component); + }, + ); + } + } - if (component.refs.editComponent) { - this.attachTooltip(component.refs.editComponent, this.t('Edit')); + const parent = this.getParentElement(element); + + if (component.refs.editComponent) { + this.attachTooltip(component.refs.editComponent, this.t('Edit')); + + component.addEventListener( + component.refs.editComponent, + 'click', + () => + this.editComponent( + component.schema, + parent, + false, + false, + component.component, + { inDataGrid: component.isInDataGrid }, + ), + ); + } - component.addEventListener(component.refs.editComponent, 'click', () => - this.editComponent(component.schema, parent, false, false, component.component, { inDataGrid: component.isInDataGrid })); - } + if (component.refs.editJson) { + this.attachTooltip(component.refs.editJson, this.t('Edit JSON')); + + component.addEventListener(component.refs.editJson, 'click', () => + this.editComponent( + component.schema, + parent, + false, + true, + component.component, + ), + ); + } - if (component.refs.editJson) { - this.attachTooltip(component.refs.editJson, this.t('Edit JSON')); + if (component.refs.removeComponent) { + this.attachTooltip( + component.refs.removeComponent, + this.t('Remove'), + ); + + component.addEventListener( + component.refs.removeComponent, + 'click', + () => + this.removeComponent( + component.schema, + parent, + component.component, + component, + ), + ); + } - component.addEventListener(component.refs.editJson, 'click', () => - this.editComponent(component.schema, parent, false, true, component.component)); + return element; } - if (component.refs.removeComponent) { - this.attachTooltip(component.refs.removeComponent, this.t('Remove')); + createForm(options) { + this.webform = new Webform(this.element, options); + if (this.element) { + this.loadRefs(this.element, { + form: 'single', + }); + if (this.refs.form) { + this.webform.element = this.refs.form; + } + } + return this.webform; + } - component.addEventListener(component.refs.removeComponent, 'click', () => - this.removeComponent(component.schema, parent, component.component, component)); + /** + * Called when everything is ready. + * + * @returns {Promise} - Wait for webform to be ready. + */ + get ready() { + return this.webform.ready; } - return element; - } - - createForm(options) { - this.webform = new Webform(this.element, options); - if (this.element) { - this.loadRefs(this.element, { - form: 'single' - }); - if (this.refs.form) { - this.webform.element = this.refs.form; - } + get defaultGroups() { + return { + basic: { + title: 'Basic', + weight: 0, + default: true, + }, + advanced: { + title: 'Advanced', + weight: 10, + }, + layout: { + title: 'Layout', + weight: 20, + }, + data: { + title: 'Data', + weight: 30, + }, + premium: { + title: 'Premium', + weight: 40, + }, + }; } - return this.webform; - } - - /** - * Called when everything is ready. - * - * @returns {Promise} - Wait for webform to be ready. - */ - get ready() { - return this.webform.ready; - } - - get defaultGroups() { - return { - basic: { - title: 'Basic', - weight: 0, - default: true, - }, - advanced: { - title: 'Advanced', - weight: 10 - }, - layout: { - title: 'Layout', - weight: 20 - }, - data: { - title: 'Data', - weight: 30 - }, - premium: { - title: 'Premium', - weight: 40 - } - }; - } - - redraw() { - return Webform.prototype.redraw.call(this); - } - - get form() { - return this.webform.form; - } - - get schema() { - return this.webform.schema; - } - - set form(value) { - this.setForm(value); - } - - get container() { - return this.webform.form.components; - } - - /** - * When a component sets its api key, we need to check if it is unique within its namespace. Find the namespace root - * so we can calculate this correctly. - * @param component - */ - findNamespaceRoot(component) { - const path = getArrayFromComponentPath(component.path); - // First get the component with nested parents. - let comp = this.webform.getComponent(path); - comp = Array.isArray(comp) ? comp[0] : comp; - const namespaceKey = this.recurseNamespace(comp); - - // If there is no key, it is the root form. - if (!namespaceKey || this.form.key === namespaceKey) { - return this.form.components; + + redraw() { + return Webform.prototype.redraw.call(this); } - const componentSchema = component.component; - // If the current component is the namespace, we don't need to find it again. - if (namespaceKey === component.key) { - return [...componentSchema.components, componentSchema]; + get form() { + return this.webform.form; } - // Get the namespace component so we have the original object. - const namespaceComponent = getComponent(this.form.components, namespaceKey, true); - return namespaceComponent ? namespaceComponent.components : comp.components; - } + get schema() { + return this.webform.schema; + } - recurseNamespace(component) { - // If there is no parent, we are at the root level. - if (!component) { - return null; + set form(value) { + this.setForm(value); } - // Some components are their own namespace. - if (['address', 'container', 'datagrid', 'editgrid', 'dynamicWizard', 'tree'].includes(component.type) || component.tree || component.arrayTree) { - return component.key; + get container() { + return this.webform.form.components; } - // Anything else, keep going up. - return this.recurseNamespace(component.parent); - } - - render() { - return this.renderTemplate('builder', { - sidebar: this.renderTemplate('builderSidebar', { - scrollEnabled: this.sideBarScroll, - groupOrder: this.groupOrder, - groupId: `builder-sidebar-${this.id}`, - groups: this.groupOrder.map((groupKey) => this.renderTemplate('builderSidebarGroup', { - group: this.groups[groupKey], - groupKey, - groupId: `builder-sidebar-${this.id}`, - subgroups: this.groups[groupKey].subgroups.map((group) => this.renderTemplate('builderSidebarGroup', { - group, - groupKey: group.key, - groupId: `group-container-${groupKey}`, - subgroups: [] - })), - keyboardActionsEnabled: this.keyboardActionsEnabled, - })), - }), - form: this.webform.render(), - }); - } - - attach(element) { - this.on('change', (form) => { - this.populateRecaptchaSettings(form); - }); - return super.attach(element).then(() => { - this.loadRefs(element, { - form: 'single', - sidebar: 'single', - 'sidebar-search': 'single', - 'sidebar-groups': 'single', - 'container': 'multiple', - 'sidebar-anchor': 'multiple', - 'sidebar-group': 'multiple', - 'sidebar-container': 'multiple', - 'sidebar-component': 'multiple', - }); - - if (this.sideBarScroll && Templates.current.handleBuilderSidebarScroll) { - Templates.current.handleBuilderSidebarScroll.call(this, this); - } - - // Add the paste status in form - if (typeof window !== 'undefined' && window.sessionStorage) { - const data = window.sessionStorage.getItem('formio.clipboard'); - if (data) { - this.addClass(this.refs.form, 'builder-paste-mode'); + /** + * When a component sets its api key, we need to check if it is unique within its namespace. Find the namespace root + * so we can calculate this correctly. + * @param component + */ + findNamespaceRoot(component) { + const path = getArrayFromComponentPath(component.path); + // First get the component with nested parents. + let comp = this.webform.getComponent(path); + comp = Array.isArray(comp) ? comp[0] : comp; + const namespaceKey = this.recurseNamespace(comp); + + // If there is no key, it is the root form. + if (!namespaceKey || this.form.key === namespaceKey) { + return this.form.components; } - } - if (!bootstrapVersion(this.options)) { - const getAttribute = (anchor, attribute) => { - let elem = anchor.getAttribute(`data-${attribute}`); - if (!elem) { - elem = anchor.getAttribute(`data-bs-${attribute}`); - } - return elem; - }; + const componentSchema = component.component; + // If the current component is the namespace, we don't need to find it again. + if (namespaceKey === component.key) { + return [...componentSchema.components, componentSchema]; + } - const hideShow = (group, show) => { - if (show) { - group.classList.add(['show']); - group.style.display = 'inherit'; - } - else { - group.classList.remove(['show']); - group.style.display = 'none'; - } - }; + // Get the namespace component so we have the original object. + const namespaceComponent = getComponent( + this.form.components, + namespaceKey, + true, + ); + return namespaceComponent + ? namespaceComponent.components + : comp.components; + } + + recurseNamespace(component) { + // If there is no parent, we are at the root level. + if (!component) { + return null; + } - // Initialize - this.refs['sidebar-group'].forEach((group) => { - hideShow(group, getAttribute(group, 'default') === 'true'); + // Some components are their own namespace. + if ( + [ + 'address', + 'container', + 'datagrid', + 'editgrid', + 'dynamicWizard', + 'tree', + ].includes(component.type) || + component.tree || + component.arrayTree + ) { + return component.key; + } + + // Anything else, keep going up. + return this.recurseNamespace(component.parent); + } + + render() { + return this.renderTemplate('builder', { + sidebar: this.renderTemplate('builderSidebar', { + scrollEnabled: this.sideBarScroll, + groupOrder: this.groupOrder, + groupId: `builder-sidebar-${this.id}`, + groups: this.groupOrder.map((groupKey) => + this.renderTemplate('builderSidebarGroup', { + group: this.groups[groupKey], + groupKey, + groupId: `builder-sidebar-${this.id}`, + subgroups: this.groups[groupKey].subgroups.map( + (group) => + this.renderTemplate('builderSidebarGroup', { + group, + groupKey: group.key, + groupId: `group-container-${groupKey}`, + subgroups: [], + }), + ), + keyboardActionsEnabled: this.keyboardActionsEnabled, + }), + ), + }), + form: this.webform.render(), }); + } - // Click event - this.refs['sidebar-anchor'].forEach((anchor, index) => { - this.addEventListener(anchor, 'click', () => { - const clickedParentId = getAttribute(anchor, 'parent').slice('#builder-sidebar-'.length); - const clickedId = getAttribute(anchor, 'target').slice('#group-'.length); - this.refs['sidebar-group'].forEach((group, groupIndex) => { - const openByDefault = getAttribute(group, 'default') === 'true'; - const groupId = group.getAttribute('id').slice('group-'.length); - const groupParent = getAttribute(group, 'parent').slice('#builder-sidebar-'.length); - hideShow(group, ((openByDefault && groupParent === clickedId) || groupId === clickedParentId || groupIndex === index)); - }); - }, true); + attach(element) { + this.on('change', (form) => { + this.populateRecaptchaSettings(form); }); - } + return super.attach(element).then(() => { + this.loadRefs(element, { + form: 'single', + sidebar: 'single', + 'sidebar-search': 'single', + 'sidebar-groups': 'single', + container: 'multiple', + 'sidebar-anchor': 'multiple', + 'sidebar-group': 'multiple', + 'sidebar-container': 'multiple', + 'sidebar-component': 'multiple', + }); - if (this.keyboardActionsEnabled) { - this.refs['sidebar-component'].forEach((component) => { - this.addEventListener(component, 'keydown', (event) => { - if (event.keyCode === 13) { - this.addNewComponent(component); + if ( + this.sideBarScroll && + Templates.current.handleBuilderSidebarScroll + ) { + Templates.current.handleBuilderSidebarScroll.call(this, this); } - }); - }); - } - - this.addEventListener(this.refs['sidebar-search'], 'input', - _.debounce((e) => { - const searchString = e.target.value; - this.searchFields(searchString); - }, 300) - ); - - if (this.dragDropEnabled) { - this.initDragula(); - } - - const drake = this.dragula; - - if (this.refs.form) { - autoScroll([window], { - margin: 20, - maxSpeed: 6, - scrollWhenOutside: true, - autoScroll: function() { - return this.down && drake?.dragging; - } - }); - return this.webform.attach(this.refs.form); - } - }); - } + // Add the paste status in form + if (typeof window !== 'undefined' && window.sessionStorage) { + const data = window.sessionStorage.getItem('formio.clipboard'); + if (data) { + this.addClass(this.refs.form, 'builder-paste-mode'); + } + } - searchFields(searchString = '') { - const searchValue = searchString.toLowerCase(); - const sidebar = this.refs['sidebar']; - const sidebarGroups = this.refs['sidebar-groups']; + if (!bootstrapVersion(this.options)) { + const getAttribute = (anchor, attribute) => { + let elem = anchor.getAttribute(`data-${attribute}`); + if (!elem) { + elem = anchor.getAttribute(`data-bs-${attribute}`); + } + return elem; + }; + + const hideShow = (group, show) => { + if (show) { + group.classList.add(['show']); + group.style.display = 'inherit'; + } else { + group.classList.remove(['show']); + group.style.display = 'none'; + } + }; + + // Initialize + this.refs['sidebar-group'].forEach((group) => { + hideShow(group, getAttribute(group, 'default') === 'true'); + }); + + // Click event + this.refs['sidebar-anchor'].forEach((anchor, index) => { + this.addEventListener( + anchor, + 'click', + () => { + const clickedParentId = getAttribute( + anchor, + 'parent', + ).slice('#builder-sidebar-'.length); + const clickedId = getAttribute( + anchor, + 'target', + ).slice('#group-'.length); + this.refs['sidebar-group'].forEach( + (group, groupIndex) => { + const openByDefault = + getAttribute(group, 'default') === + 'true'; + const groupId = group + .getAttribute('id') + .slice('group-'.length); + const groupParent = getAttribute( + group, + 'parent', + ).slice('#builder-sidebar-'.length); + hideShow( + group, + (openByDefault && + groupParent === clickedId) || + groupId === clickedParentId || + groupIndex === index, + ); + }, + ); + }, + true, + ); + }); + } - if (!sidebar || !sidebarGroups) { - return; - } + if (this.keyboardActionsEnabled) { + this.refs['sidebar-component'].forEach((component) => { + this.addEventListener(component, 'keydown', (event) => { + if (event.keyCode === 13) { + this.addNewComponent(component); + } + }); + }); + } + + this.addEventListener( + this.refs['sidebar-search'], + 'input', + _.debounce((e) => { + const searchString = e.target.value; + this.searchFields(searchString); + }, 300), + ); + + if (this.dragDropEnabled) { + this.initDragula(); + } - const filterGroupBy = (group, searchValue = '') => { - const result = _.toPlainObject(group); - const { subgroups = [], components } = result; - const filteredComponents = []; + const drake = this.dragula; - for (const key in components) { - const isMatchedToTitle = this.t(components[key].title).toLowerCase().match(searchValue); - const isMatchedToKey = components[key].key.toLowerCase().match(searchValue); + if (this.refs.form) { + autoScroll([window], { + margin: 20, + maxSpeed: 6, + scrollWhenOutside: true, + autoScroll: function () { + return this.down && drake?.dragging; + }, + }); - if (isMatchedToTitle || isMatchedToKey) { - filteredComponents.push(components[key]); + return this.webform.attach(this.refs.form); + } + }); + } + + searchFields(searchString = '') { + const searchValue = searchString.toLowerCase(); + const sidebar = this.refs['sidebar']; + const sidebarGroups = this.refs['sidebar-groups']; + + if (!sidebar || !sidebarGroups) { + return; } - } - this.orderComponents(result, filteredComponents); - if (searchValue) { - result.default = true; - } - if (result.componentOrder.length || subgroups.length) { - return result; - } - return null; - }; + const filterGroupBy = (group, searchValue = '') => { + const result = _.toPlainObject(group); + const { subgroups = [], components } = result; + const filteredComponents = []; + + for (const key in components) { + const isMatchedToTitle = this.t(components[key].title) + .toLowerCase() + .match(searchValue); + const isMatchedToKey = components[key].key + .toLowerCase() + .match(searchValue); + + if (isMatchedToTitle || isMatchedToKey) { + filteredComponents.push(components[key]); + } + } - const filterGroupOrder = (groupOrder, searchValue) => { - const result = _.cloneDeep(groupOrder); - return result.filter(key => filterGroupBy(this.groups[key], searchValue)); - }; + this.orderComponents(result, filteredComponents); + if (searchValue) { + result.default = true; + } + if (result.componentOrder.length || subgroups.length) { + return result; + } + return null; + }; - const filterSubgroups = (groups, searchValue) => { - const result = _.clone(groups); - return result - .map(subgroup => filterGroupBy(subgroup, searchValue)) - .filter(subgroup => !_.isNull(subgroup)); - }; + const filterGroupOrder = (groupOrder, searchValue) => { + const result = _.cloneDeep(groupOrder); + return result.filter((key) => + filterGroupBy(this.groups[key], searchValue), + ); + }; - const toTemplate = groupKey => { - return { - group: filterGroupBy(this.groups[groupKey], searchValue), - groupKey, - groupId: sidebar.id || sidebarGroups.id, - subgroups: filterSubgroups(this.groups[groupKey].subgroups, searchValue) - .map((group) => this.renderTemplate('builderSidebarGroup', { - group, - groupKey: group.key, - groupId: `group-container-${groupKey}`, - subgroups: [] - })), - }; - }; + const filterSubgroups = (groups, searchValue) => { + const result = _.clone(groups); + return result + .map((subgroup) => filterGroupBy(subgroup, searchValue)) + .filter((subgroup) => !_.isNull(subgroup)); + }; - sidebarGroups.innerHTML = filterGroupOrder(this.groupOrder, searchValue) - .map(groupKey => this.renderTemplate('builderSidebarGroup', toTemplate(groupKey))) - .join(''); + const toTemplate = (groupKey) => { + return { + group: filterGroupBy(this.groups[groupKey], searchValue), + groupKey, + groupId: sidebar.id || sidebarGroups.id, + subgroups: filterSubgroups( + this.groups[groupKey].subgroups, + searchValue, + ).map((group) => + this.renderTemplate('builderSidebarGroup', { + group, + groupKey: group.key, + groupId: `group-container-${groupKey}`, + subgroups: [], + }), + ), + }; + }; - this.loadRefs(this.element, { - 'sidebar-groups': 'single', - 'sidebar-anchor': 'multiple', - 'sidebar-group': 'multiple', - 'sidebar-container': 'multiple', - }); + sidebarGroups.innerHTML = filterGroupOrder(this.groupOrder, searchValue) + .map((groupKey) => + this.renderTemplate( + 'builderSidebarGroup', + toTemplate(groupKey), + ), + ) + .join(''); + + this.loadRefs(this.element, { + 'sidebar-groups': 'single', + 'sidebar-anchor': 'multiple', + 'sidebar-group': 'multiple', + 'sidebar-container': 'multiple', + }); - this.updateDragAndDrop(); + this.updateDragAndDrop(); - if (searchValue === '') { - this.triggerRedraw(); - } - } - - orderComponents(groupInfo, foundComponents) { - const components = foundComponents || groupInfo.components; - const isResource = groupInfo.key.indexOf('resource-') === 0; - if (components) { - groupInfo.componentOrder = Object.keys(components) - .map(key => components[key]) - .filter(component => component && !component.ignore && !component.ignoreForForm) - .sort((a, b) => a.weight - b.weight) - .map(component => isResource ? `component-${component.key}` : component.key); + if (searchValue === '') { + this.triggerRedraw(); + } } - } - updateDragAndDrop() { - if (this.dragDropEnabled) { - this.initDragula(); - } - if (this.refs.form) { - return this.webform.attach(this.refs.form); + orderComponents(groupInfo, foundComponents) { + const components = foundComponents || groupInfo.components; + const isResource = groupInfo.key.indexOf('resource-') === 0; + if (components) { + groupInfo.componentOrder = Object.keys(components) + .map((key) => components[key]) + .filter( + (component) => + component && + !component.ignore && + !component.ignoreForForm, + ) + .sort((a, b) => a.weight - b.weight) + .map((component) => + isResource ? `component-${component.key}` : component.key, + ); + } } - } - - initDragula() { - const options = this.options; - if (this.dragula) { - this.dragula.destroy(); + updateDragAndDrop() { + if (this.dragDropEnabled) { + this.initDragula(); + } + if (this.refs.form) { + return this.webform.attach(this.refs.form); + } } - const containersArray = Array.prototype.slice.call(this.refs['sidebar-container']).filter(item => { - return item.id !== 'group-container-resource'; - }); + initDragula() { + const options = this.options; - if (!dragula) { - return; - } + if (this.dragula) { + this.dragula.destroy(); + } - this.dragula = dragula(containersArray, { - moves(el) { - let moves = true; + const containersArray = Array.prototype.slice + .call(this.refs['sidebar-container']) + .filter((item) => { + return item.id !== 'group-container-resource'; + }); - const list = Array.from(el.classList).filter(item => item.indexOf('formio-component-') === 0); - list.forEach(item => { - const key = item.slice('formio-component-'.length); - if (options.disabled && options.disabled.includes(key)) { - moves = false; - } - }); + if (!dragula) { + return; + } - if (el.classList.contains('no-drag')) { - moves = false; - } - return moves; - }, - copy(el) { - return el.classList.contains('drag-copy'); - }, - accepts(el, target) { - return !el.contains(target) && !target.classList.contains('no-drop'); - } - }).on('drop', (element, target, source, sibling) => this.onDrop(element, target, source, sibling)); - } - - detach() { - if (this.dragula) { - this.dragula.destroy(); - } - this.dragula = null; - if (this.sideBarScroll && Templates.current.clearBuilderSidebarScroll) { - Templates.current.clearBuilderSidebarScroll.call(this, this); + this.dragula = dragula(containersArray, { + moves(el) { + let moves = true; + + const list = Array.from(el.classList).filter( + (item) => item.indexOf('formio-component-') === 0, + ); + list.forEach((item) => { + const key = item.slice('formio-component-'.length); + if (options.disabled && options.disabled.includes(key)) { + moves = false; + } + }); + + if (el.classList.contains('no-drag')) { + moves = false; + } + return moves; + }, + copy(el) { + return el.classList.contains('drag-copy'); + }, + accepts(el, target) { + return ( + !el.contains(target) && + !target.classList.contains('no-drop') + ); + }, + }).on('drop', (element, target, source, sibling) => + this.onDrop(element, target, source, sibling), + ); } - super.detach(); - } - - getComponentInfo(key, group) { - let info; - // Need to check in first order as resource component key can be the same as from webform default components - if (group && group.slice(0, group.indexOf('-')) === 'resource') { - // This is an existing resource field. - const resourceGroups = this.groups.resource.subgroups; - const resourceGroup = _.find(resourceGroups, { key: group }); - if (resourceGroup && Object.prototype.hasOwnProperty.call(resourceGroup.components, `component-${key}`)) { - info = fastCloneDeep(resourceGroup.components[`component-${key}`].schema); - } - } - // This is a new component - else if (Object.prototype.hasOwnProperty.call(this.schemas, key)) { - info = fastCloneDeep(this.schemas[key]); - } - else if (Object.prototype.hasOwnProperty.call(this.groups, group)) { - const groupComponents = this.groups[group].components; - if (Object.prototype.hasOwnProperty.call(groupComponents, key)) { - info = fastCloneDeep(groupComponents[key].schema); - } - } - else if (group === 'searchFields') {//Search components go into this group - const resourceGroups = this.groups.resource.subgroups; - for (let ix = 0; ix < resourceGroups.length; ix++) { - const resourceGroup = resourceGroups[ix]; - if (Object.prototype.hasOwnProperty.call(resourceGroup.components, `component-${key}`)) { - info = fastCloneDeep(resourceGroup.components[`component-${key}`].schema); - break; - } - } - } + detach() { + if (this.dragula) { + this.dragula.destroy(); + } + this.dragula = null; + if (this.sideBarScroll && Templates.current.clearBuilderSidebarScroll) { + Templates.current.clearBuilderSidebarScroll.call(this, this); + } - if (info) { - //if this is a custom component that was already assigned a key, don't stomp on it - if (!Object.prototype.hasOwnProperty.call(Components.components, info.type) && info.key) { - return info; - } - info.key = this.generateKey(info); - } + super.detach(); + } + + getComponentInfo(key, group) { + let info; + // Need to check in first order as resource component key can be the same as from webform default components + if (group && group.slice(0, group.indexOf('-')) === 'resource') { + // This is an existing resource field. + const resourceGroups = this.groups.resource.subgroups; + const resourceGroup = _.find(resourceGroups, { key: group }); + if ( + resourceGroup && + Object.prototype.hasOwnProperty.call( + resourceGroup.components, + `component-${key}`, + ) + ) { + info = fastCloneDeep( + resourceGroup.components[`component-${key}`].schema, + ); + } + } + // This is a new component + else if (Object.prototype.hasOwnProperty.call(this.schemas, key)) { + info = fastCloneDeep(this.schemas[key]); + } else if (Object.prototype.hasOwnProperty.call(this.groups, group)) { + const groupComponents = this.groups[group].components; + if (Object.prototype.hasOwnProperty.call(groupComponents, key)) { + info = fastCloneDeep(groupComponents[key].schema); + } + } else if (group === 'searchFields') { + //Search components go into this group + const resourceGroups = this.groups.resource.subgroups; + for (let ix = 0; ix < resourceGroups.length; ix++) { + const resourceGroup = resourceGroups[ix]; + if ( + Object.prototype.hasOwnProperty.call( + resourceGroup.components, + `component-${key}`, + ) + ) { + info = fastCloneDeep( + resourceGroup.components[`component-${key}`].schema, + ); + break; + } + } + } - return info; - } - - getComponentsPath(component, parent) { - // Get path to the component in the parent component. - let path = 'components'; - let columnIndex = 0; - let tableRowIndex = 0; - let tableColumnIndex = 0; - let tabIndex = 0; - switch (parent.type) { - case 'table': - tableRowIndex = _.findIndex(parent.rows, row => row.some(column => column.components.some(comp => comp.key === component.key))); - tableColumnIndex = _.findIndex(parent.rows[tableRowIndex], (column => column.components.some(comp => comp.key === component.key))); - path = `rows[${tableRowIndex}][${tableColumnIndex}].components`; - break; - case 'columns': - columnIndex = _.findIndex(parent.columns, column => column.components.some(comp => comp.key === component.key)); - path = `columns[${columnIndex}].components`; - break; - case 'tabs': - tabIndex = _.findIndex(parent.components, tab => tab.components.some(comp => comp.key === component.key)); - path = `components[${tabIndex}].components`; - break; - } - return path; - } + if (info) { + //if this is a custom component that was already assigned a key, don't stomp on it + if ( + !Object.prototype.hasOwnProperty.call( + Components.components, + info.type, + ) && + info.key + ) { + return info; + } + info.key = this.generateKey(info); + } - /* eslint-disable max-statements */ - onDrop(element, target, source, sibling) { - if (!target) { - return; + return info; } - // If you try to drop within itself. - if (element.contains(target)) { - return; + getComponentsPath(component, parent) { + // Get path to the component in the parent component. + let path = 'components'; + let columnIndex = 0; + let tableRowIndex = 0; + let tableColumnIndex = 0; + let tabIndex = 0; + switch (parent.type) { + case 'table': + tableRowIndex = _.findIndex(parent.rows, (row) => + row.some((column) => + column.components.some( + (comp) => comp.key === component.key, + ), + ), + ); + tableColumnIndex = _.findIndex( + parent.rows[tableRowIndex], + (column) => + column.components.some( + (comp) => comp.key === component.key, + ), + ); + path = `rows[${tableRowIndex}][${tableColumnIndex}].components`; + break; + case 'columns': + columnIndex = _.findIndex(parent.columns, (column) => + column.components.some( + (comp) => comp.key === component.key, + ), + ); + path = `columns[${columnIndex}].components`; + break; + case 'tabs': + tabIndex = _.findIndex(parent.components, (tab) => + tab.components.some((comp) => comp.key === component.key), + ); + path = `components[${tabIndex}].components`; + break; + } + return path; } - const key = element.getAttribute('data-key'); - const type = element.getAttribute('data-type'); - const group = element.getAttribute('data-group'); - let info, isNew, path, index; - - if (key && group) { - // This is a new component. - info = this.getComponentInfo(key, group); - if (!info && type) { - info = this.getComponentInfo(type, group); - } - isNew = true; - } - else if (source.formioContainer) { - index = _.findIndex(source.formioContainer, { key: element.formioComponent.component.key }); - if (index !== -1) { - // Grab and remove the component from the source container. - info = source.formioContainer.splice( - _.findIndex(source.formioContainer, { key: element.formioComponent.component.key }), 1 - ); + /* eslint-disable max-statements */ + onDrop(element, target, source, sibling) { + if (!target) { + return; + } - // Since splice returns an array of one object, we need to destructure it. - info = info[0]; - } - } + // If you try to drop within itself. + if (element.contains(target)) { + return; + } - // If we haven't found the component, stop. - if (!info) { - return; - } + const key = element.getAttribute('data-key'); + const type = element.getAttribute('data-type'); + const group = element.getAttribute('data-group'); + let info, isNew, path, index; - // Show an error if siblings are disabled for a component and such a component already exists. - const compKey = (group === 'resource') ? `component-${key}` : key; - const draggableComponent = this.groups[group]?.components[compKey] || {}; - - if (draggableComponent.disableSiblings) { - let isCompAlreadyExists = false; - eachComponent(this.webform.components, (component) => { - if (component.type === draggableComponent.schema.type) { - isCompAlreadyExists = true; - return; - } - }, true); - if (isCompAlreadyExists) { - this.webform.redraw(); - this.webform.setAlert('danger', `You cannot add more than one ${draggableComponent.key} component to one page.`); - return; - } - } + if (key && group) { + // This is a new component. + info = this.getComponentInfo(key, group); + if (!info && type) { + info = this.getComponentInfo(type, group); + } + isNew = true; + } else if (source.formioContainer) { + index = _.findIndex(source.formioContainer, { + key: element.formioComponent.component.key, + }); + if (index !== -1) { + // Grab and remove the component from the source container. + info = source.formioContainer.splice( + _.findIndex(source.formioContainer, { + key: element.formioComponent.component.key, + }), + 1, + ); + + // Since splice returns an array of one object, we need to destructure it. + info = info[0]; + } + } - if (target !== source) { - // Ensure the key remains unique in its new container. - BuilderUtils.uniquify(this.findNamespaceRoot(target.formioComponent), info); - } + // If we haven't found the component, stop. + if (!info) { + return; + } - const parent = target.formioComponent; + // Show an error if siblings are disabled for a component and such a component already exists. + const compKey = group === 'resource' ? `component-${key}` : key; + const draggableComponent = + this.groups[group]?.components[compKey] || {}; + + if (draggableComponent.disableSiblings) { + let isCompAlreadyExists = false; + eachComponent( + this.webform.components, + (component) => { + if (component.type === draggableComponent.schema.type) { + isCompAlreadyExists = true; + return; + } + }, + true, + ); + if (isCompAlreadyExists) { + this.webform.redraw(); + this.webform.setAlert( + 'danger', + `You cannot add more than one ${draggableComponent.key} component to one page.`, + ); + return; + } + } - // Insert in the new container. - if (target.formioContainer) { - if (sibling) { - if (!sibling.getAttribute('data-noattach')) { - index = _.findIndex(target.formioContainer, { key: _.get(sibling, 'formioComponent.component.key') }); - index = (index === -1) ? 0 : index; + if (target !== source) { + // Ensure the key remains unique in its new container. + BuilderUtils.uniquify( + this.findNamespaceRoot(target.formioComponent), + info, + ); } - else { - index = sibling.getAttribute('data-position'); + + const parent = target.formioComponent; + + // Insert in the new container. + if (target.formioContainer) { + if (sibling) { + if (!sibling.getAttribute('data-noattach')) { + index = _.findIndex(target.formioContainer, { + key: _.get(sibling, 'formioComponent.component.key'), + }); + index = index === -1 ? 0 : index; + } else { + index = sibling.getAttribute('data-position'); + } + if (index !== -1) { + target.formioContainer.splice(index, 0, info); + } + } else { + target.formioContainer.push(info); + } + path = this.getComponentsPath(info, parent.component); + index = _.findIndex(_.get(parent.schema, path), { key: info.key }); + if (index === -1) { + index = 0; + } } - if (index !== -1) { - target.formioContainer.splice(index, 0, info); - } - } - else { - target.formioContainer.push(info); - } - path = this.getComponentsPath(info, parent.component); - index = _.findIndex(_.get(parent.schema, path), { key: info.key }); - if (index === -1) { - index = 0; - } - } - if (parent && parent.addChildComponent) { - parent.addChildComponent(info, element, target, source, sibling); - } + if (parent && parent.addChildComponent) { + parent.addChildComponent(info, element, target, source, sibling); + } - const componentInDataGrid = parent.type === 'datagrid'; + const componentInDataGrid = parent.type === 'datagrid'; - if (isNew && !this.options.noNewEdit && !info.noNewEdit) { - this.editComponent(info, target, isNew, null, null, { inDataGrid: componentInDataGrid }); - } + if (isNew && !this.options.noNewEdit && !info.noNewEdit) { + this.editComponent(info, target, isNew, null, null, { + inDataGrid: componentInDataGrid, + }); + } - // Only rebuild the parts needing to be rebuilt. - let rebuild; - if (target !== source) { - if (source.formioContainer && source.contains(target)) { - rebuild = source.formioComponent.rebuild(); - } - else if (target.contains(source)) { - rebuild = target.formioComponent.rebuild(); - } - else { - if (source.formioContainer) { - rebuild = source.formioComponent.rebuild(); - } - rebuild = target.formioComponent.rebuild(); - } - } - else { - // If they are the same, only rebuild one. - rebuild = target.formioComponent.rebuild(); - } + // Only rebuild the parts needing to be rebuilt. + let rebuild; + if (target !== source) { + if (source.formioContainer && source.contains(target)) { + rebuild = source.formioComponent.rebuild(); + } else if (target.contains(source)) { + rebuild = target.formioComponent.rebuild(); + } else { + if (source.formioContainer) { + rebuild = source.formioComponent.rebuild(); + } + rebuild = target.formioComponent.rebuild(); + } + } else { + // If they are the same, only rebuild one. + rebuild = target.formioComponent.rebuild(); + } - if (!rebuild) { - rebuild = Promise.resolve(); - } + if (!rebuild) { + rebuild = Promise.resolve(); + } - return rebuild.then(() => { - this.emit('addComponent', info, parent, path, index, isNew && !this.options.noNewEdit && !info.noNewEdit); - if (!isNew || this.options.noNewEdit || info.noNewEdit) { - this.emit('change', this.form); - } - }); - } - - setForm(form) { - if (!form.components) { - form.components = []; + return rebuild.then(() => { + this.emit( + 'addComponent', + info, + parent, + path, + index, + isNew && !this.options.noNewEdit && !info.noNewEdit, + ); + if (!isNew || this.options.noNewEdit || info.noNewEdit) { + this.emit('change', this.form); + } + }); } - if (form && form.properties) { - this.options.properties = form.properties; - } + setForm(form) { + if (!form.components) { + form.components = []; + } - this.keyboardActionsEnabled = _.get(this.options, 'keyboardBuilder', false) || this.options.properties?.keyboardBuilder; - - const isShowSubmitButton = !this.options.noDefaultSubmitButton - && (!form.components.length || !form.components.find(comp => comp.key === 'submit')); - - // Ensure there is at least a submit button. - if (isShowSubmitButton) { - form.components.push({ - type: 'button', - label: 'Submit', - key: 'submit', - size: 'md', - block: false, - action: 'submit', - disableOnInvalid: true, - theme: 'primary' - }); - } + if (form && form.properties) { + this.options.properties = form.properties; + } - if (this.webform) { - const shouldRebuild = !this.webform.form.components || - (form.components.length !== this.webform.form.components.length); - return this.webform.setForm(form, { keepAsReference: true }).then(() => { - if (this.refs.form) { - this.builderHeight = this.refs.form.offsetHeight; + this.keyboardActionsEnabled = + _.get(this.options, 'keyboardBuilder', false) || + this.options.properties?.keyboardBuilder; + + const isShowSubmitButton = + !this.options.noDefaultSubmitButton && + (!form.components.length || + !form.components.find((comp) => comp.key === 'submit')); + + // Ensure there is at least a submit button. + if (isShowSubmitButton) { + form.components.push({ + type: 'button', + label: 'Submit', + key: 'submit', + size: 'md', + block: false, + action: 'submit', + disableOnInvalid: true, + theme: 'primary', + }); } - if (!shouldRebuild) { - return this.form; + + if (this.webform) { + const shouldRebuild = + !this.webform.form.components || + form.components.length !== this.webform.form.components.length; + return this.webform + .setForm(form, { keepAsReference: true }) + .then(() => { + if (this.refs.form) { + this.builderHeight = this.refs.form.offsetHeight; + } + if (!shouldRebuild) { + return this.form; + } + return this.rebuild().then(() => this.form); + }); } - return this.rebuild().then(() => this.form); - }); + return Promise.resolve(form); } - return Promise.resolve(form); - } - - populateRecaptchaSettings(form) { - //populate isEnabled for recaptcha form settings - let isRecaptchaEnabled = false; - if (this.form.components) { - eachComponent(form.components, component => { - if (isRecaptchaEnabled) { - return; - } - if (component.type === 'recaptcha') { - isRecaptchaEnabled = true; - return false; - } - }); - if (isRecaptchaEnabled) { - _.set(form, 'settings.recaptcha.isEnabled', true); - } - else if (_.get(form, 'settings.recaptcha.isEnabled')) { - _.set(form, 'settings.recaptcha.isEnabled', false); - } - } - } - removeComponent(component, parent, original, componentInstance) { - if (!parent) { - return; - } - let remove = true; - const removingComponentsGroup = !component.skipRemoveConfirm && - ( - (Array.isArray(component.components) && component.components.length) || - (Array.isArray(component.rows) && component.rows.length) || - (Array.isArray(component.columns) && component.columns.length) - ); - - if (this.options.alwaysConfirmComponentRemoval || removingComponentsGroup) { - const message = removingComponentsGroup ? 'Removing this component will also remove all of its children. Are you sure you want to do this?' - : 'Are you sure you want to remove this component?'; - remove = window.confirm(this.t(message)); - } - if (!original) { - original = parent.formioContainer.find((comp) => comp.id === component.id); - } - const index = parent.formioContainer ? parent.formioContainer.indexOf(original) : 0; - if (remove && index !== -1) { - const path = this.getComponentsPath(component, parent.formioComponent.component); - if (parent.formioContainer) { - parent.formioContainer.splice(index, 1); - } - else if (parent.formioComponent && parent.formioComponent.removeChildComponent) { - parent.formioComponent.removeChildComponent(component); - } - if (component.input && componentInstance && componentInstance.parent) { - _.unset(componentInstance._data, componentInstance.key); - } - const rebuild = parent.formioComponent.rebuild() || Promise.resolve(); - rebuild.then(() => { - this.emit('removeComponent', component, parent.formioComponent.schema, path, index); - this.emit('change', this.form); - }); + populateRecaptchaSettings(form) { + //populate isEnabled for recaptcha form settings + let isRecaptchaEnabled = false; + if (this.form.components) { + eachComponent(form.components, (component) => { + if (isRecaptchaEnabled) { + return; + } + if (component.type === 'recaptcha') { + isRecaptchaEnabled = true; + return false; + } + }); + if (isRecaptchaEnabled) { + _.set(form, 'settings.recaptcha.isEnabled', true); + } else if (_.get(form, 'settings.recaptcha.isEnabled')) { + _.set(form, 'settings.recaptcha.isEnabled', false); + } + } } - return remove; - } - replaceDoubleQuotes(data, fieldsToRemoveDoubleQuotes = []) { - if (data) { - fieldsToRemoveDoubleQuotes.forEach((key) => { - if (data[key]) { - data[key] = data[key].replace(/"/g, "'"); + removeComponent(component, parent, original, componentInstance) { + if (!parent) { + return; + } + let remove = true; + const removingComponentsGroup = + !component.skipRemoveConfirm && + ((Array.isArray(component.components) && + component.components.length) || + (Array.isArray(component.rows) && component.rows.length) || + (Array.isArray(component.columns) && component.columns.length)); + + if ( + this.options.alwaysConfirmComponentRemoval || + removingComponentsGroup + ) { + const message = removingComponentsGroup + ? 'Removing this component will also remove all of its children. Are you sure you want to do this?' + : 'Are you sure you want to remove this component?'; + remove = window.confirm(this.t(message)); + } + if (!original) { + original = parent.formioContainer.find( + (comp) => comp.id === component.id, + ); + } + const index = parent.formioContainer + ? parent.formioContainer.indexOf(original) + : 0; + if (remove && index !== -1) { + const path = this.getComponentsPath( + component, + parent.formioComponent.component, + ); + if (parent.formioContainer) { + parent.formioContainer.splice(index, 1); + } else if ( + parent.formioComponent && + parent.formioComponent.removeChildComponent + ) { + parent.formioComponent.removeChildComponent(component); + } + if ( + component.input && + componentInstance && + componentInstance.parent + ) { + _.unset(componentInstance._data, componentInstance.key); + } + const rebuild = + parent.formioComponent.rebuild() || Promise.resolve(); + rebuild.then(() => { + this.emit( + 'removeComponent', + component, + parent.formioComponent.schema, + path, + index, + ); + this.emit('change', this.form); + }); } - }); - return data; + return remove; } - } - - updateComponent(component, changed) { - const sanitizeConfig = _.get(this.webform, 'form.settings.sanitizeConfig') || _.get(this.webform, 'form.globalSettings.sanitizeConfig'); - // Update the preview. - if (this.preview) { - this.preview.form = { - components: [_.omit({ ...component }, [ - 'hidden', - 'conditional', - 'calculateValue', - 'logic', - 'autofocus', - 'customConditional', - ])], - config: this.options.formConfig || {}, - sanitizeConfig, - }; - - const fieldsToRemoveDoubleQuotes = ['label', 'tooltip']; - - this.preview.form.components.forEach(component => this.replaceDoubleQuotes(component, fieldsToRemoveDoubleQuotes)); - - const previewElement = this.componentEdit.querySelector('[ref="preview"]'); - if (previewElement) { - this.setContent(previewElement, this.preview.render(), null, sanitizeConfig); - this.preview.attach(previewElement); - } + + replaceDoubleQuotes(data, fieldsToRemoveDoubleQuotes = []) { + if (data) { + fieldsToRemoveDoubleQuotes.forEach((key) => { + if (data[key]) { + data[key] = data[key].replace(/"/g, "'"); + } + }); + return data; + } } - // Change the "default value" field to be reflective of this component. - const defaultValueComponent = getComponent(this.editForm.components, 'defaultValue', true); - if (defaultValueComponent && component.type !== 'hidden') { - const defaultChanged = changed && ( - (changed.component && changed.component.key === 'defaultValue') - || (changed.instance && defaultValueComponent.hasComponent && defaultValueComponent.hasComponent(changed.instance)) - ); - - if (!defaultChanged) { - _.assign(defaultValueComponent.component, _.omit({ ...component }, [ - 'key', - 'label', - 'labelPosition', - 'labelMargin', - 'labelWidth', - 'placeholder', - 'tooltip', - 'hidden', - 'autofocus', - 'validate', - 'disabled', - 'defaultValue', - 'customDefaultValue', - 'calculateValue', - 'conditional', - 'customConditional', - 'id' - ])); - const parentComponent = defaultValueComponent.parent; - let tabIndex = -1; - let index = -1; - parentComponent.tabs.some((tab, tIndex) => { - tab.some((comp, compIndex) => { - if (comp.id === defaultValueComponent.id) { - tabIndex = tIndex; - index = compIndex; - return true; - } - return false; - }); - }); + updateComponent(component, changed) { + const sanitizeConfig = + _.get(this.webform, 'form.settings.sanitizeConfig') || + _.get(this.webform, 'form.globalSettings.sanitizeConfig'); + // Update the preview. + if (this.preview) { + this.preview.form = { + components: [ + _.omit({ ...component }, [ + 'hidden', + 'conditional', + 'calculateValue', + 'logic', + 'autofocus', + 'customConditional', + ]), + ], + config: this.options.formConfig || {}, + sanitizeConfig, + }; - if (tabIndex !== -1 && index !== -1 && changed && !_.isNil(changed.value)) { - const sibling = parentComponent.tabs[tabIndex][index + 1]; - parentComponent.removeComponent(defaultValueComponent); - const newComp = parentComponent.addComponent(defaultValueComponent.component, defaultValueComponent.data, sibling); - _.pull(newComp.validators, 'required'); - parentComponent.tabs[tabIndex].splice(index, 1, newComp); - newComp.checkValidity = () => true; - newComp.build(defaultValueComponent.element); + const fieldsToRemoveDoubleQuotes = ['label', 'tooltip']; + + this.preview.form.components.forEach((component) => + this.replaceDoubleQuotes(component, fieldsToRemoveDoubleQuotes), + ); + + const previewElement = + this.componentEdit.querySelector('[ref="preview"]'); + if (previewElement) { + this.setContent( + previewElement, + this.preview.render(), + null, + sanitizeConfig, + ); + this.preview.attach(previewElement); + } } - } - else { - let dataPath = changed.instance._data.key; - const path = getArrayFromComponentPath(changed.instance.path); - path.shift(); + // Change the "default value" field to be reflective of this component. + const defaultValueComponent = getComponent( + this.editForm.components, + 'defaultValue', + true, + ); + if (defaultValueComponent && component.type !== 'hidden') { + const defaultChanged = + changed && + ((changed.component && + changed.component.key === 'defaultValue') || + (changed.instance && + defaultValueComponent.hasComponent && + defaultValueComponent.hasComponent(changed.instance))); + + if (!defaultChanged) { + _.assign( + defaultValueComponent.component, + _.omit({ ...component }, [ + 'key', + 'label', + 'labelPosition', + 'labelMargin', + 'labelWidth', + 'placeholder', + 'tooltip', + 'hidden', + 'autofocus', + 'validate', + 'disabled', + 'defaultValue', + 'customDefaultValue', + 'calculateValue', + 'conditional', + 'customConditional', + 'id', + ]), + ); + const parentComponent = defaultValueComponent.parent; + let tabIndex = -1; + let index = -1; + parentComponent.tabs.some((tab, tIndex) => { + tab.some((comp, compIndex) => { + if (comp.id === defaultValueComponent.id) { + tabIndex = tIndex; + index = compIndex; + return true; + } + return false; + }); + }); + + if ( + tabIndex !== -1 && + index !== -1 && + changed && + !_.isNil(changed.value) + ) { + const sibling = parentComponent.tabs[tabIndex][index + 1]; + parentComponent.removeComponent(defaultValueComponent); + const newComp = parentComponent.addComponent( + defaultValueComponent.component, + defaultValueComponent.data, + sibling, + ); + _.pull(newComp.validators, 'required'); + parentComponent.tabs[tabIndex].splice(index, 1, newComp); + newComp.checkValidity = () => true; + newComp.build(defaultValueComponent.element); + } + } else { + let dataPath = changed.instance._data.key; + + const path = getArrayFromComponentPath(changed.instance.path); + path.shift(); - if (path.length) { - path.unshift(component.key); - dataPath = getStringFromComponentPath(path); + if (path.length) { + path.unshift(component.key); + dataPath = getStringFromComponentPath(path); + } + + _.set(this.preview._data, dataPath, changed.value); + _.set(this.webform._data, dataPath, changed.value); + } } - _.set(this.preview._data, dataPath, changed.value); - _.set(this.webform._data, dataPath, changed.value); - } + // Called when we update a component. + this.emit('updateComponent', component); } - // Called when we update a component. - this.emit('updateComponent', component); - } - - findRepeatablePaths() { - const repeatablePaths = []; - const keys = new Map(); - - eachComponent(this.form.components, (comp, path) => { - if (!comp.key) { - return; - } - - if (keys.has(comp.key)) { - if (keys.get(comp.key).includes(path)) { - repeatablePaths.push(path); - } - else { - keys.set(comp.key, [...keys.get(comp.key), path]); - } - } - else { - keys.set(comp.key, [path]); - } - }, true); - - return repeatablePaths; - } - - highlightInvalidComponents() { - const repeatablePaths = this.findRepeatablePaths(); - let hasInvalidComponents = false; - - this.webform.everyComponent((comp) => { - const path = comp.path; - if (repeatablePaths.includes(path)) { - comp.setCustomValidity(`API Key is not unique: ${comp.key}`); - hasInvalidComponents = true; - } - else if (comp.error?.message?.startsWith('API Key is not unique')) { - comp.setCustomValidity(''); - } - }); - - this.emit('builderFormValidityChange', hasInvalidComponents); - } - - /** - * Called when a new component is saved. - * - * @param parent - * @param component - * @return {boolean} - */ - saveComponent(component, parent, isNew, original) { - this.editForm.detach(); - const parentContainer = parent ? parent.formioContainer : this.container; - const parentComponent = parent ? parent.formioComponent : this; - this.dialog.close(); - const path = parentContainer ? this.getComponentsPath(component, parentComponent.component) : ''; - if (!original) { - original = parent.formioContainer.find((comp) => comp.id === component.id); - } - const index = parentContainer ? parentContainer.indexOf(original) : 0; - if (index !== -1) { - let submissionData = this.editForm.submission.data; - submissionData = submissionData.componentJson || submissionData; - const fieldsToRemoveDoubleQuotes = ['label', 'tooltip']; - - this.replaceDoubleQuotes(submissionData, fieldsToRemoveDoubleQuotes); - - this.hook('beforeSaveComponentSettings', submissionData); - - let comp = null; - parentComponent.getComponents().forEach((component) => { - if (component.component.key === original.key) { - comp = component; - } - }); - const originalComp = comp.component; - const originalComponentSchema = comp.schema; - - const isParentSaveChildMethod = this.isParentSaveChildMethod(parent.formioComponent); - - if (parentContainer && !isParentSaveChildMethod) { - parentContainer[index] = submissionData; - if (comp) { - comp.component = submissionData; - } - } - else if (isParentSaveChildMethod) { - parent.formioComponent.saveChildComponent(submissionData); - } - - const rebuild = parentComponent.rebuild() || Promise.resolve(); - return rebuild.then(() => { - const schema = parentContainer ? parentContainer[index] : (comp ? comp.schema : []); - this.emitSaveComponentEvent( - schema, - originalComp, - parentComponent.schema, - path, - index, - isNew, - originalComponentSchema + findRepeatablePaths() { + const repeatablePaths = []; + const keys = new Map(); + + eachComponent( + this.form.components, + (comp, path) => { + if (!comp.key) { + return; + } + + if (keys.has(comp.key)) { + if (keys.get(comp.key).includes(path)) { + repeatablePaths.push(path); + } else { + keys.set(comp.key, [...keys.get(comp.key), path]); + } + } else { + keys.set(comp.key, [path]); + } + }, + true, ); - this.emit('change', this.form); - this.highlightInvalidComponents(); - if (this.isComponentCreated) { - const component = parent.formioComponent.components[0]; - this.moveComponent(component); - this.isComponentCreated = false; - } - }); + return repeatablePaths; } - this.highlightInvalidComponents(); - return Promise.resolve(); - } - - emitSaveComponentEvent(schema, originalComp, parentComponentSchema, path, index, isNew, originalComponentSchema) { - this.emit('saveComponent', - schema, - originalComp, - parentComponentSchema, - path, - index, - isNew, - originalComponentSchema - ); - } - - attachEditComponentControls(component, parent, isNew, original, ComponentClass) { - const cancelButtons = this.componentEdit.querySelectorAll('[ref="cancelButton"]'); - cancelButtons.forEach((cancelButton) => { - this.editForm.addEventListener(cancelButton, 'click', (event) => { - event.preventDefault(); - this.editForm.detach(); - this.emit('cancelComponent', component); - this.dialog.close(); - this.highlightInvalidComponents(); - }); - }); - - const removeButtons = this.componentEdit.querySelectorAll('[ref="removeButton"]'); - removeButtons.forEach((removeButton) => { - this.editForm.addEventListener(removeButton, 'click', (event) => { - event.preventDefault(); - // Since we are already removing the component, don't trigger another remove. - this.saved = true; + highlightInvalidComponents() { + const repeatablePaths = this.findRepeatablePaths(); + let hasInvalidComponents = false; + + this.webform.everyComponent((comp) => { + const path = comp.path; + if (repeatablePaths.includes(path)) { + comp.setCustomValidity(`API Key is not unique: ${comp.key}`); + hasInvalidComponents = true; + } else if ( + comp.error?.message?.startsWith('API Key is not unique') + ) { + comp.setCustomValidity(''); + } + }); + + this.emit('builderFormValidityChange', hasInvalidComponents); + } + + /** + * Called when a new component is saved. + * + * @param parent + * @param component + * @return {boolean} + */ + saveComponent(component, parent, isNew, original) { this.editForm.detach(); - this.removeComponent(component, parent, original); + const parentContainer = parent + ? parent.formioContainer + : this.container; + const parentComponent = parent ? parent.formioComponent : this; this.dialog.close(); - this.highlightInvalidComponents(); - }); - }); - - const saveButtons = this.componentEdit.querySelectorAll('[ref="saveButton"]'); - saveButtons.forEach((saveButton) => { - this.editForm.addEventListener(saveButton, 'click', (event) => { - event.preventDefault(); - if (!this.editForm.checkValidity(this.editForm.data, true, this.editForm.data)) { - this.editForm.setPristine(false); - this.editForm.showErrors(); - return false; - } - this.saved = true; - this.saveComponent(component, parent, isNew, original); - }); - }); - - const previewButtons = this.componentEdit.querySelectorAll('[ref="previewButton"]'); - previewButtons.forEach((previewButton) => { - this.editForm.addEventListener(previewButton, 'click', (event) => { - event.preventDefault(); - this.showPreview = !this.showPreview; - this.editForm.detach(); - this.setContent(this.componentEdit, this.renderTemplate('builderEditForm', { - componentInfo: ComponentClass.builderInfo, - editForm: this.editForm.render(), - preview: this.preview ? this.preview.render() : false, - showPreview: this.showPreview, - helplinks: this.helplinks, - })); - this.editForm.attach(this.componentEdit.querySelector('[ref="editForm"]')); - this.attachEditComponentControls(component, parent, isNew, original, ComponentClass); - }); - }); - } - - editComponent(component, parent, isNew, isJsonEdit, original, flags = {}) { - if (!component.key) { - return; - } - this.saved = false; - const componentCopy = fastCloneDeep(component); - let ComponentClass = Components.components[componentCopy.type]; - const isCustom = ComponentClass === undefined; - isJsonEdit = isJsonEdit || isCustom; - ComponentClass = isCustom ? Components.components.unknown : ComponentClass; - // Make sure we only have one dialog open at a time. - if (this.dialog) { - this.dialog.close(); - this.highlightInvalidComponents(); - } + const path = parentContainer + ? this.getComponentsPath(component, parentComponent.component) + : ''; + if (!original) { + original = parent.formioContainer.find( + (comp) => comp.id === component.id, + ); + } + const index = parentContainer ? parentContainer.indexOf(original) : 0; + if (index !== -1) { + let submissionData = this.editForm.submission.data; + submissionData = submissionData.componentJson || submissionData; + const fieldsToRemoveDoubleQuotes = ['label', 'tooltip']; - // This is the render step. - const editFormOptions = _.clone(_.get(this, 'options.editForm', {})); - if (this.editForm) { - this.editForm.destroy(); - } + this.replaceDoubleQuotes( + submissionData, + fieldsToRemoveDoubleQuotes, + ); - // Allow editForm overrides per component. - const overrides = _.get(this.options, `editForm.${componentCopy.type}`, {}); - - // Pass along the form being edited. - editFormOptions.editForm = this.form; - editFormOptions.editComponent = component; - editFormOptions.flags = flags; - - this.hook('editComponentParentInstance', editFormOptions, parent); - - this.editForm = new Webform( - { - ..._.omit(this.options, ['hooks', 'builder', 'events', 'attachMode', 'skipInit']), - language: this.options.language, - ...editFormOptions, - evalContext: { - ...(editFormOptions?.evalContext || this.options?.evalContext || {}), - buildingForm: this.form, - }, - } - ); - - this.hook('editFormProperties', parent); - - this.editForm.form = (isJsonEdit && !isCustom) ? { - components: [ - { - type: 'textarea', - as: 'json', - editor: 'ace', - weight: 10, - input: true, - key: 'componentJson', - label: 'Component JSON', - tooltip: 'Edit the JSON for this component.' - }, - { - type: 'checkbox', - key: 'showFullSchema', - label: 'Full Schema' - } - ] - } : ComponentClass.editForm(_.cloneDeep(overrides)); - const instanceOptions = { - inFormBuilder: true, - }; + this.hook('beforeSaveComponentSettings', submissionData); - this.hook('instanceOptionsPreview', instanceOptions); + let comp = null; + parentComponent.getComponents().forEach((component) => { + if (component.component.key === original.key) { + comp = component; + } + }); + const originalComp = comp.component; + const originalComponentSchema = comp.schema; - const instance = new ComponentClass(componentCopy, instanceOptions); - const schema = this.hook('builderComponentSchema', component, instance); + const isParentSaveChildMethod = this.isParentSaveChildMethod( + parent.formioComponent, + ); - this.editForm.submission = isJsonEdit ? { - data: { - componentJson: schema, - showFullSchema: this.options.showFullJsonSchema - }, - } : { - data: instance.component, - }; + if (parentContainer && !isParentSaveChildMethod) { + parentContainer[index] = submissionData; + if (comp) { + comp.component = submissionData; + } + } else if (isParentSaveChildMethod) { + parent.formioComponent.saveChildComponent(submissionData); + } - if (this.preview) { - this.preview.destroy(); - } - if (!Object.prototype.hasOwnProperty.call(ComponentClass.builderInfo, 'preview') || ComponentClass.builderInfo.preview) { - this.preview = new Webform(_.omit({ ...this.options, preview: true }, [ - 'hooks', - 'builder', - 'events', - 'attachMode', - 'calculateValue' - ])); - - this.hook('previewFormSettitngs', schema, isJsonEdit); + const rebuild = parentComponent.rebuild() || Promise.resolve(); + return rebuild.then(() => { + const schema = parentContainer + ? parentContainer[index] + : comp + ? comp.schema + : []; + this.emitSaveComponentEvent( + schema, + originalComp, + parentComponent.schema, + path, + index, + isNew, + originalComponentSchema, + ); + this.emit('change', this.form); + this.highlightInvalidComponents(); + + if (this.isComponentCreated) { + const component = parent.formioComponent.components[0]; + this.moveComponent(component); + this.isComponentCreated = false; + } + }); + } + + this.highlightInvalidComponents(); + return Promise.resolve(); + } + + emitSaveComponentEvent( + schema, + originalComp, + parentComponentSchema, + path, + index, + isNew, + originalComponentSchema, + ) { + this.emit( + 'saveComponent', + schema, + originalComp, + parentComponentSchema, + path, + index, + isNew, + originalComponentSchema, + ); } - this.showPreview = ComponentClass.builderInfo.showPreview ?? true; + attachEditComponentControls( + component, + parent, + isNew, + original, + ComponentClass, + ) { + const cancelButtons = this.componentEdit.querySelectorAll( + '[ref="cancelButton"]', + ); + cancelButtons.forEach((cancelButton) => { + this.editForm.addEventListener(cancelButton, 'click', (event) => { + event.preventDefault(); + this.editForm.detach(); + this.emit('cancelComponent', component); + this.dialog.close(); + this.highlightInvalidComponents(); + }); + }); - this.componentEdit = this.ce('div', { 'class': 'component-edit-container' }); - this.setContent(this.componentEdit, this.renderTemplate('builderEditForm', { - componentInfo: ComponentClass.builderInfo, - editForm: this.editForm.render(), - preview: this.preview ? this.preview.render() : false, - showPreview: this.showPreview, - helplinks: this.helplinks - })); + const removeButtons = this.componentEdit.querySelectorAll( + '[ref="removeButton"]', + ); + removeButtons.forEach((removeButton) => { + this.editForm.addEventListener(removeButton, 'click', (event) => { + event.preventDefault(); + // Since we are already removing the component, don't trigger another remove. + this.saved = true; + this.editForm.detach(); + this.removeComponent(component, parent, original); + this.dialog.close(); + this.highlightInvalidComponents(); + }); + }); + + const saveButtons = + this.componentEdit.querySelectorAll('[ref="saveButton"]'); + saveButtons.forEach((saveButton) => { + this.editForm.addEventListener(saveButton, 'click', (event) => { + event.preventDefault(); + if ( + !this.editForm.checkValidity( + this.editForm.data, + true, + this.editForm.data, + ) + ) { + this.editForm.setPristine(false); + this.editForm.showErrors(); + return false; + } + this.saved = true; + this.saveComponent(component, parent, isNew, original); + }); + }); - this.dialog = this.createModal(this.componentEdit, _.get(this.options, 'dialogAttr', {})); + const previewButtons = this.componentEdit.querySelectorAll( + '[ref="previewButton"]', + ); + previewButtons.forEach((previewButton) => { + this.editForm.addEventListener(previewButton, 'click', (event) => { + event.preventDefault(); + this.showPreview = !this.showPreview; + this.editForm.detach(); + this.setContent( + this.componentEdit, + this.renderTemplate('builderEditForm', { + componentInfo: ComponentClass.builderInfo, + editForm: this.editForm.render(), + preview: this.preview ? this.preview.render() : false, + showPreview: this.showPreview, + helplinks: this.helplinks, + }), + ); + this.editForm.attach( + this.componentEdit.querySelector('[ref="editForm"]'), + ); + this.attachEditComponentControls( + component, + parent, + isNew, + original, + ComponentClass, + ); + }); + }); + } - // This is the attach step. - this.editForm.attach(this.componentEdit.querySelector('[ref="editForm"]')); + editComponent(component, parent, isNew, isJsonEdit, original, flags = {}) { + if (!component.key) { + return; + } + this.saved = false; + const componentCopy = fastCloneDeep(component); + let ComponentClass = Components.components[componentCopy.type]; + const isCustom = ComponentClass === undefined; + isJsonEdit = isJsonEdit || isCustom; + ComponentClass = isCustom + ? Components.components.unknown + : ComponentClass; + // Make sure we only have one dialog open at a time. + if (this.dialog) { + this.dialog.close(); + this.highlightInvalidComponents(); + } - this.hook('editFormWrapper'); + // This is the render step. + const editFormOptions = _.clone(_.get(this, 'options.editForm', {})); + if (this.editForm) { + this.editForm.destroy(); + } - this.updateComponent(componentCopy); + // Allow editForm overrides per component. + const overrides = _.get( + this.options, + `editForm.${componentCopy.type}`, + {}, + ); - this.editForm.on('change', (event) => { - if (event.changed) { - if (event.changed.component && event.changed.component.key === 'showFullSchema') { - const { value } = event.changed; - this.editForm.submission = { - data: { - componentJson: value ? instance.component : component, - showFullSchema: value + // Pass along the form being edited. + editFormOptions.editForm = this.form; + editFormOptions.editComponent = component; + editFormOptions.flags = flags; + + this.hook('editComponentParentInstance', editFormOptions, parent); + + this.editForm = new Webform({ + ..._.omit(this.options, [ + 'hooks', + 'builder', + 'events', + 'attachMode', + 'skipInit', + ]), + language: this.options.language, + ...editFormOptions, + evalContext: { + ...(editFormOptions?.evalContext || + this.options?.evalContext || + {}), + buildingForm: this.form, }, - }; - return; - } - // See if this is a manually modified key. Treat custom component keys as manually modified - if ((event.changed.component && (event.changed.component.key === 'key')) || isJsonEdit) { - componentCopy.keyModified = true; - } + }); + + this.hook('editFormProperties', parent); + + this.editForm.form = + isJsonEdit && !isCustom + ? { + components: [ + { + type: 'textarea', + as: 'json', + editor: 'ace', + weight: 10, + input: true, + key: 'componentJson', + label: 'Component JSON', + tooltip: 'Edit the JSON for this component.', + }, + { + type: 'checkbox', + key: 'showFullSchema', + label: 'Full Schema', + }, + ], + } + : ComponentClass.editForm(_.cloneDeep(overrides)); + const instanceOptions = { + inFormBuilder: true, + }; - let isComponentLabelChanged = false; - if (event.changed.instance) { - isComponentLabelChanged = ['label', 'title'].includes(event.changed.instance.path); + this.hook('instanceOptionsPreview', instanceOptions); + + const instance = new ComponentClass(componentCopy, instanceOptions); + const schema = this.hook('builderComponentSchema', component, instance); + + this.editForm.submission = isJsonEdit + ? { + data: { + componentJson: schema, + showFullSchema: this.options.showFullJsonSchema, + }, + } + : { + data: instance.component, + }; + + if (this.preview) { + this.preview.destroy(); } - else if (event.changed.component) { - isComponentLabelChanged = ['label', 'title'].includes(event.changed.component.key); + if ( + !Object.prototype.hasOwnProperty.call( + ComponentClass.builderInfo, + 'preview', + ) || + ComponentClass.builderInfo.preview + ) { + this.preview = new Webform( + _.omit({ ...this.options, preview: true }, [ + 'hooks', + 'builder', + 'events', + 'attachMode', + 'calculateValue', + ]), + ); + + this.hook('previewFormSettitngs', schema, isJsonEdit); } - if (isComponentLabelChanged) { - // Ensure this component has a key. - if (isNew) { - if (!event.data.keyModified) { - this.editForm.everyComponent(component => { - if (component.key === 'key' && component.parent.component.key === 'tabs') { - component.setValue(this.updateComponentKey(event.data)); - return false; + this.showPreview = ComponentClass.builderInfo.showPreview ?? true; + + this.componentEdit = this.ce('div', { + class: 'component-edit-container', + }); + this.setContent( + this.componentEdit, + this.renderTemplate('builderEditForm', { + componentInfo: ComponentClass.builderInfo, + editForm: this.editForm.render(), + preview: this.preview ? this.preview.render() : false, + showPreview: this.showPreview, + helplinks: this.helplinks, + }), + ); + + this.dialog = this.createModal( + this.componentEdit, + _.get(this.options, 'dialogAttr', {}), + ); + + // This is the attach step. + this.editForm.attach( + this.componentEdit.querySelector('[ref="editForm"]'), + ); + + this.hook('editFormWrapper'); + + this.updateComponent(componentCopy); + + this.editForm.on('change', (event) => { + if (event.changed) { + if ( + event.changed.component && + event.changed.component.key === 'showFullSchema' + ) { + const { value } = event.changed; + this.editForm.submission = { + data: { + componentJson: value + ? instance.component + : component, + showFullSchema: value, + }, + }; + return; + } + // See if this is a manually modified key. Treat custom component keys as manually modified + if ( + (event.changed.component && + event.changed.component.key === 'key') || + isJsonEdit + ) { + componentCopy.keyModified = true; + } + + let isComponentLabelChanged = false; + if (event.changed.instance) { + isComponentLabelChanged = ['label', 'title'].includes( + event.changed.instance.path, + ); + } else if (event.changed.component) { + isComponentLabelChanged = ['label', 'title'].includes( + event.changed.component.key, + ); + } + + if (isComponentLabelChanged) { + // Ensure this component has a key. + if (isNew) { + if (!event.data.keyModified) { + this.editForm.everyComponent((component) => { + if ( + component.key === 'key' && + component.parent.component.key === 'tabs' + ) { + component.setValue( + this.updateComponentKey(event.data), + ); + return false; + } + }); + } + + if (this.form) { + let formComponents = this.findNamespaceRoot( + parent.formioComponent, + ); + // excluding component which key uniqueness is to be checked to prevent the comparing of the same keys + formComponents = formComponents.filter( + (comp) => + editFormOptions.editComponent.id !== + comp.id, + ); + + // Set a unique key for this component. + BuilderUtils.uniquify(formComponents, event.data); + } + } } - }); - } - if (this.form) { - let formComponents = this.findNamespaceRoot(parent.formioComponent); - // excluding component which key uniqueness is to be checked to prevent the comparing of the same keys - formComponents = formComponents.filter(comp => editFormOptions.editComponent.id !== comp.id); + // If the edit form has any nested form inside, we get a partial data (nested form's data) in the + // event.data property + let editFormData; + if ( + event.changed.instance && + event.changed.instance.root && + event.changed.instance.root.id !== this.editForm.id + ) { + editFormData = this.editForm.data; + } - // Set a unique key for this component. - BuilderUtils.uniquify(formComponents, event.data); + // Update the component. + this.updateComponent( + event.data.componentJson || editFormData || event.data, + event.changed, + ); } - } - } + }); - // If the edit form has any nested form inside, we get a partial data (nested form's data) in the - // event.data property - let editFormData; - if (event.changed.instance && event.changed.instance.root && event.changed.instance.root.id !== this.editForm.id) { - editFormData = this.editForm.data; - } + this.attachEditComponentControls( + component, + parent, + isNew, + original, + ComponentClass, + ); - // Update the component. - this.updateComponent(event.data.componentJson || editFormData || event.data, event.changed); - } - }); + const dialogClose = () => { + this.editForm.destroy(true); + if (this.preview) { + this.preview.destroy(true); + this.preview = null; + } + if (isNew && !this.saved) { + this.removeComponent(component, parent, original); + this.highlightInvalidComponents(); + } + // Clean up. + this.removeEventListener(this.dialog, 'close', dialogClose); + this.dialog = null; + }; + this.addEventListener(this.dialog, 'close', dialogClose); - this.attachEditComponentControls(component, parent, isNew, original, ComponentClass); + // Called when we edit a component. + this.emit('editComponent', component); + } - const dialogClose = () => { - this.editForm.destroy(true); - if (this.preview) { - this.preview.destroy(true); - this.preview = null; - } - if (isNew && !this.saved) { - this.removeComponent(component, parent, original); - this.highlightInvalidComponents(); - } - // Clean up. - this.removeEventListener(this.dialog, 'close', dialogClose); - this.dialog = null; - }; - this.addEventListener(this.dialog, 'close', dialogClose); - - // Called when we edit a component. - this.emit('editComponent', component); - } - - updateComponentKey(data) { - return _.camelCase( - data.title || - data.label || - data.placeholder || - data.type - ).replace(/^[0-9]*/, ''); - } - - moveComponent(component) { - if (this.selectedComponent) { - const prevSelected = this.selectedComponent; - prevSelected.element?.classList.remove('builder-component-selected'); - this.removeEventListener(document, 'keydown'); + updateComponentKey(data) { + return _.camelCase( + data.title || data.label || data.placeholder || data.type, + ).replace(/^[0-9]*/, ''); } - component.element.focus(); - component.element.classList.add('builder-component-selected'); - this.selectedComponent = component; - this.addEventListener(document, 'keydown', this.moveHandler.bind(this)); - } + moveComponent(component) { + if (this.selectedComponent) { + const prevSelected = this.selectedComponent; + prevSelected.element?.classList.remove( + 'builder-component-selected', + ); + this.removeEventListener(document, 'keydown'); + } - moveHandler = (e) => { - if (e.keyCode === 38 || e.keyCode === 40 || e.keyCode === 13) { - e.stopPropagation(); - e.preventDefault(); + component.element.focus(); + component.element.classList.add('builder-component-selected'); + this.selectedComponent = component; + this.addEventListener(document, 'keydown', this.moveHandler.bind(this)); } - if (e.keyCode === 38) { - this.updateComponentPlacement(true); - } + moveHandler = (e) => { + if (e.keyCode === 38 || e.keyCode === 40 || e.keyCode === 13) { + e.stopPropagation(); + e.preventDefault(); + } - if (e.keyCode === 40) { - this.updateComponentPlacement(false); - } + if (e.keyCode === 38) { + this.updateComponentPlacement(true); + } - if (e.keyCode === 13) { - this.stopMoving(this.selectedComponent); - } - }; + if (e.keyCode === 40) { + this.updateComponentPlacement(false); + } - updateComponentPlacement(direction) { - const component = this.selectedComponent; - let index, info; - const step = direction ? -1 : 1; - if (component) { - const element = component.element; - const sibling = direction ? element.previousElementSibling : element.nextElementSibling; - const source = element.parentNode; + if (e.keyCode === 13) { + this.stopMoving(this.selectedComponent); + } + }; - const containerLength = source.formioContainer.length; + updateComponentPlacement(direction) { + const component = this.selectedComponent; + let index, info; + const step = direction ? -1 : 1; + if (component) { + const element = component.element; + const sibling = direction + ? element.previousElementSibling + : element.nextElementSibling; + const source = element.parentNode; + + const containerLength = source.formioContainer.length; + + if (containerLength && containerLength <= 1) { + return; + } - if (containerLength && containerLength <= 1) { - return; - } + if (source.formioContainer) { + index = _.findIndex(source.formioContainer, { + key: element.formioComponent.component.key, + }); + + if (index !== -1) { + info = source.formioContainer.splice( + _.findIndex(source.formioContainer, { + key: element.formioComponent.component.key, + }), + 1, + ); + info = info[0]; + source.removeChild(element); + } + } - if (source.formioContainer) { - index = _.findIndex(source.formioContainer, { key: element.formioComponent.component.key }); + const len = source.formioComponent.components.length; + index = index === -1 ? 0 : index + step; + + if (index === -1) { + source.formioContainer.push(info); + source.appendChild(element); + } else if (index === len) { + const key = source.formioContainer[0].key; + index = _.findIndex(source.formioComponent.components, { + key: key, + }); + const firstElement = + source.formioComponent.components[index].element; + source.formioContainer.splice(0, 0, info); + source.insertBefore(element, firstElement); + } else if (index !== -1) { + source.formioContainer.splice(index, 0, info); + direction + ? source.insertBefore(element, sibling) + : source.insertBefore(element, sibling.nextElementSibling); + } + element.focus(); + } + } - if (index !== -1) { - info = source.formioContainer.splice( - _.findIndex(source.formioContainer, { key: element.formioComponent.component.key }), 1 - ); - info = info[0]; - source.removeChild(element); - } - } - - const len = source.formioComponent.components.length; - index = (index === -1) ? 0 : index + step; - - if (index === -1) { - source.formioContainer.push(info); - source.appendChild(element); - } - else if (index === len) { - const key = source.formioContainer[0].key; - index = _.findIndex(source.formioComponent.components, { key: key }); - const firstElement = source.formioComponent.components[index].element; - source.formioContainer.splice(0, 0, info); - source.insertBefore(element, firstElement); - } - else if (index !== -1) { - source.formioContainer.splice(index, 0, info); - direction - ? source.insertBefore(element, sibling) - : source.insertBefore(element, sibling.nextElementSibling); - } - element.focus(); + stopMoving(comp) { + const parent = comp.element.parentNode; + this.removeEventListener(document, 'keydown'); + parent.formioComponent.rebuild(); + this.selectedComponent = null; } - } - stopMoving(comp) { - const parent = comp.element.parentNode; - this.removeEventListener(document, 'keydown'); - parent.formioComponent.rebuild(); - this.selectedComponent = null; - } + addNewComponent(element) { + const source = document.querySelector('.formio-builder-form'); + const key = element.getAttribute('data-key'); + const group = element.getAttribute('data-group'); - addNewComponent(element) { - const source = document.querySelector('.formio-builder-form'); - const key = element.getAttribute('data-key'); - const group = element.getAttribute('data-group'); + const isNew = true; + let info; - const isNew = true; - let info; + if (key && group) { + info = this.getComponentInfo(key, group); + } - if (key && group) { - info = this.getComponentInfo(key, group); - } + if (isNew && !this.options.noNewEdit && !info.noNewEdit) { + BuilderUtils.uniquify( + this.findNamespaceRoot(source.formioComponent), + info, + ); + this.editComponent(info, source, isNew, null, null); + } - if (isNew && !this.options.noNewEdit && !info.noNewEdit) { - BuilderUtils.uniquify(this.findNamespaceRoot(source.formioComponent), info); - this.editComponent(info, source, isNew, null, null); - } + const firstComponent = source.formioComponent.components[0]?.element; - const firstComponent = source.formioComponent.components[0]?.element; + if (firstComponent) { + source.formioContainer.splice(0, 0, info); + } else { + source.formioContainer.push(info); + } - if (firstComponent) { - source.formioContainer.splice(0, 0, info); - } - else { - source.formioContainer.push(info); + source.formioComponent.rebuild().then(() => { + this.isComponentCreated = true; + }); } - source.formioComponent.rebuild().then(() => { - this.isComponentCreated = true; - }); - } - - /** - * Creates copy of component schema and stores it under sessionStorage. - * @param {Component} component - * @return {*} - */ - copyComponent(component) { - if (!window.sessionStorage) { - return console.warn('Session storage is not supported in this browser.'); + /** + * Creates copy of component schema and stores it under sessionStorage. + * @param {Component} component + * @return {*} + */ + copyComponent(component) { + if (!window.sessionStorage) { + return console.warn( + 'Session storage is not supported in this browser.', + ); + } + this.addClass(this.refs.form, 'builder-paste-mode'); + window.sessionStorage.setItem( + 'formio.clipboard', + JSON.stringify(component.schema), + ); } - this.addClass(this.refs.form, 'builder-paste-mode'); - window.sessionStorage.setItem('formio.clipboard', JSON.stringify(component.schema)); - } - - /** - * Paste copied component after the current component. - * @param {Component} component - * @return {*} - */ - pasteComponent(component) { - if (!window.sessionStorage) { - return console.warn('Session storage is not supported in this browser.'); + + /** + * Paste copied component after the current component. + * @param {Component} component + * @return {*} + */ + pasteComponent(component) { + if (!window.sessionStorage) { + return console.warn( + 'Session storage is not supported in this browser.', + ); + } + this.removeClass(this.refs.form, 'builder-paste-mode'); + if (window.sessionStorage) { + const data = window.sessionStorage.getItem('formio.clipboard'); + if (data) { + const schema = JSON.parse(data); + const parent = this.getParentElement(component.element); + if (parent) { + BuilderUtils.uniquify( + this.findNamespaceRoot(parent.formioComponent), + schema, + ); + let path = ''; + let index = 0; + + const isParentSaveChildMethod = + this.isParentSaveChildMethod(parent.formioComponent); + + if (parent.formioContainer && !isParentSaveChildMethod) { + index = parent.formioContainer.indexOf( + component.component, + ); + path = this.getComponentsPath( + schema, + parent.formioComponent.component, + ); + parent.formioContainer.splice(index + 1, 0, schema); + } else if (isParentSaveChildMethod) { + parent.formioComponent.saveChildComponent( + schema, + false, + ); + } + parent.formioComponent.rebuild(); + + this.emitSaveComponentEvent( + schema, + schema, + parent.formioComponent.component, + path, + index + 1, + true, + schema, + ); + } + this.emit('change', this.form); + } + } } - this.removeClass(this.refs.form, 'builder-paste-mode'); - if (window.sessionStorage) { - const data = window.sessionStorage.getItem('formio.clipboard'); - if (data) { - const schema = JSON.parse(data); - const parent = this.getParentElement(component.element); - if (parent) { - BuilderUtils.uniquify(this.findNamespaceRoot(parent.formioComponent), schema); - let path = ''; - let index = 0; - - const isParentSaveChildMethod = this.isParentSaveChildMethod(parent.formioComponent); - - if (parent.formioContainer && !isParentSaveChildMethod) { - index = parent.formioContainer.indexOf(component.component); - path = this.getComponentsPath(schema, parent.formioComponent.component); - parent.formioContainer.splice(index + 1, 0, schema); - } - else if (isParentSaveChildMethod) { - parent.formioComponent.saveChildComponent(schema, false); - } - parent.formioComponent.rebuild(); - - this.emitSaveComponentEvent(schema, schema, parent.formioComponent.component, path, (index + 1), true, schema); - } - this.emit('change', this.form); - } + + isParentSaveChildMethod(parentComp) { + return !!(parentComp && parentComp.saveChildComponent); } - } - - isParentSaveChildMethod(parentComp) { - return !!(parentComp && parentComp.saveChildComponent); - } - - getParentElement(element) { - let container = element; - do { - container = container.parentNode; - } while (container && !container.formioComponent); - return container; - } - - addBuilderComponentInfo(component) { - if (!component || !component.group || !this.groups[component.group]) { - return; + + getParentElement(element) { + let container = element; + do { + container = container.parentNode; + } while (container && !container.formioComponent); + return container; } - component = _.clone(component); - const groupInfo = this.groups[component.group]; - if (!Object.prototype.hasOwnProperty.call(groupInfo.components, component.key)) { - groupInfo.components[component.key] = component; + addBuilderComponentInfo(component) { + if (!component || !component.group || !this.groups[component.group]) { + return; + } + + component = _.clone(component); + const groupInfo = this.groups[component.group]; + if ( + !Object.prototype.hasOwnProperty.call( + groupInfo.components, + component.key, + ) + ) { + groupInfo.components[component.key] = component; + } + return component; } - return component; - } - init() { - if (this.webform) { - this.webform.init(); + init() { + if (this.webform) { + this.webform.init(); + } + return super.init(); } - return super.init(); - } - clear() { - if (this.webform.initialized) { - this.webform.clear(); + clear() { + if (this.webform.initialized) { + this.webform.clear(); + } } - } - destroy(all = false) { - if (this.webform.initialized) { - this.webform.destroy(all); + destroy(all = false) { + if (this.webform.initialized) { + this.webform.destroy(all); + } + super.destroy(all); } - super.destroy(all); - } - - addBuilderGroup(name, group) { - if (!this.groups[name]) { - this.groups[name] = group; - this.groupOrder.push(name); - this.triggerRedraw(); + + addBuilderGroup(name, group) { + if (!this.groups[name]) { + this.groups[name] = group; + this.groupOrder.push(name); + this.triggerRedraw(); + } else { + this.updateBuilderGroup(name, group); + } } - else { - this.updateBuilderGroup(name, group); + + updateBuilderGroup(name, group) { + if (this.groups[name]) { + this.groups[name] = group; + this.triggerRedraw(); + } } - } - updateBuilderGroup(name, group) { - if (this.groups[name]) { - this.groups[name] = group; - this.triggerRedraw(); + generateKey(info) { + return ( + info.key || + _.camelCase( + info.title || info.label || info.placeholder || info.type, + ) + ); } - } - - generateKey(info) { - return info.key || _.camelCase( - info.title || - info.label || - info.placeholder || - info.type - ); - } } diff --git a/src/WebformBuilder.unit.js b/src/WebformBuilder.unit.js index e126f49f1a..158774180b 100644 --- a/src/WebformBuilder.unit.js +++ b/src/WebformBuilder.unit.js @@ -2,274 +2,423 @@ import assert from 'power-assert'; import Harness from '../test/harness'; import WebformBuilder from './WebformBuilder'; import Builders from './builders'; -import { uniqueApiKeys, uniqueApiKeysLayout, uniqueApiKeysSameLevel, columnsForm, resourceKeyCamelCase } from '../test/formtest'; +import { + uniqueApiKeys, + uniqueApiKeysLayout, + uniqueApiKeysSameLevel, + columnsForm, + resourceKeyCamelCase, +} from '../test/formtest'; import sameApiKeysLayoutComps from '../test/forms/sameApiKeysLayoutComps'; import testApiKeysUniquifying from '../test/forms/testApiKeysUniquifying'; import formBasedOnWizard from '../test/forms/formBasedOnWizard'; import formWithFormController from '../test/forms/formWithFormController'; -describe('WebformBuilder tests', function() { - this.retries(3); - - before(function(done) { Harness.builderBefore(done); }); - - afterEach(function() { Harness.getBuilder().setForm({ display: 'form', components: [] }); }); - - after(function(done) { Harness.builderAfter(done); }); - - it('Should create a new form builder class', function(done) { - const builder = Harness.getBuilder(); - assert(builder instanceof WebformBuilder, 'Builder must be an instance of FormioFormBuilder'); - done(); - }); - - it('Should execute form controller', function(done) { - const builder = Harness.getBuilder(); - builder.webform.form = formWithFormController; - - setTimeout(() => { - const textF = builder.webform.getComponent('textField'); - assert.equal(textF.getValue(), 'Hello World'); - assert.equal(textF.disabled, true); - assert.equal(builder.webform.components[0].disabled, true); - done(); - }, 500); - }); - - it('Should not show unique API error when components with same keys are inside and outside of the Data component', function(done) { - const builder = Harness.getBuilder(); - builder.webform.setForm(uniqueApiKeys).then(() => { - builder.highlightInvalidComponents(); - const component = builder.webform.getComponent(['textField']); - assert.equal(component.errors.length, 0); - done(); - }).catch(done); - }); - - it('Should show unique API error when components inside and outside of the Layout component have same keys', function(done) { - const builder = Harness.getBuilder(); - builder.webform.setForm(uniqueApiKeysLayout).then(() => { - builder.highlightInvalidComponents(); - const component = builder.webform.getComponent(['textField']); - assert.equal(component.errors.length, 1); - done(); - }).catch(done); - }); - - it('Should not overwrite existing resource key in camelCase', function(done) { - const builder = Harness.getBuilder(); - builder.setForm(resourceKeyCamelCase).then(() => { - const component = builder.webform.getComponent('CalendarID'); - assert.equal(!!document.querySelector(`[name='data[${component.key}]']`), true); - done(); - }).catch(done); - }); - - it('Should show unique API error when layout components have same keys', function(done) { - const builder = Harness.getBuilder(); - builder.webform.setForm(sameApiKeysLayoutComps).then(() => { - builder.highlightInvalidComponents(); - const component = builder.webform.getComponent(['tabs']); - assert.equal(component.errors.length, 1, 'Should show Unique API Key error'); - done(); - }).catch(done); - }); - - it('Should allow add components', function(done) { - const builder = Harness.getBuilder(); - builder.setForm(columnsForm).then(() => { - const column1 = builder.webform.element.querySelector('[ref="columns-container"]'); - Harness.buildComponent('textfield', column1); - setTimeout(() => { - Harness.saveComponent(); - setTimeout(() => { - const columns = builder.webform.getComponent('columns'); - assert.equal(columns.columns[0].length, 1); - done(); - }, 150); - }, 150); - }).catch(done); - }); - - it('Should show unique API error when components on the same level have same keys', function(done) { - const builder = Harness.getBuilder(); - builder.webform.setForm(uniqueApiKeysSameLevel).then(() => { - builder.highlightInvalidComponents(); - const component = builder.webform.getComponent(['textField']); - assert.equal(component.errors.length, 1); - done(); - }).catch(done); - }); - - it('Should uniquify API keys when add a component to the container which already has the same type component', function(done) { - const builder = Harness.getBuilder(); - builder.webform.setForm(testApiKeysUniquifying).then(() => { - const ERROR_MSG = 'Should add a number to the api key of the second component of the same type'; - let containerTestsReady; - const containerTestsPromise = new Promise((resolve) => containerTestsReady = resolve); - - const container = builder.webform.element.querySelector(['[ref="container-container"]']); - Harness.buildComponent('editgrid', container); - - setTimeout(() => { - const newEditGridApiKey = builder.editForm.getComponent('key'); - assert.equal(newEditGridApiKey.dataValue, 'editGrid1', ERROR_MSG); - Harness.saveComponent(); +describe('WebformBuilder tests', function () { + this.retries(3); - setTimeout(() => { - const editGridInsideContainer = container.querySelector('[ref="editGrid-container"]'); - Harness.buildComponent('columns', editGridInsideContainer); - - setTimeout(() => { - const newColumnsApiKey = builder.editForm.getComponent('key'); - assert.equal(newColumnsApiKey.dataValue, 'columns1', ERROR_MSG); - Harness.saveComponent(); - - setTimeout(() => { - const columnInsideEditGridInsideContainer = editGridInsideContainer.querySelector('[ref="columns-container"]'); - Harness.buildComponent('textfield', columnInsideEditGridInsideContainer); - - setTimeout(() => { - const newTextFieldApiKey = builder.editForm.getComponent('key'); - assert.equal(newTextFieldApiKey.dataValue, 'textField1', ERROR_MSG); - Harness.saveComponent(); - containerTestsReady(); - }, 150); - }, 150); - }, 150); - }, 150); - }, 150); - - containerTestsPromise.then(() => { - const panel = builder.webform.element.querySelector(['[ref="panel-container"]']); - Harness.buildComponent('datagrid', panel); + before(function (done) { + Harness.builderBefore(done); + }); - setTimeout(() => { - const newDataGridApiKey = builder.editForm.getComponent('key'); - assert.equal(newDataGridApiKey.dataValue, 'dataGrid1', ERROR_MSG); - Harness.saveComponent(); + afterEach(function () { + Harness.getBuilder().setForm({ display: 'form', components: [] }); + }); - setTimeout(() => { - const dataGridInsidePanel = panel.querySelector('[ref="dataGrid-container"]'); - Harness.buildComponent('number', dataGridInsidePanel); + after(function (done) { + Harness.builderAfter(done); + }); - setTimeout(() => { - const newNumberApiKey = builder.editForm.getComponent('key'); - assert.equal(newNumberApiKey.dataValue, 'number1', ERROR_MSG); - Harness.saveComponent(); + it('Should create a new form builder class', function (done) { + const builder = Harness.getBuilder(); + assert( + builder instanceof WebformBuilder, + 'Builder must be an instance of FormioFormBuilder', + ); + done(); + }); - setTimeout(() => { - const columnInsidefieldSetInsideDataGridInsidePanel = dataGridInsidePanel.parentElement.querySelectorAll('[ref="columns-container"]')[1]; - Harness.buildComponent('checkbox', columnInsidefieldSetInsideDataGridInsidePanel); + it('Should execute form controller', function (done) { + const builder = Harness.getBuilder(); + builder.webform.form = formWithFormController; + setTimeout(() => { + const textF = builder.webform.getComponent('textField'); + assert.equal(textF.getValue(), 'Hello World'); + assert.equal(textF.disabled, true); + assert.equal(builder.webform.components[0].disabled, true); + done(); + }, 500); + }); + + it('Should not show unique API error when components with same keys are inside and outside of the Data component', function (done) { + const builder = Harness.getBuilder(); + builder.webform + .setForm(uniqueApiKeys) + .then(() => { + builder.highlightInvalidComponents(); + const component = builder.webform.getComponent(['textField']); + assert.equal(component.errors.length, 0); + done(); + }) + .catch(done); + }); + + it('Should show unique API error when components inside and outside of the Layout component have same keys', function (done) { + const builder = Harness.getBuilder(); + builder.webform + .setForm(uniqueApiKeysLayout) + .then(() => { + builder.highlightInvalidComponents(); + const component = builder.webform.getComponent(['textField']); + assert.equal(component.errors.length, 1); + done(); + }) + .catch(done); + }); + + it('Should not overwrite existing resource key in camelCase', function (done) { + const builder = Harness.getBuilder(); + builder + .setForm(resourceKeyCamelCase) + .then(() => { + const component = builder.webform.getComponent('CalendarID'); + assert.equal( + !!document.querySelector(`[name='data[${component.key}]']`), + true, + ); + done(); + }) + .catch(done); + }); + + it('Should show unique API error when layout components have same keys', function (done) { + const builder = Harness.getBuilder(); + builder.webform + .setForm(sameApiKeysLayoutComps) + .then(() => { + builder.highlightInvalidComponents(); + const component = builder.webform.getComponent(['tabs']); + assert.equal( + component.errors.length, + 1, + 'Should show Unique API Key error', + ); + done(); + }) + .catch(done); + }); + + it('Should allow add components', function (done) { + const builder = Harness.getBuilder(); + builder + .setForm(columnsForm) + .then(() => { + const column1 = builder.webform.element.querySelector( + '[ref="columns-container"]', + ); + Harness.buildComponent('textfield', column1); setTimeout(() => { - const newTextFieldApiKey = builder.editForm.getComponent('key'); - assert.equal(newTextFieldApiKey.dataValue, 'checkbox1', ERROR_MSG); - done(); + Harness.saveComponent(); + setTimeout(() => { + const columns = builder.webform.getComponent('columns'); + assert.equal(columns.columns[0].length, 1); + done(); + }, 150); }, 150); - }, 150); - }, 150); - }, 150); - }, 150); - }); - }).catch(done); - }); + }) + .catch(done); + }); + + it('Should show unique API error when components on the same level have same keys', function (done) { + const builder = Harness.getBuilder(); + builder.webform + .setForm(uniqueApiKeysSameLevel) + .then(() => { + builder.highlightInvalidComponents(); + const component = builder.webform.getComponent(['textField']); + assert.equal(component.errors.length, 1); + done(); + }) + .catch(done); + }); + + it('Should uniquify API keys when add a component to the container which already has the same type component', function (done) { + const builder = Harness.getBuilder(); + builder.webform + .setForm(testApiKeysUniquifying) + .then(() => { + const ERROR_MSG = + 'Should add a number to the api key of the second component of the same type'; + let containerTestsReady; + const containerTestsPromise = new Promise( + (resolve) => (containerTestsReady = resolve), + ); + + const container = builder.webform.element.querySelector([ + '[ref="container-container"]', + ]); + Harness.buildComponent('editgrid', container); - it('Should override the way a key for new component is set', function(done) { - const builder = Harness.getBuilder(); - builder.setForm(columnsForm).then(() => { - Builders.builders.webform.prototype.updateComponentKey = function() { - return 'rewrittenNumberKey'; - }; - - const column = builder.webform.element.querySelector('[ref="columns-container"]'); - - Harness.buildComponent('number', column); - - setTimeout(() => { - const numberLabel = builder.editForm.getComponent('label'); - numberLabel.setValue('Test Number'); - - setTimeout(() => { - const numberKey = builder.editForm.getComponent('key'); - assert.equal(numberKey.dataValue, 'rewrittenNumberKey'); - - done(); - }, 350); - }, 350); - }).catch(done); - }); - - it('Should add submit button after switching from wizard form', function(done) { - const builder = Harness.getBuilder(); - builder.setForm(formBasedOnWizard).then(() => { - const components = builder.webform.components; - const submit = components[components.length - 1]; - - assert.equal(submit.key, 'submit'); - done(); - }).catch(done); - }); - - it('Should keep min/max date validation settings with moment.js function', function(done) { - const builder = Harness.getBuilder(); - builder.setForm(columnsForm).then(() => { - const column1 = builder.webform.element.querySelector('[ref="columns-container"]'); - Harness.buildComponent('day', column1); - - setTimeout(() => { - const maxDateComp = builder.editForm.getComponent('maxDate'); - maxDateComp.setValue('moment().add(10, \'days\')'); - - setTimeout(() => { - Harness.saveComponent(); + setTimeout(() => { + const newEditGridApiKey = + builder.editForm.getComponent('key'); + assert.equal( + newEditGridApiKey.dataValue, + 'editGrid1', + ERROR_MSG, + ); + Harness.saveComponent(); + + setTimeout(() => { + const editGridInsideContainer = container.querySelector( + '[ref="editGrid-container"]', + ); + Harness.buildComponent( + 'columns', + editGridInsideContainer, + ); + + setTimeout(() => { + const newColumnsApiKey = + builder.editForm.getComponent('key'); + assert.equal( + newColumnsApiKey.dataValue, + 'columns1', + ERROR_MSG, + ); + Harness.saveComponent(); + + setTimeout(() => { + const columnInsideEditGridInsideContainer = + editGridInsideContainer.querySelector( + '[ref="columns-container"]', + ); + Harness.buildComponent( + 'textfield', + columnInsideEditGridInsideContainer, + ); + + setTimeout(() => { + const newTextFieldApiKey = + builder.editForm.getComponent('key'); + assert.equal( + newTextFieldApiKey.dataValue, + 'textField1', + ERROR_MSG, + ); + Harness.saveComponent(); + containerTestsReady(); + }, 150); + }, 150); + }, 150); + }, 150); + }, 150); - setTimeout(() => { - const dayComp = builder.webform.getComponent(['day']); - assert.equal(dayComp.component.maxDate, 'moment().add(10, \'days\')'); - done(); - }, 200); - }, 200); - }, 150); - }).catch(done); - }); + containerTestsPromise.then(() => { + const panel = builder.webform.element.querySelector([ + '[ref="panel-container"]', + ]); + Harness.buildComponent('datagrid', panel); + + setTimeout(() => { + const newDataGridApiKey = + builder.editForm.getComponent('key'); + assert.equal( + newDataGridApiKey.dataValue, + 'dataGrid1', + ERROR_MSG, + ); + Harness.saveComponent(); + + setTimeout(() => { + const dataGridInsidePanel = panel.querySelector( + '[ref="dataGrid-container"]', + ); + Harness.buildComponent( + 'number', + dataGridInsidePanel, + ); + + setTimeout(() => { + const newNumberApiKey = + builder.editForm.getComponent('key'); + assert.equal( + newNumberApiKey.dataValue, + 'number1', + ERROR_MSG, + ); + Harness.saveComponent(); + + setTimeout(() => { + const columnInsidefieldSetInsideDataGridInsidePanel = + dataGridInsidePanel.parentElement.querySelectorAll( + '[ref="columns-container"]', + )[1]; + Harness.buildComponent( + 'checkbox', + columnInsidefieldSetInsideDataGridInsidePanel, + ); + + setTimeout(() => { + const newTextFieldApiKey = + builder.editForm.getComponent( + 'key', + ); + assert.equal( + newTextFieldApiKey.dataValue, + 'checkbox1', + ERROR_MSG, + ); + done(); + }, 150); + }, 150); + }, 150); + }, 150); + }, 150); + }); + }) + .catch(done); + }); + + it('Should override the way a key for new component is set', function (done) { + const builder = Harness.getBuilder(); + builder + .setForm(columnsForm) + .then(() => { + Builders.builders.webform.prototype.updateComponentKey = + function () { + return 'rewrittenNumberKey'; + }; + + const column = builder.webform.element.querySelector( + '[ref="columns-container"]', + ); + + Harness.buildComponent('number', column); - it('Should remove deleted components keys from default value', function(done) { - const builder = Harness.getBuilder(); - builder.setForm({}).then(() => { - Harness.buildComponent('datagrid'); + setTimeout(() => { + const numberLabel = builder.editForm.getComponent('label'); + numberLabel.setValue('Test Number'); + + setTimeout(() => { + const numberKey = builder.editForm.getComponent('key'); + assert.equal(numberKey.dataValue, 'rewrittenNumberKey'); + + done(); + }, 350); + }, 350); + }) + .catch(done); + }); + + it('Should add submit button after switching from wizard form', function (done) { + const builder = Harness.getBuilder(); + builder + .setForm(formBasedOnWizard) + .then(() => { + const components = builder.webform.components; + const submit = components[components.length - 1]; + + assert.equal(submit.key, 'submit'); + done(); + }) + .catch(done); + }); + + it('Should keep min/max date validation settings with moment.js function', function (done) { + const builder = Harness.getBuilder(); + builder + .setForm(columnsForm) + .then(() => { + const column1 = builder.webform.element.querySelector( + '[ref="columns-container"]', + ); + Harness.buildComponent('day', column1); - setTimeout(() => { - const dataGridDefaultValue = builder.editForm.getComponent('defaultValue'); - dataGridDefaultValue.removeRow(0); + setTimeout(() => { + const maxDateComp = + builder.editForm.getComponent('maxDate'); + maxDateComp.setValue("moment().add(10, 'days')"); + + setTimeout(() => { + Harness.saveComponent(); + + setTimeout(() => { + const dayComp = builder.webform.getComponent([ + 'day', + ]); + assert.equal( + dayComp.component.maxDate, + "moment().add(10, 'days')", + ); + done(); + }, 200); + }, 200); + }, 150); + }) + .catch(done); + }); - setTimeout(() => { - Harness.saveComponent(); - setTimeout(() => { - const dataGridContainer = builder.webform.element.querySelector('[ref="dataGrid-container"]'); - Harness.buildComponent('textfield', dataGridContainer); - - setTimeout(() => { - Harness.saveComponent(); - - setTimeout(() => { - const textField = builder.webform.getComponent(['dataGrid', 'textField'])[0]; - textField.refs.removeComponent.dispatchEvent( new MouseEvent('click', { - view: window, - bubbles: true, - cancelable: true - })); + it('Should remove deleted components keys from default value', function (done) { + const builder = Harness.getBuilder(); + builder + .setForm({}) + .then(() => { + Harness.buildComponent('datagrid'); setTimeout(() => { - const dataGrid = builder.webform.getComponent(['dataGrid']); - assert.deepEqual(dataGrid.schema.defaultValue, [{}], 'Should remove TextField key'); - done(); - }, 300); - }); - }, 300); - }, 300); - }, 350); - }, 350); - }).catch(done); - }); + const dataGridDefaultValue = + builder.editForm.getComponent('defaultValue'); + dataGridDefaultValue.removeRow(0); + + setTimeout(() => { + Harness.saveComponent(); + setTimeout(() => { + const dataGridContainer = + builder.webform.element.querySelector( + '[ref="dataGrid-container"]', + ); + Harness.buildComponent( + 'textfield', + dataGridContainer, + ); + + setTimeout(() => { + Harness.saveComponent(); + + setTimeout(() => { + const textField = + builder.webform.getComponent([ + 'dataGrid', + 'textField', + ])[0]; + textField.refs.removeComponent.dispatchEvent( + new MouseEvent('click', { + view: window, + bubbles: true, + cancelable: true, + }), + ); + + setTimeout(() => { + const dataGrid = + builder.webform.getComponent([ + 'dataGrid', + ]); + assert.deepEqual( + dataGrid.schema.defaultValue, + [{}], + 'Should remove TextField key', + ); + done(); + }, 300); + }); + }, 300); + }, 300); + }, 350); + }, 350); + }) + .catch(done); + }); }); diff --git a/src/Wizard.js b/src/Wizard.js index 961a52d897..565d52043a 100644 --- a/src/Wizard.js +++ b/src/Wizard.js @@ -1,1190 +1,1245 @@ -import _ from "lodash"; -import Webform from "./Webform"; -import { Formio } from "./Formio"; +import _ from 'lodash'; +import Webform from './Webform'; +import { Formio } from './Formio'; import { - fastCloneDeep, - checkCondition, - firstNonNil, - uniqueKey, - eachComponent, -} from "./utils/utils"; + fastCloneDeep, + checkCondition, + firstNonNil, + uniqueKey, + eachComponent, +} from './utils/utils'; export default class Wizard extends Webform { - /** - * Constructor for wizard based forms - * @param element Dom element to place this wizard. - * @param {Object} options Options object, supported options are: - * - breadcrumbSettings.clickable: true (default) determines if the breadcrumb bar is clickable or not - * - buttonSettings.show*(Previous, Next, Cancel): true (default) determines if the button is shown or not - * - allowPrevious: false (default) determines if the breadcrumb bar is clickable or not for visited tabs - */ - constructor() { - let element, options; - if (arguments[0] instanceof HTMLElement || arguments[1]) { - element = arguments[0]; - options = arguments[1] || {}; - } else { - options = arguments[0] || {}; - } - - options.display = "wizard"; - - super(element, options); - this.pages = []; - this.prefixComps = []; - this.suffixComps = []; - this.components = []; - this.originalComponents = []; - this.page = 0; - this.currentPanel = null; - this.currentPanels = null; - this.currentNextPage = 0; - this._seenPages = [0]; - this.subWizards = []; - this.allPages = []; - this.lastPromise = Promise.resolve(); - this.enabledIndex = 0; - this.editMode = false; - this.originalOptions = _.cloneDeep(this.options); - } - - isLastPage() { - const next = this.getNextPage(); - - if (_.isNumber(next)) { - return next === -1; - } - - return _.isNull(next); - } - - getPages(args = {}) { - const { all = false } = args; - const pages = this.hasExtraPages ? this.components : this.pages; - const filteredPages = pages.filter( - all ? _.identity : (p, index) => this._seenPages.includes(index), - ); - - return filteredPages; - } - - get hasExtraPages() { - return !_.isEmpty(this.subWizards); - } - - get data() { - return super.data; - } - - get localData() { - return this.pages[this.page]?.root?.submission.data || this.submission.data; - } - - checkConditions(data, flags, row) { - const visible = super.checkConditions(data, flags, row); - this.establishPages(data); - return visible; - } - - set data(value) { - this._data = value; - _.each(this.getPages({ all: true }), (component) => { - component.data = this.componentContext(component); - }); - } - - getComponents() { - return this.submitting - ? this.getPages({ all: this.isLastPage() }) - : super.getComponents(); - } - - resetValue() { - this.getPages({ all: true }).forEach((page) => page.resetValue()); - this.setPristine(true); - } - - init() { - // Check for and initlize button settings object - this.options.buttonSettings = _.defaults(this.options.buttonSettings, { - showPrevious: true, - showNext: true, - showSubmit: true, - showCancel: !this.options.readOnly, - }); - - if (!this.isSecondInit) { - this.isClickableDefined = - this.options?.breadCrumbSettings && - Object.prototype.hasOwnProperty.call( - this.options.breadcrumbSettings, - "clickable", + /** + * Constructor for wizard based forms + * @param element Dom element to place this wizard. + * @param {Object} options Options object, supported options are: + * - breadcrumbSettings.clickable: true (default) determines if the breadcrumb bar is clickable or not + * - buttonSettings.show*(Previous, Next, Cancel): true (default) determines if the button is shown or not + * - allowPrevious: false (default) determines if the breadcrumb bar is clickable or not for visited tabs + */ + constructor() { + let element, options; + if (arguments[0] instanceof HTMLElement || arguments[1]) { + element = arguments[0]; + options = arguments[1] || {}; + } else { + options = arguments[0] || {}; + } + + options.display = 'wizard'; + + super(element, options); + this.pages = []; + this.prefixComps = []; + this.suffixComps = []; + this.components = []; + this.originalComponents = []; + this.page = 0; + this.currentPanel = null; + this.currentPanels = null; + this.currentNextPage = 0; + this._seenPages = [0]; + this.subWizards = []; + this.allPages = []; + this.lastPromise = Promise.resolve(); + this.enabledIndex = 0; + this.editMode = false; + this.originalOptions = _.cloneDeep(this.options); + } + + isLastPage() { + const next = this.getNextPage(); + + if (_.isNumber(next)) { + return next === -1; + } + + return _.isNull(next); + } + + getPages(args = {}) { + const { all = false } = args; + const pages = this.hasExtraPages ? this.components : this.pages; + const filteredPages = pages.filter( + all ? _.identity : (p, index) => this._seenPages.includes(index), ); - this.isSecondInit = true; + + return filteredPages; } - this.options.breadcrumbSettings = _.defaults( - this.options.breadcrumbSettings, - { - clickable: true, - }, - ); - this.options.allowPrevious = this.options.allowPrevious || false; + get hasExtraPages() { + return !_.isEmpty(this.subWizards); + } - this.page = 0; - const onReady = super.init(); - this.setComponentSchema(); + get data() { + return super.data; + } - if (this.pages?.[this.page]) { - this.component = this.pages[this.page].component; + get localData() { + return ( + this.pages[this.page]?.root?.submission.data || this.submission.data + ); } - this.on("subWizardsUpdated", (subForm) => { - const subWizard = this.subWizards.find( - (subWizard) => subForm?.id && subWizard.subForm?.id === subForm?.id, - ); + checkConditions(data, flags, row) { + const visible = super.checkConditions(data, flags, row); + this.establishPages(data); + return visible; + } - if (this.subWizards.length && subWizard) { - subWizard.subForm.setValue(subForm._submission, {}, true); - this.establishPages(); - this.redraw(); - } - }); - - return onReady; - } - - get wizardKey() { - return `wizard-${this.id}`; - } - - get wizard() { - return this.form; - } - - set wizard(form) { - this.setForm(form); - } - - get buttons() { - const buttons = {}; - [ - { name: "cancel", method: "cancel" }, - { name: "previous", method: "prevPage" }, - { name: "next", method: "nextPage" }, - { name: "submit", method: "submit" }, - ].forEach((button) => { - if (this.hasButton(button.name)) { - buttons[button.name] = button; - } - }); - return buttons; - } - - get buttonOrder() { - const defaultButtonOrder = ["cancel", "previous", "next", "submit"]; - return ( - this.options.properties?.wizardButtonOrder?.toLowerCase().split(", ") ?? - defaultButtonOrder - ); - } - - get renderContext() { - return { - disableWizardSubmit: this.form.disableWizardSubmit, - wizardKey: this.wizardKey, - isBreadcrumbClickable: this.isBreadcrumbClickable(), - isSubForm: !!this.parent && !this.root?.component?.type === "wizard", - panels: this.allPages.length - ? this.allPages.map((page) => page.component) - : this.pages.map((page) => page.component), - buttons: this.buttons, - currentPage: this.page, - buttonOrder: this.buttonOrder, - }; - } - - prepareNavigationSettings(ctx) { - const currentPanel = this.currentPanel; - - if (currentPanel && currentPanel.buttonSettings) { - Object.keys(currentPanel.buttonSettings).forEach(() => { - Object.keys(ctx.buttons).forEach((key) => { - if ( - (typeof currentPanel.buttonSettings[key] !== "undefined" && - !currentPanel.buttonSettings[key]) || - ctx.isSubForm - ) { - ctx.buttons[key] = null; - } + set data(value) { + this._data = value; + _.each(this.getPages({ all: true }), (component) => { + component.data = this.componentContext(component); }); - }); } - return this.renderTemplate("wizardNav", ctx); - } + getComponents() { + return this.submitting + ? this.getPages({ all: this.isLastPage() }) + : super.getComponents(); + } - prepareHeaderSettings(ctx, headerType) { - const shouldHideBreadcrumbs = - this.currentPanel?.breadcrumb === "none" || - _.get(this.form, "settings.wizardBreadcrumbsType", "") === "none"; - if (shouldHideBreadcrumbs || ctx.isSubForm) { - return null; + resetValue() { + this.getPages({ all: true }).forEach((page) => page.resetValue()); + this.setPristine(true); } - return this.renderTemplate(headerType, ctx); - } - render() { - const ctx = this.renderContext; + init() { + // Check for and initlize button settings object + this.options.buttonSettings = _.defaults(this.options.buttonSettings, { + showPrevious: true, + showNext: true, + showSubmit: true, + showCancel: !this.options.readOnly, + }); - if (this.component.key) { - ctx.panels.map((panel) => { - if (panel.key === this.component.key) { - this.currentPanel = panel; - ctx.wizardPageTooltip = this.getFormattedTooltip(panel.tooltip); + if (!this.isSecondInit) { + this.isClickableDefined = + this.options?.breadCrumbSettings && + Object.prototype.hasOwnProperty.call( + this.options.breadcrumbSettings, + 'clickable', + ); + this.isSecondInit = true; } - }); - } - - const wizardNav = this.prepareNavigationSettings(ctx); - - const wizardHeaderType = `wizardHeader${_.get(this.form, "settings.wizardHeaderType", "")}`; - const wizardHeaderLocation = _.get( - this.form, - "settings.wizardHeaderLocation", - "left", - ); - const wizardHeader = this.prepareHeaderSettings(ctx, wizardHeaderType); - - return this.renderTemplate( - "wizard", - { - ...ctx, - className: super.getClassName(), - wizardHeader, - wizardHeaderType, - wizardHeaderLocation, - wizardNav, - components: this.renderComponents([ - ...this.prefixComps, - ...this.currentPage.components, - ...this.suffixComps, - ]), - }, - this.builderMode ? "builder" : "form", - ); - } - - redrawNavigation() { - if (this.element) { - let navElement = this.element.querySelector(`#${this.wizardKey}-nav`); - if (navElement) { - this.detachNav(); - navElement.outerHTML = this.renderTemplate( - "wizardNav", - this.renderContext, + + this.options.breadcrumbSettings = _.defaults( + this.options.breadcrumbSettings, + { + clickable: true, + }, ); - navElement = this.element.querySelector(`#${this.wizardKey}-nav`); - this.loadRefs(navElement, { - [`${this.wizardKey}-cancel`]: "single", - [`${this.wizardKey}-previous`]: "single", - [`${this.wizardKey}-next`]: "single", - [`${this.wizardKey}-submit`]: "single", + this.options.allowPrevious = this.options.allowPrevious || false; + + this.page = 0; + const onReady = super.init(); + this.setComponentSchema(); + + if (this.pages?.[this.page]) { + this.component = this.pages[this.page].component; + } + + this.on('subWizardsUpdated', (subForm) => { + const subWizard = this.subWizards.find( + (subWizard) => + subForm?.id && subWizard.subForm?.id === subForm?.id, + ); + + if (this.subWizards.length && subWizard) { + subWizard.subForm.setValue(subForm._submission, {}, true); + this.establishPages(); + this.redraw(); + } }); - this.attachNav(); - } - } - } - - redrawHeader() { - if (this.element) { - let headerElement = this.element.querySelector( - `#${this.wizardKey}-header`, - ); - if (headerElement) { - this.detachHeader(); - headerElement.outerHTML = this.renderTemplate( - `wizardHeader${_.get(this.form, "settings.wizardHeaderType", "")}`, - this.renderContext, + + return onReady; + } + + get wizardKey() { + return `wizard-${this.id}`; + } + + get wizard() { + return this.form; + } + + set wizard(form) { + this.setForm(form); + } + + get buttons() { + const buttons = {}; + [ + { name: 'cancel', method: 'cancel' }, + { name: 'previous', method: 'prevPage' }, + { name: 'next', method: 'nextPage' }, + { name: 'submit', method: 'submit' }, + ].forEach((button) => { + if (this.hasButton(button.name)) { + buttons[button.name] = button; + } + }); + return buttons; + } + + get buttonOrder() { + const defaultButtonOrder = ['cancel', 'previous', 'next', 'submit']; + return ( + this.options.properties?.wizardButtonOrder + ?.toLowerCase() + .split(', ') ?? defaultButtonOrder + ); + } + + get renderContext() { + return { + disableWizardSubmit: this.form.disableWizardSubmit, + wizardKey: this.wizardKey, + isBreadcrumbClickable: this.isBreadcrumbClickable(), + isSubForm: + !!this.parent && !this.root?.component?.type === 'wizard', + panels: this.allPages.length + ? this.allPages.map((page) => page.component) + : this.pages.map((page) => page.component), + buttons: this.buttons, + currentPage: this.page, + buttonOrder: this.buttonOrder, + }; + } + + prepareNavigationSettings(ctx) { + const currentPanel = this.currentPanel; + + if (currentPanel && currentPanel.buttonSettings) { + Object.keys(currentPanel.buttonSettings).forEach(() => { + Object.keys(ctx.buttons).forEach((key) => { + if ( + (typeof currentPanel.buttonSettings[key] !== + 'undefined' && + !currentPanel.buttonSettings[key]) || + ctx.isSubForm + ) { + ctx.buttons[key] = null; + } + }); + }); + } + + return this.renderTemplate('wizardNav', ctx); + } + + prepareHeaderSettings(ctx, headerType) { + const shouldHideBreadcrumbs = + this.currentPanel?.breadcrumb === 'none' || + _.get(this.form, 'settings.wizardBreadcrumbsType', '') === 'none'; + if (shouldHideBreadcrumbs || ctx.isSubForm) { + return null; + } + return this.renderTemplate(headerType, ctx); + } + + render() { + const ctx = this.renderContext; + + if (this.component.key) { + ctx.panels.map((panel) => { + if (panel.key === this.component.key) { + this.currentPanel = panel; + ctx.wizardPageTooltip = this.getFormattedTooltip( + panel.tooltip, + ); + } + }); + } + + const wizardNav = this.prepareNavigationSettings(ctx); + + const wizardHeaderType = `wizardHeader${_.get( + this.form, + 'settings.wizardHeaderType', + '', + )}`; + const wizardHeaderLocation = _.get( + this.form, + 'settings.wizardHeaderLocation', + 'left', + ); + const wizardHeader = this.prepareHeaderSettings(ctx, wizardHeaderType); + + return this.renderTemplate( + 'wizard', + { + ...ctx, + className: super.getClassName(), + wizardHeader, + wizardHeaderType, + wizardHeaderLocation, + wizardNav, + components: this.renderComponents([ + ...this.prefixComps, + ...this.currentPage.components, + ...this.suffixComps, + ]), + }, + this.builderMode ? 'builder' : 'form', ); - headerElement = this.element.querySelector(`#${this.wizardKey}-header`); - this.loadRefs(headerElement, { - [`${this.wizardKey}-link`]: "multiple", - [`${this.wizardKey}-tooltip`]: "multiple", + } + + redrawNavigation() { + if (this.element) { + let navElement = this.element.querySelector( + `#${this.wizardKey}-nav`, + ); + if (navElement) { + this.detachNav(); + navElement.outerHTML = this.renderTemplate( + 'wizardNav', + this.renderContext, + ); + navElement = this.element.querySelector( + `#${this.wizardKey}-nav`, + ); + this.loadRefs(navElement, { + [`${this.wizardKey}-cancel`]: 'single', + [`${this.wizardKey}-previous`]: 'single', + [`${this.wizardKey}-next`]: 'single', + [`${this.wizardKey}-submit`]: 'single', + }); + this.attachNav(); + } + } + } + + redrawHeader() { + if (this.element) { + let headerElement = this.element.querySelector( + `#${this.wizardKey}-header`, + ); + if (headerElement) { + this.detachHeader(); + headerElement.outerHTML = this.renderTemplate( + `wizardHeader${_.get( + this.form, + 'settings.wizardHeaderType', + '', + )}`, + this.renderContext, + ); + headerElement = this.element.querySelector( + `#${this.wizardKey}-header`, + ); + this.loadRefs(headerElement, { + [`${this.wizardKey}-link`]: 'multiple', + [`${this.wizardKey}-tooltip`]: 'multiple', + }); + this.attachHeader(); + } + } + } + + attach(element) { + this.setElement(element); + this.loadRefs(element, { + [this.wizardKey]: 'single', + [`${this.wizardKey}-header`]: 'single', + [`${this.wizardKey}-cancel`]: 'single', + [`${this.wizardKey}-previous`]: 'single', + [`${this.wizardKey}-next`]: 'single', + [`${this.wizardKey}-submit`]: 'single', + [`${this.wizardKey}-link`]: 'multiple', + [`${this.wizardKey}-tooltip`]: 'multiple', }); + if ((this.options.readOnly || this.editMode) && !this.enabledIndex) { + this.enabledIndex = this.pages?.length - 1; + } + + this.hook('attachWebform', element, this); + const promises = this.attachComponents(this.refs[this.wizardKey], [ + ...this.prefixComps, + ...this.currentPage.components, + ...this.suffixComps, + ]); + this.attachNav(); this.attachHeader(); - } - } - } - - attach(element) { - this.setElement(element); - this.loadRefs(element, { - [this.wizardKey]: "single", - [`${this.wizardKey}-header`]: "single", - [`${this.wizardKey}-cancel`]: "single", - [`${this.wizardKey}-previous`]: "single", - [`${this.wizardKey}-next`]: "single", - [`${this.wizardKey}-submit`]: "single", - [`${this.wizardKey}-link`]: "multiple", - [`${this.wizardKey}-tooltip`]: "multiple", - }); - if ((this.options.readOnly || this.editMode) && !this.enabledIndex) { - this.enabledIndex = this.pages?.length - 1; - } - - this.hook("attachWebform", element, this); - const promises = this.attachComponents(this.refs[this.wizardKey], [ - ...this.prefixComps, - ...this.currentPage.components, - ...this.suffixComps, - ]); - this.attachNav(); - this.attachHeader(); - return promises.then(() => { - this.emit("render", { component: this.currentPage, page: this.page }); - if (this.component.scrollToTop) { - this.scrollPageToTop(); - } - }); - } - - scrollPageToTop() { - const pageTop = - this.refs[`${this.wizardKey}-header`] ?? this.refs[this.wizardKey]; - - if (!pageTop) { - return; - } - - if ("scrollIntoView" in pageTop) { - pageTop.scrollIntoView(true); - } else { - this.scrollIntoView(pageTop); - } - } - - isBreadcrumbClickable() { - let currentPage = null; - this.pages.map((page) => { - if (_.isEqual(this.currentPage.component, page.component)) { - currentPage = page; - } - }); - - return this.isClickableDefined - ? this.options.breadcrumbSettings.clickable - : _.get(currentPage, "component.breadcrumbClickable", true); - } - - isAllowPrevious() { - let currentPage = null; - this.pages.map((page) => { - if (_.isEqual(this.currentPage.component, page.component)) { - currentPage = page; - } - }); - - return _.get( - currentPage.component, - "allowPrevious", - this.options.allowPrevious, - ); - } - - handleNaviageteOnEnter(event) { - if (event.keyCode === 13) { - const clickEvent = new CustomEvent("click"); - const buttonElement = - this.refs[`${this.wizardKey}-${this.buttons.next.name}`]; - if (buttonElement) { - buttonElement.dispatchEvent(clickEvent); - } - } - } - - handleSaveOnEnter(event) { - if (event.keyCode === 13) { - const clickEvent = new CustomEvent("click"); - const buttonElement = - this.refs[`${this.wizardKey}-${this.buttons.submit.name}`]; - if (buttonElement) { - buttonElement.dispatchEvent(clickEvent); - } - } - } - - attachNav() { - if (this.component.navigateOnEnter) { - this.addEventListener( - document, - "keyup", - this.handleNaviageteOnEnter.bind(this), - ); - } - if (this.component.saveOnEnter) { - this.addEventListener( - document, - "keyup", - this.handleSaveOnEnter.bind(this), - ); - } - _.each(this.buttons, (button) => { - const buttonElement = this.refs[`${this.wizardKey}-${button.name}`]; - this.addEventListener(buttonElement, "click", (event) => { - event.preventDefault(); - - // Disable the button until done. - buttonElement.setAttribute("disabled", "disabled"); - this.setLoading(buttonElement, true); - - // Call the button method, then re-enable the button. - this[button.method]() - .then(() => { - buttonElement.removeAttribute("disabled"); - this.setLoading(buttonElement, false); - }) - .catch(() => { - buttonElement.removeAttribute("disabled"); - this.setLoading(buttonElement, false); - }); - }); - }); - } - - emitWizardPageSelected(index) { - this.emit("wizardPageSelected", this.pages[index], index); - } - - attachHeader() { - const isAllowPrevious = this.isAllowPrevious(); - this.attachTooltips( - this.refs[`${this.wizardKey}-tooltip`], - this.currentPanel.tooltip, - ); - - if (this.isBreadcrumbClickable() || isAllowPrevious) { - this.refs[`${this.wizardKey}-link`]?.forEach((link, index) => { - if (!isAllowPrevious || index <= this.enabledIndex) { - this.addEventListener(link, "click", (event) => { - this.emit("wizardNavigationClicked", this.pages[index]); - event.preventDefault(); - return this.setPage(index).then(() => { - this.emitWizardPageSelected(index); + return promises.then(() => { + this.emit('render', { + component: this.currentPage, + page: this.page, }); - }); + if (this.component.scrollToTop) { + this.scrollPageToTop(); + } + }); + } + + scrollPageToTop() { + const pageTop = + this.refs[`${this.wizardKey}-header`] ?? this.refs[this.wizardKey]; + + if (!pageTop) { + return; + } + + if ('scrollIntoView' in pageTop) { + pageTop.scrollIntoView(true); + } else { + this.scrollIntoView(pageTop); } - }); - } - } - - detachNav() { - if (this.component.navigateOnEnter) { - this.removeEventListener( - document, - "keyup", - this.handleNaviageteOnEnter.bind(this), - ); - } - if (this.component.saveOnEnter) { - this.removeEventListener( - document, - "keyup", - this.handleSaveOnEnter.bind(this), - ); - } - _.each(this.buttons, (button) => { - this.removeEventListener( - this.refs[`${this.wizardKey}-${button.name}`], - "click", - ); - }); - } - - detachHeader() { - if (this.refs[`${this.wizardKey}-link`]) { - this.refs[`${this.wizardKey}-link`].forEach((link) => { - this.removeEventListener(link, "click"); - }); - } - } - - transformPages() { - const allComponents = []; - const components = this.getSortedComponents(this); - let defferedComponents = []; - this.allPages = []; - - // Get all components including all nested components and line up in the correct order - const getAllComponents = (nestedComp, compsArr, pushAllowed = true) => { - const nestedPages = []; - const dataArrayComponents = ["datagrid", "editgrid", "dynamicWizard"]; - const currentComponents = nestedComp?.subForm - ? this.getSortedComponents(nestedComp.subForm) - : nestedComp?.components || []; - const visibleComponents = currentComponents.filter( - (comp) => comp._visible, - ); - const filteredComponents = visibleComponents.filter( - (comp) => - !dataArrayComponents.includes(comp.component.type) && - (comp.type !== "form" || comp.isNestedWizard), - ); - const additionalComponents = currentComponents.filter( - (comp) => comp.subForm?._form.display !== "wizard", - ); - let hasNested = false; - - eachComponent( - filteredComponents, - (comp) => { - if (comp && comp.component) { - if ( - comp.component.type === "panel" && - comp?.parent.wizard && - !getAllComponents(comp, compsArr, false) - ) { - if (pushAllowed) { - this.setRootPanelId(comp); - nestedPages.push(comp); - } - hasNested = true; + } + + isBreadcrumbClickable() { + let currentPage = null; + this.pages.map((page) => { + if (_.isEqual(this.currentPage.component, page.component)) { + currentPage = page; + } + }); + + return this.isClickableDefined + ? this.options.breadcrumbSettings.clickable + : _.get(currentPage, 'component.breadcrumbClickable', true); + } + + isAllowPrevious() { + let currentPage = null; + this.pages.map((page) => { + if (_.isEqual(this.currentPage.component, page.component)) { + currentPage = page; + } + }); + + return _.get( + currentPage.component, + 'allowPrevious', + this.options.allowPrevious, + ); + } + + handleNaviageteOnEnter(event) { + if (event.keyCode === 13) { + const clickEvent = new CustomEvent('click'); + const buttonElement = + this.refs[`${this.wizardKey}-${this.buttons.next.name}`]; + if (buttonElement) { + buttonElement.dispatchEvent(clickEvent); } - if (comp.isNestedWizard && comp.subForm) { - const hasNestedForm = getAllComponents( - comp, - nestedPages, - pushAllowed, - ); - if (!hasNested) { - hasNested = hasNestedForm; - } + } + } + + handleSaveOnEnter(event) { + if (event.keyCode === 13) { + const clickEvent = new CustomEvent('click'); + const buttonElement = + this.refs[`${this.wizardKey}-${this.buttons.submit.name}`]; + if (buttonElement) { + buttonElement.dispatchEvent(clickEvent); } - } - }, - true, - ); - - if (nestedComp.component.type === "panel") { - if (!hasNested && pushAllowed) { - this.setRootPanelId(nestedComp); - compsArr.push(nestedComp); } - if (hasNested && additionalComponents.length) { - const newComp = _.clone(nestedComp); - newComp.components = additionalComponents; - this.setRootPanelId(newComp); - defferedComponents.push(newComp); + } + + attachNav() { + if (this.component.navigateOnEnter) { + this.addEventListener( + document, + 'keyup', + this.handleNaviageteOnEnter.bind(this), + ); + } + if (this.component.saveOnEnter) { + this.addEventListener( + document, + 'keyup', + this.handleSaveOnEnter.bind(this), + ); } - } - if (pushAllowed) { - compsArr.push(...defferedComponents, ...nestedPages); - defferedComponents = []; - } - return hasNested; - }; - - components.forEach((component) => { - if (component.visible) { - getAllComponents(component, allComponents); - } - }, []); - - // recalculate pages only for root wizards, including the situation when the wizard is in a wrapper - if (this.localRoot && this.id === this.localRoot.id) { - allComponents.forEach((comp, index) => { - comp.eachComponent((component) => { - component.page = index; + _.each(this.buttons, (button) => { + const buttonElement = this.refs[`${this.wizardKey}-${button.name}`]; + this.addEventListener(buttonElement, 'click', (event) => { + event.preventDefault(); + + // Disable the button until done. + buttonElement.setAttribute('disabled', 'disabled'); + this.setLoading(buttonElement, true); + + // Call the button method, then re-enable the button. + this[button.method]() + .then(() => { + buttonElement.removeAttribute('disabled'); + this.setLoading(buttonElement, false); + }) + .catch(() => { + buttonElement.removeAttribute('disabled'); + this.setLoading(buttonElement, false); + }); + }); }); - }); } - this.allPages = allComponents; - } + emitWizardPageSelected(index) { + this.emit('wizardPageSelected', this.pages[index], index); + } + + attachHeader() { + const isAllowPrevious = this.isAllowPrevious(); + this.attachTooltips( + this.refs[`${this.wizardKey}-tooltip`], + this.currentPanel.tooltip, + ); - getSortedComponents({ components, originalComponents }) { - // sorts components if they were shuffled after the conditional logic - const currentComponents = []; - const currentPages = []; + if (this.isBreadcrumbClickable() || isAllowPrevious) { + this.refs[`${this.wizardKey}-link`]?.forEach((link, index) => { + if (!isAllowPrevious || index <= this.enabledIndex) { + this.addEventListener(link, 'click', (event) => { + this.emit('wizardNavigationClicked', this.pages[index]); + event.preventDefault(); + return this.setPage(index).then(() => { + this.emitWizardPageSelected(index); + }); + }); + } + }); + } + } - if (components && components.length) { - components.map((page) => { - if (page.component.type === "panel") { - currentPages[page.component.key || page.component.title] = page; + detachNav() { + if (this.component.navigateOnEnter) { + this.removeEventListener( + document, + 'keyup', + this.handleNaviageteOnEnter.bind(this), + ); } - }); - } - - originalComponents?.forEach((item) => { - if (!item.key) { - item.key = item.title; - } - if (currentPages[item.key]) { - currentComponents.push(currentPages[item.key]); - } - }); - - return currentComponents; - } - - findRootPanel(component) { - return component.parent?.parent - ? this.findRootPanel(component.parent) - : component; - } - - setRootPanelId(component) { - if (component.rootPanelId && component.rootPanelId !== component.id) { - return; - } - - const parent = component.parent?.parent - ? this.findRootPanel(component.parent) - : component; - component.rootPanelId = parent.id; - } - - establishPages(data = this.data) { - this.pages = []; - this.prefixComps = []; - this.suffixComps = []; - const visible = []; - const currentPages = {}; - const pageOptions = { - ..._.clone(this.options), - ...(this.parent ? { root: this } : {}), - }; - if (this.components && this.components.length) { - this.components.forEach((page) => { - if (page.component.type === "panel") { - currentPages[page.component.key || page.component.title] = page; + if (this.component.saveOnEnter) { + this.removeEventListener( + document, + 'keyup', + this.handleSaveOnEnter.bind(this), + ); } - }); - } - - if (this.originalComponents) { - this.originalComponents.forEach((item) => { - if (item.type === "panel") { - if (!item.key) { - item.key = item.title; - } - let page = currentPages[item.key]; - const forceShow = this.shouldForceShow(item); - const forceHide = this.shouldForceHide(item); - - let isVisible = !page - ? checkCondition(item, data, data, this.component, this) && - !item.hidden - : page.visible; - - if (forceShow) { - isVisible = true; - } else if (forceHide) { - isVisible = false; - } - - if (isVisible) { - visible.push(item); - if (page) { - this.pages.push(page); + _.each(this.buttons, (button) => { + this.removeEventListener( + this.refs[`${this.wizardKey}-${button.name}`], + 'click', + ); + }); + } + + detachHeader() { + if (this.refs[`${this.wizardKey}-link`]) { + this.refs[`${this.wizardKey}-link`].forEach((link) => { + this.removeEventListener(link, 'click'); + }); + } + } + + transformPages() { + const allComponents = []; + const components = this.getSortedComponents(this); + let defferedComponents = []; + this.allPages = []; + + // Get all components including all nested components and line up in the correct order + const getAllComponents = (nestedComp, compsArr, pushAllowed = true) => { + const nestedPages = []; + const dataArrayComponents = [ + 'datagrid', + 'editgrid', + 'dynamicWizard', + ]; + const currentComponents = nestedComp?.subForm + ? this.getSortedComponents(nestedComp.subForm) + : nestedComp?.components || []; + const visibleComponents = currentComponents.filter( + (comp) => comp._visible, + ); + const filteredComponents = visibleComponents.filter( + (comp) => + !dataArrayComponents.includes(comp.component.type) && + (comp.type !== 'form' || comp.isNestedWizard), + ); + const additionalComponents = currentComponents.filter( + (comp) => comp.subForm?._form.display !== 'wizard', + ); + let hasNested = false; + + eachComponent( + filteredComponents, + (comp) => { + if (comp && comp.component) { + if ( + comp.component.type === 'panel' && + comp?.parent.wizard && + !getAllComponents(comp, compsArr, false) + ) { + if (pushAllowed) { + this.setRootPanelId(comp); + nestedPages.push(comp); + } + hasNested = true; + } + if (comp.isNestedWizard && comp.subForm) { + const hasNestedForm = getAllComponents( + comp, + nestedPages, + pushAllowed, + ); + if (!hasNested) { + hasNested = hasNestedForm; + } + } + } + }, + true, + ); + + if (nestedComp.component.type === 'panel') { + if (!hasNested && pushAllowed) { + this.setRootPanelId(nestedComp); + compsArr.push(nestedComp); + } + if (hasNested && additionalComponents.length) { + const newComp = _.clone(nestedComp); + newComp.components = additionalComponents; + this.setRootPanelId(newComp); + defferedComponents.push(newComp); + } } - } - - if (!page && isVisible) { - page = this.createComponent(item, pageOptions); - page.visible = isVisible; - this.pages.push(page); - page.eachComponent((component) => { - component.page = this.pages.length - 1; + if (pushAllowed) { + compsArr.push(...defferedComponents, ...nestedPages); + defferedComponents = []; + } + return hasNested; + }; + + components.forEach((component) => { + if (component.visible) { + getAllComponents(component, allComponents); + } + }, []); + + // recalculate pages only for root wizards, including the situation when the wizard is in a wrapper + if (this.localRoot && this.id === this.localRoot.id) { + allComponents.forEach((comp, index) => { + comp.eachComponent((component) => { + component.page = index; + }); }); - } - } else if (item.type !== "button") { - if (!this.pages.length) { - this.prefixComps.push(this.createComponent(item, pageOptions)); - } else { - this.suffixComps.push(this.createComponent(item, pageOptions)); - } } - }); + + this.allPages = allComponents; } - if (this.pages.length) { - this.emit("pagesChanged"); + getSortedComponents({ components, originalComponents }) { + // sorts components if they were shuffled after the conditional logic + const currentComponents = []; + const currentPages = []; + + if (components && components.length) { + components.map((page) => { + if (page.component.type === 'panel') { + currentPages[page.component.key || page.component.title] = + page; + } + }); + } + + originalComponents?.forEach((item) => { + if (!item.key) { + item.key = item.title; + } + if (currentPages[item.key]) { + currentComponents.push(currentPages[item.key]); + } + }); + + return currentComponents; + } + + findRootPanel(component) { + return component.parent?.parent + ? this.findRootPanel(component.parent) + : component; } - this.transformPages(); - if (this.allPages && this.allPages.length) { - this.updatePages(); + setRootPanelId(component) { + if (component.rootPanelId && component.rootPanelId !== component.id) { + return; + } + + const parent = component.parent?.parent + ? this.findRootPanel(component.parent) + : component; + component.rootPanelId = parent.id; } - return visible; - } + establishPages(data = this.data) { + this.pages = []; + this.prefixComps = []; + this.suffixComps = []; + const visible = []; + const currentPages = {}; + const pageOptions = { + ..._.clone(this.options), + ...(this.parent ? { root: this } : {}), + }; + if (this.components && this.components.length) { + this.components.forEach((page) => { + if (page.component.type === 'panel') { + currentPages[page.component.key || page.component.title] = + page; + } + }); + } + + if (this.originalComponents) { + this.originalComponents.forEach((item) => { + if (item.type === 'panel') { + if (!item.key) { + item.key = item.title; + } + let page = currentPages[item.key]; + const forceShow = this.shouldForceShow(item); + const forceHide = this.shouldForceHide(item); + + let isVisible = !page + ? checkCondition( + item, + data, + data, + this.component, + this, + ) && !item.hidden + : page.visible; + + if (forceShow) { + isVisible = true; + } else if (forceHide) { + isVisible = false; + } + + if (isVisible) { + visible.push(item); + if (page) { + this.pages.push(page); + } + } + + if (!page && isVisible) { + page = this.createComponent(item, pageOptions); + page.visible = isVisible; + this.pages.push(page); + page.eachComponent((component) => { + component.page = this.pages.length - 1; + }); + } + } else if (item.type !== 'button') { + if (!this.pages.length) { + this.prefixComps.push( + this.createComponent(item, pageOptions), + ); + } else { + this.suffixComps.push( + this.createComponent(item, pageOptions), + ); + } + } + }); + } - updatePages() { - this.pages = this.allPages; - } + if (this.pages.length) { + this.emit('pagesChanged'); + } - addComponents() { - this.establishPages(); - } + this.transformPages(); + if (this.allPages && this.allPages.length) { + this.updatePages(); + } - setPage(num) { - if (num === this.page) { - return Promise.resolve(); + return visible; } - if (num >= 0 && num < this.pages.length) { - this.page = num; + updatePages() { + this.pages = this.allPages; + } - this.pageFieldLogic(num); + addComponents() { + this.establishPages(); + } - this.getNextPage(); + setPage(num) { + if (num === this.page) { + return Promise.resolve(); + } - let parentNum = num; - if (this.hasExtraPages) { - const pageFromPages = this.pages[num]; - const pageFromComponents = this.components[num]; - if ( - !pageFromComponents || - pageFromPages?.id !== pageFromComponents.id - ) { - parentNum = this.components.findIndex((comp) => { - return comp.id === this.pages?.[parentNum]?.rootPanelId; - }); + if (num >= 0 && num < this.pages.length) { + this.page = num; + + this.pageFieldLogic(num); + + this.getNextPage(); + + let parentNum = num; + if (this.hasExtraPages) { + const pageFromPages = this.pages[num]; + const pageFromComponents = this.components[num]; + if ( + !pageFromComponents || + pageFromPages?.id !== pageFromComponents.id + ) { + parentNum = this.components.findIndex((comp) => { + return comp.id === this.pages?.[parentNum]?.rootPanelId; + }); + } + } + if (!this._seenPages.includes(parentNum)) { + this._seenPages = this._seenPages.concat(parentNum); + } + this.redraw().then(() => { + this.checkData(this.submission.data); + }); + return Promise.resolve(); + } else if (!this.pages.length) { + this.redraw(); + return Promise.resolve(); } - } - if (!this._seenPages.includes(parentNum)) { - this._seenPages = this._seenPages.concat(parentNum); - } - this.redraw().then(() => { - this.checkData(this.submission.data); - }); - return Promise.resolve(); - } else if (!this.pages.length) { - this.redraw(); - return Promise.resolve(); - } - return Promise.reject("Page not found"); - } - - pageFieldLogic(page) { - if (this.pages?.[page]) { - // Handle field logic on pages. - this.component = this.pages[page].component; - this.originalComponent = fastCloneDeep(this.component); - this.fieldLogic(this.data); - // If disabled changed, be sure to distribute the setting. - this.disabled = this.shouldDisabled; - } - } - - get currentPage() { - return this.pages && this.pages.length >= this.page - ? this.pages[this.page] - : { components: [] }; - } - - getNextPage() { - if (this.pages?.[this.page]) { - const data = this.submission.data; - const form = this.pages[this.page].component; - // Check conditional nextPage - if (form) { - const page = - this.pages.length > this.page + 1 && !this.showAllErrors - ? this.page + 1 - : -1; - if (form.nextPage) { - const next = this.evaluate( - form.nextPage, - { - next: page, - data, - page, - form, - }, - "next", - ); - if (next === null) { + return Promise.reject('Page not found'); + } + + pageFieldLogic(page) { + if (this.pages?.[page]) { + // Handle field logic on pages. + this.component = this.pages[page].component; + this.originalComponent = fastCloneDeep(this.component); + this.fieldLogic(this.data); + // If disabled changed, be sure to distribute the setting. + this.disabled = this.shouldDisabled; + } + } + + get currentPage() { + return this.pages && this.pages.length >= this.page + ? this.pages[this.page] + : { components: [] }; + } + + getNextPage() { + if (this.pages?.[this.page]) { + const data = this.submission.data; + const form = this.pages[this.page].component; + // Check conditional nextPage + if (form) { + const page = + this.pages.length > this.page + 1 && !this.showAllErrors + ? this.page + 1 + : -1; + if (form.nextPage) { + const next = this.evaluate( + form.nextPage, + { + next: page, + data, + page, + form, + }, + 'next', + ); + if (next === null) { + this.currentNextPage = null; + return null; + } + + const pageNum = parseInt(next, 10); + if (!isNaN(parseInt(pageNum, 10)) && isFinite(pageNum)) { + this.currentNextPage = pageNum; + return pageNum; + } + + this.currentNextPage = this.getPageIndexByKey(next); + return this.currentNextPage; + } + + this.currentNextPage = page; + return page; + } + this.currentNextPage = null; - return null; - } + } + + return null; + } + + getPreviousPage() { + return this.page - 1; + } + + beforeSubmit() { + const pages = this.getPages(); + + return Promise.all( + pages.map((page) => { + page.options.beforeSubmit = true; + return page.beforeSubmit(); + }), + ); + } - const pageNum = parseInt(next, 10); - if (!isNaN(parseInt(pageNum, 10)) && isFinite(pageNum)) { - this.currentNextPage = pageNum; - return pageNum; - } + beforePage(next) { + return new Promise((resolve, reject) => { + this.hook( + next ? 'beforeNext' : 'beforePrev', + this.currentPage, + this.submission, + (err) => { + if (err) { + this.showErrors(err, true); + reject(err); + } + + const form = this.currentPage; + if (form) { + form.beforePage(next).then(resolve).catch(reject); + } else { + resolve(); + } + }, + ); + }); + } - this.currentNextPage = this.getPageIndexByKey(next); - return this.currentNextPage; + emitNextPage() { + this.emit('nextPage', { page: this.page, submission: this.submission }); + } + + nextPage() { + // Read-only forms should not worry about validation before going to next page, nor should they submit. + if (this.options.readOnly) { + return this.beforePage(true).then(() => { + return this.setPage(this.getNextPage()).then(() => { + this.emitNextPage(); + }); + }); + } + + // Validate the form, before go to the next page + if (this.checkValidity(this.localData, true, this.localData, true)) { + this.checkData(this.submission.data); + return this.beforePage(true).then(() => { + return this.setPage(this.getNextPage()).then(() => { + if ( + !(this.options.readOnly || this.editMode) && + this.enabledIndex < this.page + ) { + this.enabledIndex = this.page; + this.redraw(); + } + + this.emitNextPage(); + }); + }); + } else { + this.currentPage.components.forEach((comp) => + comp.setPristine(false), + ); + this.scrollIntoView(this.element); + return Promise.reject(this.showErrors([], true)); } + } + + emitPrevPage() { + this.emit('prevPage', { page: this.page, submission: this.submission }); + } - this.currentNextPage = page; - return page; - } - - this.currentNextPage = null; - } - - return null; - } - - getPreviousPage() { - return this.page - 1; - } - - beforeSubmit() { - const pages = this.getPages(); - - return Promise.all( - pages.map((page) => { - page.options.beforeSubmit = true; - return page.beforeSubmit(); - }), - ); - } - - beforePage(next) { - return new Promise((resolve, reject) => { - this.hook( - next ? "beforeNext" : "beforePrev", - this.currentPage, - this.submission, - (err) => { - if (err) { - this.showErrors(err, true); - reject(err); - } - - const form = this.currentPage; - if (form) { - form.beforePage(next).then(resolve).catch(reject); - } else { - resolve(); - } - }, - ); - }); - } - - emitNextPage() { - this.emit("nextPage", { page: this.page, submission: this.submission }); - } - - nextPage() { - // Read-only forms should not worry about validation before going to next page, nor should they submit. - if (this.options.readOnly) { - return this.beforePage(true).then(() => { - return this.setPage(this.getNextPage()).then(() => { - this.emitNextPage(); + prevPage() { + return this.beforePage().then(() => { + return this.setPage(this.getPreviousPage()).then(() => { + this.emitPrevPage(); + }); }); - }); - } - - // Validate the form, before go to the next page - if (this.checkValidity(this.localData, true, this.localData, true)) { - this.checkData(this.submission.data); - return this.beforePage(true).then(() => { - return this.setPage(this.getNextPage()).then(() => { - if ( - !(this.options.readOnly || this.editMode) && - this.enabledIndex < this.page - ) { - this.enabledIndex = this.page; - this.redraw(); - } + } + + cancel(noconfirm) { + if (this.options.readOnly) { + return Promise.resolve(); + } + + if (super.cancel(noconfirm)) { + this.setPristine(true); + return this.setPage(0).then(() => { + if (this.enabledIndex) { + this.enabledIndex = 0; + } + this.onChange({ resetValue: true }); + this.redraw(); + return this.page; + }); + } + return Promise.resolve(); + } + + getPageIndexByKey(key) { + let pageIndex = this.page; + this.pages.forEach((page, index) => { + if (page.component.key === key) { + pageIndex = index; + return false; + } + }); + return pageIndex; + } + + get schema() { + return this.wizard; + } - this.emitNextPage(); + setComponentSchema() { + const pageKeys = {}; + this.originalComponents = []; + this.component.components.map((item) => { + if (item.type === 'panel') { + item.key = uniqueKey(pageKeys, item.key || 'panel'); + pageKeys[item.key] = true; + + if (this.wizard.full) { + this.options.show = this.options.show || {}; + this.options.show[item.key] = true; + } else if ( + Object.prototype.hasOwnProperty.call(this.wizard, 'full') && + !_.isEqual(this.originalOptions.show, this.options.show) + ) { + this.options.show = { + ...(this.originalOptions.show || {}), + }; + } + } + this.originalComponents.push(_.clone(item)); }); - }); - } else { - this.currentPage.components.forEach((comp) => comp.setPristine(false)); - this.scrollIntoView(this.element); - return Promise.reject(this.showErrors([], true)); - } - } - - emitPrevPage() { - this.emit("prevPage", { page: this.page, submission: this.submission }); - } - - prevPage() { - return this.beforePage().then(() => { - return this.setPage(this.getPreviousPage()).then(() => { - this.emitPrevPage(); - }); - }); - } - - cancel(noconfirm) { - if (this.options.readOnly) { - return Promise.resolve(); - } - - if (super.cancel(noconfirm)) { - this.setPristine(true); - return this.setPage(0).then(() => { - if (this.enabledIndex) { - this.enabledIndex = 0; + + if (!Object.keys(pageKeys).length) { + const newPage = { + type: 'panel', + title: 'Page 1', + label: 'Page 1', + key: 'page1', + components: this.component.components, + }; + this.component.components = [newPage]; + this.originalComponents.push(_.clone(newPage)); } - this.onChange({ resetValue: true }); - this.redraw(); - return this.page; - }); - } - return Promise.resolve(); - } - - getPageIndexByKey(key) { - let pageIndex = this.page; - this.pages.forEach((page, index) => { - if (page.component.key === key) { - pageIndex = index; - return false; - } - }); - return pageIndex; - } - - get schema() { - return this.wizard; - } - - setComponentSchema() { - const pageKeys = {}; - this.originalComponents = []; - this.component.components.map((item) => { - if (item.type === "panel") { - item.key = uniqueKey(pageKeys, item.key || "panel"); - pageKeys[item.key] = true; - - if (this.wizard.full) { - this.options.show = this.options.show || {}; - this.options.show[item.key] = true; - } else if ( - Object.prototype.hasOwnProperty.call(this.wizard, "full") && - !_.isEqual(this.originalOptions.show, this.options.show) - ) { - this.options.show = { ...(this.originalOptions.show || {}) }; + } + + setForm(form, flags) { + if (!form) { + return; } - } - this.originalComponents.push(_.clone(item)); - }); - - if (!Object.keys(pageKeys).length) { - const newPage = { - type: "panel", - title: "Page 1", - label: "Page 1", - key: "page1", - components: this.component.components, - }; - this.component.components = [newPage]; - this.originalComponents.push(_.clone(newPage)); - } - } - - setForm(form, flags) { - if (!form) { - return; - } - - return super.setForm(form, flags); - } - - onSetForm(clonedForm, initialForm) { - this.component.components = - (this._parentPath ? initialForm.components : clonedForm.components) || []; - this.setComponentSchema(); - } - - setEditMode(submission) { - if (!this.editMode && submission._id && !this.options.readOnly) { - this.editMode = true; - this.redraw(); - } - } - - setValue(submission, flags = {}, ignoreEstablishment) { - const changed = this.getPages({ all: true }).reduce((changed, page) => { - return ( - this.setNestedValue(page, submission.data, flags, changed) || changed - ); - }, false); - - this.mergeData(this.data, submission.data); - - if (changed) { - this.pageFieldLogic(this.page); - } - - submission.data = this.data; - this._submission = submission; - - if (!ignoreEstablishment) { - this.establishPages(submission.data); - } - - this.setEditMode(submission); - - return changed; - } - - isClickable(page, index) { - return ( - this.page !== index && - firstNonNil([ - _.get(page, "breadcrumbClickable"), - this.options.breadcrumbSettings.clickable, - ]) - ); - } - - hasButton(name, nextPage = this.getNextPage()) { - // get page options with global options as default values - const { - previous = this.options.buttonSettings.showPrevious, - cancel = this.options.buttonSettings.showCancel, - submit = this.options.buttonSettings.showSubmit, - next = this.options.buttonSettings.showNext, - } = _.get(this.currentPage, "component.buttonSettings", {}); - - switch (name) { - case "previous": - return previous && this.getPreviousPage() > -1; - case "next": - return next && nextPage !== null && nextPage !== -1; - case "cancel": - return cancel && !this.options.readOnly; - case "submit": + + return super.setForm(form, flags); + } + + onSetForm(clonedForm, initialForm) { + this.component.components = + (this._parentPath + ? initialForm.components + : clonedForm.components) || []; + this.setComponentSchema(); + } + + setEditMode(submission) { + if (!this.editMode && submission._id && !this.options.readOnly) { + this.editMode = true; + this.redraw(); + } + } + + setValue(submission, flags = {}, ignoreEstablishment) { + const changed = this.getPages({ all: true }).reduce((changed, page) => { + return ( + this.setNestedValue(page, submission.data, flags, changed) || + changed + ); + }, false); + + this.mergeData(this.data, submission.data); + + if (changed) { + this.pageFieldLogic(this.page); + } + + submission.data = this.data; + this._submission = submission; + + if (!ignoreEstablishment) { + this.establishPages(submission.data); + } + + this.setEditMode(submission); + + return changed; + } + + isClickable(page, index) { return ( - submit && - !this.options.readOnly && - (nextPage === null || this.page === this.pages.length - 1) - ); - default: - return true; - } - } - - pageId(page) { - if (page.key) { - // Some panels have the same key.... - return `${page.key}-${page.title}`; - } else if (page.components && page.components.length > 0) { - return this.pageId(page.components[0]); - } else { - return page.title; - } - } - - onChange(flags, changed, modified, changes) { - super.onChange(flags, changed, modified, changes); - if (this.alert && !this.submitted) { - this.checkValidity(this.localData, false, this.localData, true); - this.showErrors([], true, true); - } - - // If the pages change, need to redraw the header. - let currentPanels; - let panels; - const currentNextPage = this.currentNextPage; - if (this.hasExtraPages) { - currentPanels = this.pages.map((page) => page.component.key); - this.establishPages(); - panels = this.pages.map((page) => page.component.key); - } else { - currentPanels = - this.currentPanels || this.pages.map((page) => page.component.key); - panels = this.establishPages().map((panel) => panel.key); - this.currentPanels = panels; - if (this.currentPanel?.key && this.currentPanels?.length) { - this.setPage( - this.currentPanels.findIndex( - (panel) => panel === this.currentPanel.key, - ), + this.page !== index && + firstNonNil([ + _.get(page, 'breadcrumbClickable'), + this.options.breadcrumbSettings.clickable, + ]) ); - } } - if (!_.isEqual(panels, currentPanels) || (flags && flags.fromSubmission)) { - this.redrawHeader(); + hasButton(name, nextPage = this.getNextPage()) { + // get page options with global options as default values + const { + previous = this.options.buttonSettings.showPrevious, + cancel = this.options.buttonSettings.showCancel, + submit = this.options.buttonSettings.showSubmit, + next = this.options.buttonSettings.showNext, + } = _.get(this.currentPage, 'component.buttonSettings', {}); + + switch (name) { + case 'previous': + return previous && this.getPreviousPage() > -1; + case 'next': + return next && nextPage !== null && nextPage !== -1; + case 'cancel': + return cancel && !this.options.readOnly; + case 'submit': + return ( + submit && + !this.options.readOnly && + (nextPage === null || this.page === this.pages.length - 1) + ); + default: + return true; + } } - // If the next page changes, then make sure to redraw navigation. - if (currentNextPage !== this.getNextPage()) { - this.redrawNavigation(); - } - if ( - this.options.readOnly && - (this.prefixComps.length || this.suffixComps.length) - ) { - this.redraw(); + pageId(page) { + if (page.key) { + // Some panels have the same key.... + return `${page.key}-${page.title}`; + } else if (page.components && page.components.length > 0) { + return this.pageId(page.components[0]); + } else { + return page.title; + } } - } - redraw() { - if (this.parent?.component?.modalEdit) { - return this.parent.redraw(); + onChange(flags, changed, modified, changes) { + super.onChange(flags, changed, modified, changes); + if (this.alert && !this.submitted) { + this.checkValidity(this.localData, false, this.localData, true); + this.showErrors([], true, true); + } + + // If the pages change, need to redraw the header. + let currentPanels; + let panels; + const currentNextPage = this.currentNextPage; + if (this.hasExtraPages) { + currentPanels = this.pages.map((page) => page.component.key); + this.establishPages(); + panels = this.pages.map((page) => page.component.key); + } else { + currentPanels = + this.currentPanels || + this.pages.map((page) => page.component.key); + panels = this.establishPages().map((panel) => panel.key); + this.currentPanels = panels; + if (this.currentPanel?.key && this.currentPanels?.length) { + this.setPage( + this.currentPanels.findIndex( + (panel) => panel === this.currentPanel.key, + ), + ); + } + } + + if ( + !_.isEqual(panels, currentPanels) || + (flags && flags.fromSubmission) + ) { + this.redrawHeader(); + } + + // If the next page changes, then make sure to redraw navigation. + if (currentNextPage !== this.getNextPage()) { + this.redrawNavigation(); + } + if ( + this.options.readOnly && + (this.prefixComps.length || this.suffixComps.length) + ) { + this.redraw(); + } } - return super.redraw(); - } - rebuild() { - const currentPage = this.page; - const setCurrentPage = () => this.setPage(currentPage); - return super.rebuild().then(setCurrentPage); - } + redraw() { + if (this.parent?.component?.modalEdit) { + return this.parent.redraw(); + } + return super.redraw(); + } - checkValidity(data, dirty, row, currentPageOnly) { - if (!this.checkCondition(row, data)) { - this.setCustomValidity(""); - return true; + rebuild() { + const currentPage = this.page; + const setCurrentPage = () => this.setPage(currentPage); + return super.rebuild().then(setCurrentPage); } - const components = - !currentPageOnly || this.isLastPage() - ? this.getComponents() - : this.currentPage.components; + checkValidity(data, dirty, row, currentPageOnly) { + if (!this.checkCondition(row, data)) { + this.setCustomValidity(''); + return true; + } - return components.reduce( - (check, comp) => comp.checkValidity(data, dirty, row) && check, - true, - ); - } + const components = + !currentPageOnly || this.isLastPage() + ? this.getComponents() + : this.currentPage.components; - get errors() { - if (!this.isLastPage()) { - return this.currentPage.errors; + return components.reduce( + (check, comp) => comp.checkValidity(data, dirty, row) && check, + true, + ); } - return super.errors; - } + get errors() { + if (!this.isLastPage()) { + return this.currentPage.errors; + } - focusOnComponent(key) { - let pageIndex = 0; + return super.errors; + } + + focusOnComponent(key) { + let pageIndex = 0; - const [page] = this.pages.filter((page, index) => { - let hasComponent = false; - page.getComponent(key, (comp) => { - if (comp.path === key) { - pageIndex = index; - hasComponent = true; + const [page] = this.pages.filter((page, index) => { + let hasComponent = false; + page.getComponent(key, (comp) => { + if (comp.path === key) { + pageIndex = index; + hasComponent = true; + } + }); + return hasComponent; + }); + + if (page && page !== this.currentPage) { + return this.setPage(pageIndex).then(() => { + this.checkValidity( + this.submission.data, + true, + this.submission.data, + ); + this.showErrors(); + super.focusOnComponent(key); + }); } - }); - return hasComponent; - }); - - if (page && page !== this.currentPage) { - return this.setPage(pageIndex).then(() => { - this.checkValidity(this.submission.data, true, this.submission.data); - this.showErrors(); - super.focusOnComponent(key); - }); - } - return super.focusOnComponent(key); - } + return super.focusOnComponent(key); + } } Wizard.setBaseUrl = Formio.setBaseUrl; diff --git a/src/Wizard.spec.js b/src/Wizard.spec.js index ab8c91b6be..4697d98d5e 100644 --- a/src/Wizard.spec.js +++ b/src/Wizard.spec.js @@ -4,31 +4,34 @@ import { expect } from 'chai'; import WizardTests from '../test/wizards'; import Wizard from './Wizard'; -describe('Wizard Component', function() { - describe('getPreviousPage', function() { - it('should return previous page number or zero', function() { - const { getPreviousPage } = Wizard.prototype; - expect(getPreviousPage.call({ page: 3 })).to.equal(2); - expect(getPreviousPage.call({ page: 9 })).to.equal(8); - expect(getPreviousPage.call({ page: 199 })).to.equal(198); - expect(getPreviousPage.call({ page: 1 })).to.equal(0); - expect(getPreviousPage.call({ page: 0 })).to.equal(0); +describe('Wizard Component', function () { + describe('getPreviousPage', function () { + it('should return previous page number or zero', function () { + const { getPreviousPage } = Wizard.prototype; + expect(getPreviousPage.call({ page: 3 })).to.equal(2); + expect(getPreviousPage.call({ page: 9 })).to.equal(8); + expect(getPreviousPage.call({ page: 199 })).to.equal(198); + expect(getPreviousPage.call({ page: 1 })).to.equal(0); + expect(getPreviousPage.call({ page: 0 })).to.equal(0); + }); }); - }); }); -describe('WizardRenderer tests', function() { - each(WizardTests, (wizardTest) => { - each(wizardTest.tests, (wizardTestTest, title) => { - it(title, function(done) { - const wizardElement = document.createElement('div'); - const wizard = new Wizard(wizardElement); - wizard.setForm(wizardTest.form).then(() => { - return wizardTestTest(wizard, done); - }).catch((error) => { - done(error); +describe('WizardRenderer tests', function () { + each(WizardTests, (wizardTest) => { + each(wizardTest.tests, (wizardTestTest, title) => { + it(title, function (done) { + const wizardElement = document.createElement('div'); + const wizard = new Wizard(wizardElement); + wizard + .setForm(wizardTest.form) + .then(() => { + return wizardTestTest(wizard, done); + }) + .catch((error) => { + done(error); + }); + }); }); - }); }); - }); }); diff --git a/src/Wizard.unit.js b/src/Wizard.unit.js index 7a09358682..10314d275d 100644 --- a/src/Wizard.unit.js +++ b/src/Wizard.unit.js @@ -45,2007 +45,3018 @@ global.requestAnimationFrame = (cb) => cb(); global.cancelAnimationFrame = () => {}; // eslint-disable-next-line max-statements -describe('Wizard tests', function() { - it('Should recalculate values for components with "allow override" after wizard is canceled', function(done) { - const formElement = document.createElement('div'); - Formio.createForm(formElement, formsWithAllowOverride.wizard).then((form) => { - const calculatedValues = { - number: 123, - textField: 'test data', - textArea: 'test data', - radio: 'one' - }; - - const overridenValues = { - number: 1233333, - textField: 'test data3333', - textArea: 'test data3333', - radio: 'two' - }; - - const number = form.getComponent('number'); - const textArea = form.getComponent('textArea'); - const radio = form.getComponent('radio'); - const textField = form.getComponent('textField'); - const radioTrigger = form.getComponent('radio1'); - - assert.equal(number.dataValue, number.emptyValue); - assert.equal(textField.dataValue, textField.emptyValue); - assert.equal(textArea.dataValue, textArea.emptyValue); - assert.equal(radio.dataValue, calculatedValues.radio); - - radioTrigger.setValue('a'); - setTimeout(() => { - // check if values are calculated correctly - assert.equal(number.dataValue, calculatedValues.number); - assert.equal(textField.dataValue, calculatedValues.textField); - assert.equal(textArea.dataValue, calculatedValues.textArea); - assert.equal(radio.dataValue, calculatedValues.radio); - - // override calculated values - const numberInput = number.refs.input[0]; - const textFieldInput = textField.refs.input[0]; - const textAreaInput = textArea.refs.input[0]; - const radioInput =radio.refs.input[1]; - - numberInput.value = overridenValues.number; - textFieldInput.value = overridenValues.textField; - textAreaInput.value = overridenValues.textArea; - radioInput.checked = true; - const inputEvent = new Event('input'); - const clickEvent = new Event('click'); - - numberInput.dispatchEvent(inputEvent); - textFieldInput.dispatchEvent(inputEvent); - textAreaInput.dispatchEvent(inputEvent); - radioInput.dispatchEvent(clickEvent); - - setTimeout(() => { - // check if values are overriden - assert.equal(number.getValue(), overridenValues.number); - assert.equal(textField.dataValue, overridenValues.textField); - assert.equal(textArea.dataValue, overridenValues.textArea); - assert.equal(radio.dataValue, overridenValues.radio); - // reset form - form.cancel(true); - - setTimeout(() => { - // make sure that values are reset - assert.equal(number.dataValue, number.emptyValue); - assert.equal(textField.dataValue, textField.emptyValue); - assert.equal(textArea.dataValue, textArea.emptyValue); - assert.equal(radio.dataValue, calculatedValues.radio); - - radioTrigger.setValue('a'); - setTimeout(() => { - // check if values are recalculated correctly - assert.equal(number.dataValue, calculatedValues.number); - assert.equal(textField.dataValue, calculatedValues.textField); - assert.equal(textArea.dataValue, calculatedValues.textArea); - assert.equal(radio.dataValue, calculatedValues.radio); - document.body.innerHTML = ''; - done(); - }, 300); - }, 300); - }, 300); - }, 400); - }).catch((err) => done(err)); - }); - - it('Should execute form controller', function(done) { - const form = fastCloneDeep(formWithFormController); - form.display = 'wizard'; - Formio.createForm(form).then((form) => { - setTimeout(() => { - const textField = form.getComponent('textField'); - - assert.equal(textField.getValue(), 'Hello World'); - assert.equal(textField.disabled, true); - assert.equal(form.components[0].disabled, true); - - done(); - }, 300); - }).catch((err) => done(err)); - }); - - it('Should check correctly Permissions and disabled sumbit button', function(done) { - const formElement = document.createElement('div'); - const wizard = new Wizard(formElement); - - wizard.setForm(wizardPermission).then(() => { - wizard.form.disableWizardSubmit = true; - wizard.redraw(); - const btn = wizard.element.querySelector('.btn-wizard-nav-submit'); - assert.equal(btn.disabled, true); - - done(); - }).catch(err => done(err)); - }); - - it('Should correctly reset values', function(done) { - const formElement = document.createElement('div'); - const wizard = new Wizard(formElement); - - wizard.setForm(wizardWithDataGridAndEditGrid).then(() => { - const dataGrid = wizard.getComponent('dataGrid'); - const editGrid = wizard.getComponent('editGrid'); - - const checkComponents = (editGridRowsNumber, dataGridRowsNumber, editGridValue, dataGridValue) => { - assert.equal(editGrid.editRows.length, editGridRowsNumber, `EditGrit should have ${dataGridRowsNumber} rows`); - assert.equal(editGrid.components.length, editGridRowsNumber, `EditGrit should have ${dataGridRowsNumber} components`); - assert.equal(dataGrid.rows.length, dataGridRowsNumber, `DataGrit should have ${dataGridRowsNumber} rows`); - assert.equal(dataGrid.components.length, dataGridRowsNumber, `DataGrit should have ${dataGridRowsNumber} components`); - - if (editGridValue) { - assert.deepEqual(editGrid.dataValue, editGridValue, 'Should set correct editGrid value'); - } - - if (dataGridValue) { - assert.deepEqual(dataGrid.dataValue, dataGridValue, 'Should set correct dataGrid value'); - } - }; - - const event = (name, elem) => { - const event = new Event(name); - elem.dispatchEvent(event); - }; - - checkComponents(0, 1, [], [{}]); - - const submission = { - data: { - dataGrid: [{ number: 1111 }, { number: 2222 }], - editGrid: [{ textField: 'test1' }, { textField: 'test2' }] - } - }; - - wizard.submission = _.cloneDeep(submission); - - setTimeout(() => { - checkComponents(2, 2, submission.data.editGrid,submission.data.dataGrid); - wizard.cancel(true); - - setTimeout(() => { - checkComponents(0, 1, [], [{}]); - event('click', editGrid.refs['editgrid-editGrid-addRow'][0]); - - setTimeout(() => { - const editGridFirstRowInput = editGrid.element.querySelector('[name="data[editGrid][0][textField]"]'); - editGridFirstRowInput.value = 'test row 1'; - event('input', editGridFirstRowInput); - event('click', editGrid.refs['editgrid-editGrid-saveRow'][0]); - - const dataGridFirstRowInput = dataGrid.element.querySelector('[name="data[dataGrid][0][number]"]'); - dataGridFirstRowInput.value = 11; - event('input', dataGridFirstRowInput); +describe('Wizard tests', function () { + it('Should recalculate values for components with "allow override" after wizard is canceled', function (done) { + const formElement = document.createElement('div'); + Formio.createForm(formElement, formsWithAllowOverride.wizard) + .then((form) => { + const calculatedValues = { + number: 123, + textField: 'test data', + textArea: 'test data', + radio: 'one', + }; + + const overridenValues = { + number: 1233333, + textField: 'test data3333', + textArea: 'test data3333', + radio: 'two', + }; + + const number = form.getComponent('number'); + const textArea = form.getComponent('textArea'); + const radio = form.getComponent('radio'); + const textField = form.getComponent('textField'); + const radioTrigger = form.getComponent('radio1'); + + assert.equal(number.dataValue, number.emptyValue); + assert.equal(textField.dataValue, textField.emptyValue); + assert.equal(textArea.dataValue, textArea.emptyValue); + assert.equal(radio.dataValue, calculatedValues.radio); + + radioTrigger.setValue('a'); + setTimeout(() => { + // check if values are calculated correctly + assert.equal(number.dataValue, calculatedValues.number); + assert.equal( + textField.dataValue, + calculatedValues.textField, + ); + assert.equal(textArea.dataValue, calculatedValues.textArea); + assert.equal(radio.dataValue, calculatedValues.radio); + + // override calculated values + const numberInput = number.refs.input[0]; + const textFieldInput = textField.refs.input[0]; + const textAreaInput = textArea.refs.input[0]; + const radioInput = radio.refs.input[1]; + + numberInput.value = overridenValues.number; + textFieldInput.value = overridenValues.textField; + textAreaInput.value = overridenValues.textArea; + radioInput.checked = true; + const inputEvent = new Event('input'); + const clickEvent = new Event('click'); + + numberInput.dispatchEvent(inputEvent); + textFieldInput.dispatchEvent(inputEvent); + textAreaInput.dispatchEvent(inputEvent); + radioInput.dispatchEvent(clickEvent); - setTimeout(() => { - checkComponents(1, 1, [{ textField:'test row 1' }], [{ number: 11 }]); + setTimeout(() => { + // check if values are overriden + assert.equal(number.getValue(), overridenValues.number); + assert.equal( + textField.dataValue, + overridenValues.textField, + ); + assert.equal( + textArea.dataValue, + overridenValues.textArea, + ); + assert.equal(radio.dataValue, overridenValues.radio); + // reset form + form.cancel(true); - event('click', editGrid.refs['editgrid-editGrid-addRow'][0]); - event('click', dataGrid.refs['datagrid-dataGrid-addRow'][0]); + setTimeout(() => { + // make sure that values are reset + assert.equal(number.dataValue, number.emptyValue); + assert.equal( + textField.dataValue, + textField.emptyValue, + ); + assert.equal( + textArea.dataValue, + textArea.emptyValue, + ); + assert.equal( + radio.dataValue, + calculatedValues.radio, + ); + + radioTrigger.setValue('a'); + setTimeout(() => { + // check if values are recalculated correctly + assert.equal( + number.dataValue, + calculatedValues.number, + ); + assert.equal( + textField.dataValue, + calculatedValues.textField, + ); + assert.equal( + textArea.dataValue, + calculatedValues.textArea, + ); + assert.equal( + radio.dataValue, + calculatedValues.radio, + ); + document.body.innerHTML = ''; + done(); + }, 300); + }, 300); + }, 300); + }, 400); + }) + .catch((err) => done(err)); + }); - setTimeout(() => { - const editGridFirstRowInput = editGrid.element.querySelector('[name="data[editGrid][1][textField]"]'); - editGridFirstRowInput.value = 'test row 2'; - event('input', editGridFirstRowInput); - event('click', editGrid.refs['editgrid-editGrid-saveRow'][0]); + it('Should execute form controller', function (done) { + const form = fastCloneDeep(formWithFormController); + form.display = 'wizard'; + Formio.createForm(form) + .then((form) => { + setTimeout(() => { + const textField = form.getComponent('textField'); - const dataGridFirstRowInput = dataGrid.element.querySelector('[name="data[dataGrid][1][number]"]'); - dataGridFirstRowInput.value = 22; - event('input', dataGridFirstRowInput); + assert.equal(textField.getValue(), 'Hello World'); + assert.equal(textField.disabled, true); + assert.equal(form.components[0].disabled, true); - setTimeout(() => { - const editGridValue = [{ textField:'test row 1' }, { textField:'test row 2' }]; - const dataGridValue = [{ number: 11 }, { number: 22 }]; + done(); + }, 300); + }) + .catch((err) => done(err)); + }); - checkComponents(2, 2, editGridValue, dataGridValue); + it('Should check correctly Permissions and disabled sumbit button', function (done) { + const formElement = document.createElement('div'); + const wizard = new Wizard(formElement); - assert.deepEqual(wizard.submission.data, { - dataGrid: dataGridValue, - editGrid: editGridValue - }, 'Should contain correct submission data'); + wizard + .setForm(wizardPermission) + .then(() => { + wizard.form.disableWizardSubmit = true; + wizard.redraw(); + const btn = wizard.element.querySelector( + '.btn-wizard-nav-submit', + ); + assert.equal(btn.disabled, true); - done(); - }, 200); - }, 200); - }, 200); - }, 200); - }, 200); - }, 200); - }) - .catch((err) => done(err)); - }).timeout(2500); - - it('Should render nested wizard, navigate pages and trigger validation', function(done) { - const formElement = document.createElement('div'); - const wizard = new Wizard(formElement); - const nestedWizard = _.cloneDeep(wizardTestForm.form); - - wizard.setForm(formWithNestedWizard).then(() => { - const nestedFormComp = wizard.getComponent('formNested'); - - nestedFormComp.loadSubForm = ()=> { - nestedFormComp.formObj = nestedWizard; - nestedFormComp.subFormLoading = false; - return new Promise((resolve) => resolve(nestedWizard)); - }; - - nestedFormComp.createSubForm(); - setTimeout(() => { - const clickWizardBtn = (pathPart, clickError) => { - const btn = _.get(wizard.refs, clickError ? pathPart : `${wizard.wizardKey}-${pathPart}`); - const clickEvent = new Event('click'); - btn.dispatchEvent(clickEvent); - }; + done(); + }) + .catch((err) => done(err)); + }); - const checkPage = (pageNumber) => { - assert.equal(wizard.page, pageNumber, `Should open wizard page ${pageNumber + 1}`); - }; + it('Should correctly reset values', function (done) { + const formElement = document.createElement('div'); + const wizard = new Wizard(formElement); + + wizard + .setForm(wizardWithDataGridAndEditGrid) + .then(() => { + const dataGrid = wizard.getComponent('dataGrid'); + const editGrid = wizard.getComponent('editGrid'); + + const checkComponents = ( + editGridRowsNumber, + dataGridRowsNumber, + editGridValue, + dataGridValue, + ) => { + assert.equal( + editGrid.editRows.length, + editGridRowsNumber, + `EditGrit should have ${dataGridRowsNumber} rows`, + ); + assert.equal( + editGrid.components.length, + editGridRowsNumber, + `EditGrit should have ${dataGridRowsNumber} components`, + ); + assert.equal( + dataGrid.rows.length, + dataGridRowsNumber, + `DataGrit should have ${dataGridRowsNumber} rows`, + ); + assert.equal( + dataGrid.components.length, + dataGridRowsNumber, + `DataGrit should have ${dataGridRowsNumber} components`, + ); + + if (editGridValue) { + assert.deepEqual( + editGrid.dataValue, + editGridValue, + 'Should set correct editGrid value', + ); + } + + if (dataGridValue) { + assert.deepEqual( + dataGrid.dataValue, + dataGridValue, + 'Should set correct dataGrid value', + ); + } + }; + + const event = (name, elem) => { + const event = new Event(name); + elem.dispatchEvent(event); + }; + + checkComponents(0, 1, [], [{}]); + + const submission = { + data: { + dataGrid: [{ number: 1111 }, { number: 2222 }], + editGrid: [ + { textField: 'test1' }, + { textField: 'test2' }, + ], + }, + }; + + wizard.submission = _.cloneDeep(submission); - checkPage(0); - assert.equal(wizard.pages.length, 5, 'Should have 5 pages'); - assert.equal(wizard.allPages.length, 5, 'Should have 5 pages'); - assert.equal(wizard.refs[`${wizard.wizardKey}-link`].length, 5, 'Should contain refs to breadcrumbs of parent and nested wizard'); + setTimeout(() => { + checkComponents( + 2, + 2, + submission.data.editGrid, + submission.data.dataGrid, + ); + wizard.cancel(true); - clickWizardBtn('next'); + setTimeout(() => { + checkComponents(0, 1, [], [{}]); + event( + 'click', + editGrid.refs['editgrid-editGrid-addRow'][0], + ); - setTimeout(() => { - checkPage(1); - assert.equal(wizard.refs[`${wizard.wizardKey}`].querySelectorAll('[ref="component"]').length, 1, 'Should not load nested wizard component of the page of nested form if this page contains other components'); - clickWizardBtn('next'); + setTimeout(() => { + const editGridFirstRowInput = + editGrid.element.querySelector( + '[name="data[editGrid][0][textField]"]', + ); + editGridFirstRowInput.value = 'test row 1'; + event('input', editGridFirstRowInput); + event( + 'click', + editGrid.refs['editgrid-editGrid-saveRow'][0], + ); + + const dataGridFirstRowInput = + dataGrid.element.querySelector( + '[name="data[dataGrid][0][number]"]', + ); + dataGridFirstRowInput.value = 11; + event('input', dataGridFirstRowInput); + + setTimeout(() => { + checkComponents( + 1, + 1, + [{ textField: 'test row 1' }], + [{ number: 11 }], + ); + + event( + 'click', + editGrid.refs[ + 'editgrid-editGrid-addRow' + ][0], + ); + event( + 'click', + dataGrid.refs[ + 'datagrid-dataGrid-addRow' + ][0], + ); + + setTimeout(() => { + const editGridFirstRowInput = + editGrid.element.querySelector( + '[name="data[editGrid][1][textField]"]', + ); + editGridFirstRowInput.value = 'test row 2'; + event('input', editGridFirstRowInput); + event( + 'click', + editGrid.refs[ + 'editgrid-editGrid-saveRow' + ][0], + ); + + const dataGridFirstRowInput = + dataGrid.element.querySelector( + '[name="data[dataGrid][1][number]"]', + ); + dataGridFirstRowInput.value = 22; + event('input', dataGridFirstRowInput); + + setTimeout(() => { + const editGridValue = [ + { textField: 'test row 1' }, + { textField: 'test row 2' }, + ]; + const dataGridValue = [ + { number: 11 }, + { number: 22 }, + ]; + + checkComponents( + 2, + 2, + editGridValue, + dataGridValue, + ); + + assert.deepEqual( + wizard.submission.data, + { + dataGrid: dataGridValue, + editGrid: editGridValue, + }, + 'Should contain correct submission data', + ); + + done(); + }, 200); + }, 200); + }, 200); + }, 200); + }, 200); + }, 200); + }) + .catch((err) => done(err)); + }).timeout(2500); + + it('Should render nested wizard, navigate pages and trigger validation', function (done) { + const formElement = document.createElement('div'); + const wizard = new Wizard(formElement); + const nestedWizard = _.cloneDeep(wizardTestForm.form); + + wizard + .setForm(formWithNestedWizard) + .then(() => { + const nestedFormComp = wizard.getComponent('formNested'); + + nestedFormComp.loadSubForm = () => { + nestedFormComp.formObj = nestedWizard; + nestedFormComp.subFormLoading = false; + return new Promise((resolve) => resolve(nestedWizard)); + }; + + nestedFormComp.createSubForm(); + setTimeout(() => { + const clickWizardBtn = (pathPart, clickError) => { + const btn = _.get( + wizard.refs, + clickError + ? pathPart + : `${wizard.wizardKey}-${pathPart}`, + ); + const clickEvent = new Event('click'); + btn.dispatchEvent(clickEvent); + }; + + const checkPage = (pageNumber) => { + assert.equal( + wizard.page, + pageNumber, + `Should open wizard page ${pageNumber + 1}`, + ); + }; - setTimeout(() => { - checkPage(2); - assert.equal(wizard.refs[`${wizard.wizardKey}`].querySelectorAll('[ref="component"]').length, 4, 'Should render nested wizard first page components'); + checkPage(0); + assert.equal(wizard.pages.length, 5, 'Should have 5 pages'); + assert.equal( + wizard.allPages.length, + 5, + 'Should have 5 pages', + ); + assert.equal( + wizard.refs[`${wizard.wizardKey}-link`].length, + 5, + 'Should contain refs to breadcrumbs of parent and nested wizard', + ); + + clickWizardBtn('next'); - clickWizardBtn('next'); + setTimeout(() => { + checkPage(1); + assert.equal( + wizard.refs[`${wizard.wizardKey}`].querySelectorAll( + '[ref="component"]', + ).length, + 1, + 'Should not load nested wizard component of the page of nested form if this page contains other components', + ); + clickWizardBtn('next'); - setTimeout(() => { - checkPage(2); - assert.equal(wizard.errors.length, 1, 'Should show validation error for required field'); - assert.equal(wizard.refs.errorRef.length, 1, 'Should show alert with error'); - clickWizardBtn('previous'); + setTimeout(() => { + checkPage(2); + assert.equal( + wizard.refs[ + `${wizard.wizardKey}` + ].querySelectorAll('[ref="component"]').length, + 4, + 'Should render nested wizard first page components', + ); + + clickWizardBtn('next'); + + setTimeout(() => { + checkPage(2); + assert.equal( + wizard.errors.length, + 1, + 'Should show validation error for required field', + ); + assert.equal( + wizard.refs.errorRef.length, + 1, + 'Should show alert with error', + ); + clickWizardBtn('previous'); + + setTimeout(() => { + checkPage(1); + assert.equal( + wizard.errors.length, + 0, + 'Should not have validation errors', + ); + + clickWizardBtn('link[4]'); + + setTimeout(() => { + checkPage(4); + assert.equal( + !!wizard.refs[ + `${wizard.wizardKey}-submit` + ], + true, + 'Should have submit btn on the last page', + ); + clickWizardBtn('submit'); + + setTimeout(() => { + checkPage(4); + assert.equal( + wizard.errors.length, + 3, + 'Should trigger validation errors on submit', + ); + assert.equal( + wizard.refs.errorRef.length, + 3, + 'Should show alert with error on submit', + ); + wizard + .getComponent('select') + .setValue('value1'); + setTimeout(() => { + checkPage(4); + assert.equal( + wizard.errors.length, + 2, + 'Should remove validation error if a component is valid', + ); + assert.equal( + wizard.refs.errorRef.length, + 2, + 'Should remove error from alert if component is valid', + ); + + done(); + }, 500); + }, 500); + }, 200); + }, 200); + }, 200); + }, 200); + }, 200); + }, 200); + }) + .catch((err) => done(err)); + }).timeout(3000); + + it('Should set submission in wizard with nested wizard', function (done) { + const formElement = document.createElement('div'); + const wizard = new Wizard(formElement); + const nestedWizard = _.cloneDeep(wizardTestForm.form); + const submission = { + data: { + selectBoxesParent: { + a: true, + b: false, + c: false, + }, + formNested: { + data: wizardTestForm.submission.data, + }, + numberParent: 1111, + }, + }; - setTimeout(() => { - checkPage(1); - assert.equal(wizard.errors.length, 0, 'Should not have validation errors'); + wizard + .setForm(formWithNestedWizard) + .then(() => { + const nestedFormComp = wizard.getComponent('formNested'); - clickWizardBtn('link[4]'); + nestedFormComp.loadSubForm = () => { + nestedFormComp.formObj = nestedWizard; + nestedFormComp.subFormLoading = false; + return new Promise((resolve) => resolve(nestedWizard)); + }; - setTimeout(() => { - checkPage(4); - assert.equal(!!wizard.refs[`${wizard.wizardKey}-submit`], true, 'Should have submit btn on the last page'); - clickWizardBtn('submit'); + nestedFormComp.createSubForm(); setTimeout(() => { - checkPage(4); - assert.equal(wizard.errors.length, 3, 'Should trigger validation errors on submit'); - assert.equal(wizard.refs.errorRef.length, 3, 'Should show alert with error on submit'); - wizard.getComponent('select').setValue('value1'); - setTimeout(() => { - checkPage(4); - assert.equal(wizard.errors.length, 2, 'Should remove validation error if a component is valid'); - assert.equal(wizard.refs.errorRef.length, 2, 'Should remove error from alert if component is valid'); + wizard.submission = _.cloneDeep(submission); - done(); - }, 500); - }, 500); - }, 200); - }, 200); - }, 200); - }, 200); - }, 200); - }, 200); - }) - .catch((err) => done(err)); - }).timeout(3000); - - it('Should set submission in wizard with nested wizard', function(done) { - const formElement = document.createElement('div'); - const wizard = new Wizard(formElement); - const nestedWizard = _.cloneDeep(wizardTestForm.form); - const submission = { - data: { - selectBoxesParent: { - a: true, - b: false, - c: false - }, - formNested: { - data: wizardTestForm.submission.data - }, - numberParent: 1111 - } - }; - - wizard.setForm(formWithNestedWizard).then(() => { - const nestedFormComp = wizard.getComponent('formNested'); - - nestedFormComp.loadSubForm = ()=> { - nestedFormComp.formObj = nestedWizard; - nestedFormComp.subFormLoading = false; - return new Promise((resolve) => resolve(nestedWizard)); - }; - - nestedFormComp.createSubForm(); - - setTimeout(() => { - wizard.submission = _.cloneDeep(submission); - - setTimeout(() => { - assert.deepEqual(wizard.data, submission.data, 'Should set wizard submission'); - assert.deepEqual(wizard.submission.data, submission.data, 'Should get wizard submission data'); - - wizard.everyComponent((comp) => { - const expectedValue = _.get(submission.data, comp.path, 'no data'); - - if (expectedValue !== 'no data') { - assert.deepEqual(comp.getValue(), expectedValue, `Should set value for ${comp.component.type} inside wizard`); - assert.deepEqual(comp.dataValue, expectedValue, `Should set value for ${comp.component.type} inside wizard`); - } - }); + setTimeout(() => { + assert.deepEqual( + wizard.data, + submission.data, + 'Should set wizard submission', + ); + assert.deepEqual( + wizard.submission.data, + submission.data, + 'Should get wizard submission data', + ); + + wizard.everyComponent((comp) => { + const expectedValue = _.get( + submission.data, + comp.path, + 'no data', + ); + + if (expectedValue !== 'no data') { + assert.deepEqual( + comp.getValue(), + expectedValue, + `Should set value for ${comp.component.type} inside wizard`, + ); + assert.deepEqual( + comp.dataValue, + expectedValue, + `Should set value for ${comp.component.type} inside wizard`, + ); + } + }); + + done(); + }, 300); + }, 300); + }) + .catch((err) => done(err)); + }); - done(); - }, 300); - }, 300); - }) - .catch((err) => done(err)); - }); - - it('Should show conditional page inside nested wizard', function(done) { - const formElement = document.createElement('div'); - const wizard = new Wizard(formElement); - const nestedWizard = _.cloneDeep(wizardTestForm.form); - nestedWizard.components[2].conditional = { show: true, when: 'checkbox', eq: 'true' }; - - wizard.setForm(formWithNestedWizard).then(() => { - const nestedFormComp = wizard.getComponent('formNested'); - - nestedFormComp.loadSubForm = ()=> { - nestedFormComp.formObj = nestedWizard; - nestedFormComp.subFormLoading = false; - return new Promise((resolve) => resolve(nestedWizard)); - }; - - nestedFormComp.createSubForm(); - - setTimeout(() => { - const checkPage = (pageNumber) => { - assert.equal(wizard.page, pageNumber, `Should open wizard page ${pageNumber + 1}`); - }; - - const clickWizardBtn = (pathPart, clickError) => { - const btn = _.get(wizard.refs, clickError ? pathPart : `${wizard.wizardKey}-${pathPart}`); - const clickEvent = new Event('click'); - btn.dispatchEvent(clickEvent); - }; + it('Should show conditional page inside nested wizard', function (done) { + const formElement = document.createElement('div'); + const wizard = new Wizard(formElement); + const nestedWizard = _.cloneDeep(wizardTestForm.form); + nestedWizard.components[2].conditional = { + show: true, + when: 'checkbox', + eq: 'true', + }; - checkPage(0); - assert.equal(wizard.pages.length, 4, 'Should have 4 pages'); - assert.equal(wizard.allPages.length, 4, 'Should have 4 pages'); - assert.equal(wizard.refs[`${wizard.wizardKey}-link`].length, 4, 'Should contain refs to breadcrumbs of parent and nested wizard'); + wizard + .setForm(formWithNestedWizard) + .then(() => { + const nestedFormComp = wizard.getComponent('formNested'); - clickWizardBtn('link[3]'); + nestedFormComp.loadSubForm = () => { + nestedFormComp.formObj = nestedWizard; + nestedFormComp.subFormLoading = false; + return new Promise((resolve) => resolve(nestedWizard)); + }; - setTimeout(() => { - checkPage(3); + nestedFormComp.createSubForm(); - assert.deepEqual(!!wizard.refs[`${wizard.wizardKey}-submit`], true, 'Should hav submit btn on the last page'); - wizard.getComponent('checkbox').setValue(true); + setTimeout(() => { + const checkPage = (pageNumber) => { + assert.equal( + wizard.page, + pageNumber, + `Should open wizard page ${pageNumber + 1}`, + ); + }; + + const clickWizardBtn = (pathPart, clickError) => { + const btn = _.get( + wizard.refs, + clickError + ? pathPart + : `${wizard.wizardKey}-${pathPart}`, + ); + const clickEvent = new Event('click'); + btn.dispatchEvent(clickEvent); + }; - setTimeout(() => { - checkPage(3); + checkPage(0); + assert.equal(wizard.pages.length, 4, 'Should have 4 pages'); + assert.equal( + wizard.allPages.length, + 4, + 'Should have 4 pages', + ); + assert.equal( + wizard.refs[`${wizard.wizardKey}-link`].length, + 4, + 'Should contain refs to breadcrumbs of parent and nested wizard', + ); + + clickWizardBtn('link[3]'); - assert.deepEqual(!!wizard.refs[`${wizard.wizardKey}-submit`], true, 'Should have submit btn on the last page'); - wizard.getComponent('checkbox').setValue(true); + setTimeout(() => { + checkPage(3); - setTimeout(() => { - checkPage(3); - assert.deepEqual(!!wizard.refs[`${wizard.wizardKey}-submit`], false, 'Should not have submit btn '); - assert.equal(wizard.pages.length, 5, 'Should show conditional page'); - assert.equal(wizard.allPages.length, 5, 'Should show conditional page'); - assert.equal(wizard.refs[`${wizard.wizardKey}-link`].length, 5, 'Should contain refs to breadcrumbs of visible conditional page'); + assert.deepEqual( + !!wizard.refs[`${wizard.wizardKey}-submit`], + true, + 'Should hav submit btn on the last page', + ); + wizard.getComponent('checkbox').setValue(true); - clickWizardBtn('next'); + setTimeout(() => { + checkPage(3); + + assert.deepEqual( + !!wizard.refs[`${wizard.wizardKey}-submit`], + true, + 'Should have submit btn on the last page', + ); + wizard.getComponent('checkbox').setValue(true); + + setTimeout(() => { + checkPage(3); + assert.deepEqual( + !!wizard.refs[`${wizard.wizardKey}-submit`], + false, + 'Should not have submit btn ', + ); + assert.equal( + wizard.pages.length, + 5, + 'Should show conditional page', + ); + assert.equal( + wizard.allPages.length, + 5, + 'Should show conditional page', + ); + assert.equal( + wizard.refs[`${wizard.wizardKey}-link`] + .length, + 5, + 'Should contain refs to breadcrumbs of visible conditional page', + ); + + clickWizardBtn('next'); + + setTimeout(() => { + checkPage(4); + clickWizardBtn('previous'); + + setTimeout(() => { + checkPage(3); + wizard + .getComponent('checkbox') + .setValue(false); + + setTimeout(() => { + assert.equal( + wizard.pages.length, + 4, + 'Should hide conditional page', + ); + assert.equal( + wizard.allPages.length, + 4, + 'Should hide conditional page', + ); + assert.equal( + wizard.refs[ + `${wizard.wizardKey}-link` + ].length, + 4, + 'Should contain refs to breadcrumbs of visible pages', + ); + assert.deepEqual( + !!wizard.refs[ + `${wizard.wizardKey}-submit` + ], + true, + 'Should have submit btn on the last page', + ); + + done(); + }, 500); + }, 300); + }, 300); + }, 500); + }, 300); + }, 300); + }, 300); + }) + .catch((err) => done(err)); + }).timeout(3000); + + it('Should render values in HTML render mode', function (done) { + const formElement = document.createElement('div'); + const wizard = new Wizard(formElement, { + readOnly: true, + renderMode: 'html', + }); + const form = _.cloneDeep(wizardTestForm.form); + + wizard + .setForm(form) + .then(() => { + const clickWizardBtn = (pathPart, clickError) => { + const btn = _.get( + wizard.refs, + clickError + ? pathPart + : `${wizard.wizardKey}-${pathPart}`, + ); + const clickEvent = new Event('click'); + btn.dispatchEvent(clickEvent); + }; + + const checkPage = (pageNumber) => { + assert.equal( + wizard.page, + pageNumber, + `Should open wizard page ${pageNumber + 1}`, + ); + }; + + const checkValues = () => { + wizard.allPages[wizard.page].everyComponent((comp) => { + const isParent = !!( + comp.component.components || + comp.component.rows || + comp.component.columns + ); + + if (!isParent) { + const isInEditGrid = + comp.parent.component.type === 'editgrid'; + const value = isInEditGrid + ? comp.parent.refs['editgrid-editGrid-row'][ + comp.rowIndex + ].textContent.trim() + : comp.element.querySelector("[ref='value']") + .textContent; + const expectedValue = _.get( + wizardTestForm.htmlModeValues, + comp.path, + 'no data', + ); + + assert.equal( + value, + expectedValue === 'true' + ? 'True' + : expectedValue, + `${comp.component.key}: should render value in html render mode`, + ); + } + }); + }; + + wizard.submission = _.cloneDeep(wizardTestForm.submission); - setTimeout(() => { - checkPage(4); - clickWizardBtn('previous'); + setTimeout(() => { + checkPage(0); + checkValues(); + clickWizardBtn('next'); - setTimeout(() => { - checkPage(3); - wizard.getComponent('checkbox').setValue(false); + setTimeout(() => { + checkPage(1); + checkValues(); + clickWizardBtn('next'); - setTimeout(() => { - assert.equal(wizard.pages.length, 4, 'Should hide conditional page'); - assert.equal(wizard.allPages.length, 4, 'Should hide conditional page'); - assert.equal(wizard.refs[`${wizard.wizardKey}-link`].length, 4, 'Should contain refs to breadcrumbs of visible pages'); - assert.deepEqual(!!wizard.refs[`${wizard.wizardKey}-submit`], true, 'Should have submit btn on the last page'); - - done(); - }, 500); - }, 300); - }, 300); - }, 500); - }, 300); - }, 300); - }, 300); - }) - .catch((err) => done(err)); - }).timeout(3000); - - it('Should render values in HTML render mode', function(done) { - const formElement = document.createElement('div'); - const wizard = new Wizard(formElement, { - readOnly: true, - renderMode: 'html' + setTimeout(() => { + checkPage(2); + checkValues(); + done(); + }, 200); + }, 200); + }, 200); + }) + .catch((err) => done(err)); }); - const form = _.cloneDeep(wizardTestForm.form); - - wizard.setForm(form, ).then(() => { - const clickWizardBtn = (pathPart, clickError) => { - const btn = _.get(wizard.refs, clickError ? pathPart : `${wizard.wizardKey}-${pathPart}`); - const clickEvent = new Event('click'); - btn.dispatchEvent(clickEvent); - }; - const checkPage = (pageNumber) => { - assert.equal(wizard.page, pageNumber, `Should open wizard page ${pageNumber + 1}`); - }; + it('Should render values for prefix Components', function (done) { + const formElement = document.createElement('div'); + const wizard = new Wizard(formElement, { + readOnly: true, + }); + const form = _.cloneDeep(wizardWithPrefixComps.form); + + wizard + .setForm(form) + .then(() => { + const clickWizardBtn = (pathPart, clickError) => { + const btn = _.get( + wizard.refs, + clickError + ? pathPart + : `${wizard.wizardKey}-${pathPart}`, + ); + const clickEvent = new Event('click'); + btn.dispatchEvent(clickEvent); + }; + + const checkPage = (pageNumber) => { + assert.equal( + wizard.page, + pageNumber, + `Should open wizard page ${pageNumber + 1}`, + ); + }; + + const checkValues = () => { + wizard.refs[`wizard-${wizard.id}`] + .querySelectorAll('input') + .forEach((element, i) => { + switch (i) { + case 0: + assert.equal( + element.value, + 'prefix', + 'Should render value', + ); + break; + case 1: + assert.equal( + element.value, + `page${wizard.page + 1}`, + 'Should render value', + ); + break; + case 2: + assert.equal( + element.value, + 'suffix', + 'Should render value', + ); + break; + } + }); + }; + wizard.submission = _.cloneDeep( + wizardWithPrefixComps.submission, + ); - const checkValues = () => { - wizard.allPages[wizard.page].everyComponent((comp) => { - const isParent = !!(comp.component.components || comp.component.rows || comp.component.columns); + setTimeout(() => { + checkPage(0); + checkValues(); + clickWizardBtn('next'); - if (!isParent) { - const isInEditGrid = comp.parent.component.type === 'editgrid'; - const value = isInEditGrid ? comp.parent.refs['editgrid-editGrid-row'][comp.rowIndex].textContent.trim() : comp.element.querySelector("[ref='value']").textContent; - const expectedValue = _.get(wizardTestForm.htmlModeValues, comp.path, 'no data'); + setTimeout(() => { + checkPage(1); + checkValues(); + done(); + }, 200); + }, 200); + }) + .catch((err) => done(err)); + }); - assert.equal(value, expectedValue === 'true' ? 'True' : expectedValue, `${comp.component.key}: should render value in html render mode`); - } + it('Should redirect to the correct page from the Error list', function (done) { + const formElement = document.createElement('div'); + const wizard = new Wizard(formElement, { + renderMode: 'html', }); - }; - wizard.submission = _.cloneDeep(wizardTestForm.submission); + wizard + .setForm(wizardWithComponentsWithSameApi) + .then(() => { + const clickWizardBtn = (pathPart) => { + const [btnKey] = Object.keys(wizard.refs).filter( + (key) => key.indexOf(pathPart) !== -1, + ); + const btn = _.get(wizard.refs, btnKey); + const clickEvent = new Event('click'); + btn.dispatchEvent(clickEvent); + }; + + const checkPage = (pageNumber) => { + assert.equal( + wizard.page, + pageNumber, + `Should open wizard page ${pageNumber + 1}`, + ); + }; - setTimeout(() => { - checkPage(0); - checkValues(); - clickWizardBtn('next'); + setTimeout(() => { + checkPage(0); + wizard.setPage(1); - setTimeout(() => { - checkPage(1); - checkValues(); - clickWizardBtn('next'); + setTimeout(() => { + checkPage(1); + clickWizardBtn('submit'); - setTimeout(() => { - checkPage(2); - checkValues(); - done(); - }, 200); - }, 200); - }, 200); - }) - .catch((err) => done(err)); - }); - - it('Should render values for prefix Components', function(done) { - const formElement = document.createElement('div'); - const wizard = new Wizard(formElement, { - readOnly: true, + setTimeout(() => { + assert.equal( + wizard.refs.errorRef.length, + 1, + 'Should have an error', + ); + const clickEvent = new Event('click'); + wizard.refs.errorRef[0].dispatchEvent(clickEvent); + + setTimeout(() => { + checkPage(0); + done(); + }, 200); + }, 200); + }, 300); + }, 200); + }) + .catch((err) => done(err)); }); - const form = _.cloneDeep(wizardWithPrefixComps.form); - wizard.setForm(form).then(() => { - const clickWizardBtn = (pathPart, clickError) => { - const btn = _.get(wizard.refs, clickError ? pathPart : `${wizard.wizardKey}-${pathPart}`); - const clickEvent = new Event('click'); - btn.dispatchEvent(clickEvent); - }; - - const checkPage = (pageNumber) => { - assert.equal(wizard.page, pageNumber, `Should open wizard page ${pageNumber + 1}`); - }; - - const checkValues = () => { - wizard.refs[`wizard-${wizard.id}`].querySelectorAll('input').forEach((element, i)=> { - switch (i) { - case 0: - assert.equal(element.value, 'prefix', 'Should render value'); - break; - case 1: - assert.equal(element.value, `page${wizard.page+1}`, 'Should render value'); - break; - case 2: - assert.equal(element.value, 'suffix', 'Should render value'); - break; - } + it('Should execute advanced logic for wizard pages', function (done) { + const formElement = document.createElement('div'); + const wizard = new Wizard(formElement); + const form = _.cloneDeep(wizardTestForm.form); + _.each(form.components, (comp, index) => { + if (index === 1) { + comp.logic = [ + { + name: 'simple logic', + trigger: { + type: 'simple', + simple: { + show: true, + when: 'textField', + eq: 'tooltip', + }, + }, + actions: [ + { + name: 'merge schema action', + type: 'mergeComponentSchema', + schemaDefinition: + "schema = { tooltip: 'some tooltip'}", + }, + ], + }, + ]; + } + if (index === 2) { + comp.logic = [ + { + name: 'logic test', + trigger: { + type: 'simple', + simple: { + show: true, + when: 'checkbox', + eq: 'true', + }, + }, + actions: [ + { + name: 'disabled', + type: 'property', + property: { + label: 'Disabled', + value: 'disabled', + type: 'boolean', + }, + state: true, + }, + ], + }, + ]; + } }); - }; - wizard.submission = _.cloneDeep(wizardWithPrefixComps.submission); - - setTimeout(() => { - checkPage(0); - checkValues(); - clickWizardBtn('next'); - - setTimeout(() => { - checkPage(1); - checkValues(); - done(); - }, 200); - }, 200); - }) - .catch((err) => done(err)); - }); - - it('Should redirect to the correct page from the Error list', function(done) { - const formElement = document.createElement('div'); - const wizard = new Wizard(formElement, { - renderMode: 'html' + + wizard + .setForm(form) + .then(() => { + const clickWizardBtn = (pathPart, clickError) => { + const btn = _.get( + wizard.refs, + clickError + ? pathPart + : `${wizard.wizardKey}-${pathPart}`, + ); + const clickEvent = new Event('click'); + btn.dispatchEvent(clickEvent); + }; + + const checkPage = (pageNumber) => { + assert.equal( + wizard.page, + pageNumber, + `Should open wizard page ${pageNumber + 1}`, + ); + }; + + checkPage(0); + wizard.getComponent('textField').setValue('tooltip'); + clickWizardBtn('next'); + + setTimeout(() => { + checkPage(1); + assert.equal( + wizard.tooltips.length, + 1, + 'Should have tooltip after advanced logic execution', + ); + assert.equal( + !!wizard.refs[`${wizard.wizardKey}-tooltip`][0], + true, + 'Should render tooltip icon', + ); + + wizard.getComponent('checkbox').setValue(true); + clickWizardBtn('next'); + + setTimeout(() => { + checkPage(2); + assert.equal( + wizard.allPages[wizard.page].disabled, + true, + 'Should disable page components after advanced logic execution', + ); + done(); + }, 200); + }, 200); + }) + .catch((err) => done(err)); }); - wizard.setForm(wizardWithComponentsWithSameApi, ).then(() => { - const clickWizardBtn = (pathPart) => { - const [btnKey] = Object.keys(wizard.refs).filter((key) => key.indexOf(pathPart) !== -1); - const btn = _.get(wizard.refs, btnKey); - const clickEvent = new Event('click'); - btn.dispatchEvent(clickEvent); - }; + it('Should navigate next page according to advanced next page logic', function (done) { + const formElement = document.createElement('div'); + const wizard = new Wizard(formElement); + const form = _.cloneDeep(wizardTestForm.form); + _.each(form.components, (comp, index) => { + if (index === 0) { + comp.nextPage = + "next = data.textField === 'page3' ? 'page3' : 'page2'"; + } + if (index === 1) { + comp.nextPage = + "next = data.container && data.container.select === 'value1' ? 'page1' : 'page3'"; + } + }); - const checkPage = (pageNumber) => { - assert.equal(wizard.page, pageNumber, `Should open wizard page ${pageNumber + 1}`); - }; + wizard + .setForm(form) + .then(() => { + const clickWizardBtn = (pathPart, clickError) => { + const btn = _.get( + wizard.refs, + clickError + ? pathPart + : `${wizard.wizardKey}-${pathPart}`, + ); + const clickEvent = new Event('click'); + btn.dispatchEvent(clickEvent); + }; + + const checkPage = (pageNumber) => { + assert.equal( + wizard.page, + pageNumber, + `Should open wizard page ${pageNumber + 1}`, + ); + }; + checkPage(0); + wizard.getComponent('textField').setValue('page3'); + clickWizardBtn('next'); - setTimeout(() => { - checkPage(0); - wizard.setPage(1); + setTimeout(() => { + checkPage(2); + wizard.getComponent('select').setValue('value1'); + clickWizardBtn('previous'); - setTimeout(() => { - checkPage(1); - clickWizardBtn('submit'); + setTimeout(() => { + checkPage(1); + wizard.getComponent('checkbox').setValue(true); + clickWizardBtn('next'); - setTimeout(() => { - assert.equal(wizard.refs.errorRef.length, 1, 'Should have an error'); - const clickEvent = new Event('click'); - wizard.refs.errorRef[0].dispatchEvent(clickEvent); + setTimeout(() => { + checkPage(0); + done(); + }, 200); + }, 200); + }, 200); + }) + .catch((err) => done(err)); + }); - setTimeout(() => { - checkPage(0); - done(); - }, 200); - }, 200); - }, 300); - }, 200); - }) - .catch((err) => done(err)); - }); - - it('Should execute advanced logic for wizard pages', function(done) { - const formElement = document.createElement('div'); - const wizard = new Wizard(formElement); - const form = _.cloneDeep(wizardTestForm.form); - _.each(form.components, (comp, index) => { - if (index === 1) { - comp.logic = [ - { - name: 'simple logic', - trigger: { type: 'simple', simple: { show: true, when: 'textField', eq: 'tooltip' } }, - actions: [ - { - name: 'merge schema action', - type: 'mergeComponentSchema', - schemaDefinition: "schema = { tooltip: 'some tooltip'}" - } - ] - } - ]; - } - if (index === 2) { - comp.logic = [ - { - name: 'logic test', - trigger: { type: 'simple', simple: { show: true, when: 'checkbox', eq: 'true' } }, - actions: [ - { - name: 'disabled', - type: 'property', - property: { label: 'Disabled', value: 'disabled', type: 'boolean' }, - state: true - } - ] - } - ]; - } - }); - - wizard.setForm(form).then(() => { - const clickWizardBtn = (pathPart, clickError) => { - const btn = _.get(wizard.refs, clickError ? pathPart : `${wizard.wizardKey}-${pathPart}`); - const clickEvent = new Event('click'); - btn.dispatchEvent(clickEvent); - }; - - const checkPage = (pageNumber) => { - assert.equal(wizard.page, pageNumber, `Should open wizard page ${pageNumber + 1}`); - }; - - checkPage(0); - wizard.getComponent('textField').setValue('tooltip'); - clickWizardBtn('next'); - - setTimeout(() => { - checkPage(1); - assert.equal(wizard.tooltips.length, 1, 'Should have tooltip after advanced logic execution'); - assert.equal(!!wizard.refs[`${wizard.wizardKey}-tooltip`][0], true, 'Should render tooltip icon'); - - wizard.getComponent('checkbox').setValue(true); - clickWizardBtn('next'); - - setTimeout(() => { - checkPage(2); - assert.equal(wizard.allPages[wizard.page].disabled, true, 'Should disable page components after advanced logic execution'); - done(); - }, 200); - }, 200); - }) - .catch((err) => done(err)); - }); - - it('Should navigate next page according to advanced next page logic', function(done) { - const formElement = document.createElement('div'); - const wizard = new Wizard(formElement); - const form = _.cloneDeep(wizardTestForm.form); - _.each(form.components, (comp, index) => { - if (index === 0) { - comp.nextPage = "next = data.textField === 'page3' ? 'page3' : 'page2'"; - } - if (index === 1) { - comp.nextPage = "next = data.container && data.container.select === 'value1' ? 'page1' : 'page3'"; - } - }); - - wizard.setForm(form).then(() => { - const clickWizardBtn = (pathPart, clickError) => { - const btn = _.get(wizard.refs, clickError ? pathPart : `${wizard.wizardKey}-${pathPart}`); - const clickEvent = new Event('click'); - btn.dispatchEvent(clickEvent); - }; - - const checkPage = (pageNumber) => { - assert.equal(wizard.page, pageNumber, `Should open wizard page ${pageNumber + 1}`); - }; - checkPage(0); - wizard.getComponent('textField').setValue('page3'); - clickWizardBtn('next'); - - setTimeout(() => { - checkPage(2); - wizard.getComponent('select').setValue('value1'); - clickWizardBtn('previous'); - - setTimeout(() => { - checkPage(1); - wizard.getComponent('checkbox').setValue(true); - clickWizardBtn('next'); - - setTimeout(() => { - checkPage(0); - done(); - }, 200); - }, 200); - }, 200); - }) - .catch((err) => done(err)); - }); - - it('Should NOT navigate to next page if it contains invalid nested component', function(done) { - const formElement = document.createElement('div'); - const wizard = new Wizard(formElement); - const form = _.cloneDeep(wizardTestFormWithNestedComponents.form); - - wizard.setForm(form).then(() => { - const checkPage = (pageNumber) => { - assert.equal(wizard.page, pageNumber, `Should open wizard page ${pageNumber + 1}`); - }; - checkPage(0); - wizard.submission = { - data: { - outerContainer: { - firstComponent: 'c', - secondComponent: 'q', - } - } - }; - wizard.nextPage(); - setTimeout(() => { - const errors = wizard.errors; - checkPage(0); - assert(errors.length > 0, 'Must err before next page'); - assert.equal(errors[0].message, 'Required Component is required'); - done(); - }, 200); - }) - .catch((err) => done(err)); - }); - - it('Should not render breadcrumb if it has hidden type', function(done) { - const formElement = document.createElement('div'); - const wizard = new Wizard(formElement); - const form = _.cloneDeep(wizardTestForm.form); - _.each(form.components, (comp) => { - comp.breadcrumb = 'none'; + it('Should NOT navigate to next page if it contains invalid nested component', function (done) { + const formElement = document.createElement('div'); + const wizard = new Wizard(formElement); + const form = _.cloneDeep(wizardTestFormWithNestedComponents.form); + + wizard + .setForm(form) + .then(() => { + const checkPage = (pageNumber) => { + assert.equal( + wizard.page, + pageNumber, + `Should open wizard page ${pageNumber + 1}`, + ); + }; + checkPage(0); + wizard.submission = { + data: { + outerContainer: { + firstComponent: 'c', + secondComponent: 'q', + }, + }, + }; + wizard.nextPage(); + setTimeout(() => { + const errors = wizard.errors; + checkPage(0); + assert(errors.length > 0, 'Must err before next page'); + assert.equal( + errors[0].message, + 'Required Component is required', + ); + done(); + }, 200); + }) + .catch((err) => done(err)); }); - wizard.setForm(form).then(() => { - const clickWizardBtn = (pathPart, clickError) => { - const btn = _.get(wizard.refs, clickError ? pathPart : `${wizard.wizardKey}-${pathPart}`); - const clickEvent = new Event('click'); - btn.dispatchEvent(clickEvent); - }; + it('Should not render breadcrumb if it has hidden type', function (done) { + const formElement = document.createElement('div'); + const wizard = new Wizard(formElement); + const form = _.cloneDeep(wizardTestForm.form); + _.each(form.components, (comp) => { + comp.breadcrumb = 'none'; + }); - const checkPage = (pageNumber) => { - assert.equal(wizard.page, pageNumber, `Should open wizard page ${pageNumber + 1}`); - }; + wizard + .setForm(form) + .then(() => { + const clickWizardBtn = (pathPart, clickError) => { + const btn = _.get( + wizard.refs, + clickError + ? pathPart + : `${wizard.wizardKey}-${pathPart}`, + ); + const clickEvent = new Event('click'); + btn.dispatchEvent(clickEvent); + }; + + const checkPage = (pageNumber) => { + assert.equal( + wizard.page, + pageNumber, + `Should open wizard page ${pageNumber + 1}`, + ); + }; + + const checkBreadcrumb = () => { + assert.equal( + _.get(wizard.refs, `${wizard.wizardKey}-link`).length, + 0, + 'Should not render wizard breadcrumb', + ); + }; + + checkBreadcrumb(); + wizard.setSubmission(_.cloneDeep(wizardTestForm.submission)); - const checkBreadcrumb = () => { - assert.equal(_.get(wizard.refs, `${wizard.wizardKey}-link`).length, 0, 'Should not render wizard breadcrumb'); - }; + setTimeout(() => { + checkPage(0); + checkBreadcrumb(); + clickWizardBtn('next'); - checkBreadcrumb(); - wizard.setSubmission(_.cloneDeep(wizardTestForm.submission)); + setTimeout(() => { + checkPage(1); + checkBreadcrumb(); + clickWizardBtn('next'); - setTimeout(() => { - checkPage(0); - checkBreadcrumb(); - clickWizardBtn('next'); + setTimeout(() => { + checkPage(2); + checkBreadcrumb(); + done(); + }, 100); + }, 100); + }, 100); + }) + .catch((err) => done(err)); + }); - setTimeout(() => { - checkPage(1); - checkBreadcrumb(); - clickWizardBtn('next'); + it('Should not navigate between wizard pages on breadcrumb click if breadcrumbClickable is false', function (done) { + const formElement = document.createElement('div'); + const wizard = new Wizard(formElement); + const form = _.cloneDeep(wizardTestForm.form); + _.each(form.components, (comp) => { + comp.breadcrumbClickable = false; + }); - setTimeout(() => { - checkPage(2); - checkBreadcrumb(); - done(); - }, 100); - }, 100); - }, 100); - }) - .catch((err) => done(err)); - }); - - it('Should not navigate between wizard pages on breadcrumb click if breadcrumbClickable is false', function(done) { - const formElement = document.createElement('div'); - const wizard = new Wizard(formElement); - const form = _.cloneDeep(wizardTestForm.form); - _.each(form.components, (comp) => { - comp.breadcrumbClickable = false; - }); + wizard + .setForm(form) + .then(() => { + const clickWizardBtn = (pathPart, clickError) => { + const btn = _.get( + wizard.refs, + clickError + ? pathPart + : `${wizard.wizardKey}-${pathPart}`, + ); + const clickEvent = new Event('click'); + btn.dispatchEvent(clickEvent); + }; + + const checkPage = (pageNumber) => { + assert.equal( + wizard.page, + pageNumber, + `Should open wizard page ${pageNumber + 1}`, + ); + }; + + checkPage(0); + clickWizardBtn('link[1]'); - wizard.setForm(form).then(() => { - const clickWizardBtn = (pathPart, clickError) => { - const btn = _.get(wizard.refs, clickError ? pathPart : `${wizard.wizardKey}-${pathPart}`); - const clickEvent = new Event('click'); - btn.dispatchEvent(clickEvent); - }; + setTimeout(() => { + checkPage(0); + clickWizardBtn('link[2]'); - const checkPage = (pageNumber) => { - assert.equal(wizard.page, pageNumber, `Should open wizard page ${pageNumber + 1}`); - }; + setTimeout(() => { + checkPage(0); + wizard.setSubmission( + _.cloneDeep(wizardTestForm.submission), + ); - checkPage(0); - clickWizardBtn('link[1]'); + setTimeout(() => { + checkPage(0); + clickWizardBtn('next'); + + setTimeout(() => { + checkPage(1); + clickWizardBtn('link[0]'); + + setTimeout(() => { + checkPage(1); + done(); + }, 100); + }, 100); + }, 100); + }, 100); + }, 100); + }) + .catch((err) => done(err)); + }); - setTimeout(() => { - checkPage(0); - clickWizardBtn('link[2]'); + it('Should set/get wizard submission', function (done) { + const formElement = document.createElement('div'); + const wizard = new Wizard(formElement); - setTimeout(() => { - checkPage(0); - wizard.setSubmission(_.cloneDeep(wizardTestForm.submission)); + wizard + .setForm(wizardTestForm.form) + .then(() => { + wizard.submission = _.cloneDeep(wizardTestForm.submission); - setTimeout(() => { - checkPage(0); - clickWizardBtn('next'); + setTimeout(() => { + assert.deepEqual( + wizard.data, + wizardTestForm.submission.data, + 'Should set wizard submission', + ); + assert.deepEqual( + wizard.submission.data, + wizardTestForm.submission.data, + 'Should get wizard submission data', + ); + + wizard.everyComponent((comp) => { + const expectedValue = _.get( + wizardTestForm.submission.data, + comp.path, + 'no data', + ); + if (expectedValue !== 'no data') { + assert.deepEqual( + comp.getValue(), + expectedValue, + `Should set value for ${comp.component.type} inside wizard`, + ); + assert.deepEqual( + comp.dataValue, + expectedValue, + `Should set value for ${comp.component.type} inside wizard`, + ); + } + }); + done(); + }, 300); + }) + .catch((err) => done(err)); + }); - setTimeout(() => { - checkPage(1); - clickWizardBtn('link[0]'); + it('Should correctly render customized wizard and navigate using custom btns', function (done) { + const formElement = document.createElement('div'); + const customizedWizard = new Wizard(formElement); + + customizedWizard + .setForm(customWizard) + .then(() => { + customizedWizard.on('goToNextPage', function () { + customizedWizard.nextPage(); + }); + customizedWizard.on('goToPrevPage', function () { + customizedWizard.prevPage(); + }); + + const checkBtns = (page) => { + assert.equal( + customizedWizard.page, + page, + `Should set page ${page + 1}`, + ); + assert.equal( + !!customizedWizard.refs[ + `${customizedWizard.wizardKey}-next` + ], + false, + 'Should not render wizard next btn', + ); + assert.equal( + !!customizedWizard.refs[ + `${customizedWizard.wizardKey}-cancel` + ], + false, + 'Should not render wizard cancel btn', + ); + assert.equal( + !!customizedWizard.refs[ + `${customizedWizard.wizardKey}-previous` + ], + false, + 'Should not render wizard previous btn', + ); + }; + + const navigatePage = (btnKey) => { + const customBtn = + customizedWizard.components[ + customizedWizard.page + ].getComponent(btnKey).refs.button; + const clickEvent = new Event('click'); + customBtn.dispatchEvent(clickEvent); + }; + checkBtns(0); + navigatePage('nextPage'); + setTimeout(() => { + checkBtns(1); + navigatePage('nextPage1'); + setTimeout(() => { + checkBtns(2); + navigatePage('prevPage1'); - setTimeout(() => { - checkPage(1); - done(); - }, 100); - }, 100); - }, 100); - }, 100); - }, 100); - }) - .catch((err) => done(err)); - }); - - it('Should set/get wizard submission', function(done) { - const formElement = document.createElement('div'); - const wizard = new Wizard(formElement); - - wizard.setForm(wizardTestForm.form).then(() => { - wizard.submission = _.cloneDeep(wizardTestForm.submission); - - setTimeout(() => { - assert.deepEqual(wizard.data, wizardTestForm.submission.data, 'Should set wizard submission'); - assert.deepEqual(wizard.submission.data, wizardTestForm.submission.data, 'Should get wizard submission data'); - - wizard.everyComponent((comp) => { - const expectedValue = _.get(wizardTestForm.submission.data, comp.path, 'no data'); - if (expectedValue !== 'no data') { - assert.deepEqual(comp.getValue(), expectedValue, `Should set value for ${comp.component.type} inside wizard`); - assert.deepEqual(comp.dataValue, expectedValue, `Should set value for ${comp.component.type} inside wizard`); - } - }); - done(); - }, 300); - }) - .catch((err) => done(err)); - }); - - it('Should correctly render customized wizard and navigate using custom btns', function(done) { - const formElement = document.createElement('div'); - const customizedWizard = new Wizard(formElement); - - customizedWizard.setForm(customWizard).then(() => { - customizedWizard.on('goToNextPage', function() { - customizedWizard.nextPage(); - }); - customizedWizard.on('goToPrevPage', function() { - customizedWizard.prevPage(); - }); - - const checkBtns = (page) => { - assert.equal(customizedWizard.page, page, `Should set page ${page + 1}`); - assert.equal(!!customizedWizard.refs[`${customizedWizard.wizardKey}-next`], false, 'Should not render wizard next btn'); - assert.equal(!!customizedWizard.refs[`${customizedWizard.wizardKey}-cancel`], false, 'Should not render wizard cancel btn'); - assert.equal(!!customizedWizard.refs[`${customizedWizard.wizardKey}-previous`], false, 'Should not render wizard previous btn'); - }; - - const navigatePage = (btnKey) => { - const customBtn = customizedWizard.components[customizedWizard.page].getComponent(btnKey).refs.button; - const clickEvent = new Event('click'); - customBtn.dispatchEvent(clickEvent); - }; - checkBtns(0); - navigatePage('nextPage'); - setTimeout(() => { - checkBtns(1); - navigatePage('nextPage1'); - setTimeout(() => { - checkBtns(2); - navigatePage('prevPage1'); - - setTimeout(() => { - checkBtns(1); - navigatePage('prevPage'); + setTimeout(() => { + checkBtns(1); + navigatePage('prevPage'); - setTimeout(() => { - checkBtns(0); - customizedWizard.destroy(); + setTimeout(() => { + checkBtns(0); + customizedWizard.destroy(); - done(); - }, 200); - }, 200); - }, 200); - }, 200); - }) - .catch((err) => done(err)); - }); - - it('Should not create a new submission on submission of edited draft submission', function(done) { - const formElement = document.createElement('div'); - const customizedWizard = new Wizard(formElement); - const expectedValues = { - '1': { - method: 'post', - urlEnd: 'submission', - state: 'draft', - data: { - textArea1: '', - textField: 'test' - }, - id: undefined - }, - '2': { - method: 'put', - urlEnd: 'someId', - state: 'draft', - data: { - number: 111111, - textArea1: 'test1', - textField: 'test1' - }, - id: 'someId' - }, - '3': { - method: 'put', - urlEnd: 'someId', - state: 'draft', - data: { - number: 22222, - textArea1: 'test', - textField: 'test1' - }, - id: 'someId' - }, - '4': { - method: 'put', - urlEnd: 'someId', - state: 'draft', - data: { - number: 22222, - textArea1: 'test1', - textField: 'test1' - }, - id: 'someId' - }, - '5': { - method: 'put', - urlEnd: 'someId', - state: 'submitted', - data: { - number: 22222, - textArea1: 'test1', - textField: 'test1' - }, - id: 'someId' - } - }; - - customizedWizard.setForm(customWizard).then(() => { - const formio = new Formio('http://test.localhost/draftwizardpages', {}); - let number = 1; - - formio.makeRequest = (type, url, method, data) => { - assert.equal(method, expectedValues[number].method, `Should send ${expectedValues[number].method} request`); - assert.equal(data._id, expectedValues[number].id, `Submission data should ${expectedValues[number].id ? '' : 'not'} contain id of editted submission`); - assert.equal(url.endsWith(expectedValues[number].urlEnd), true, `Request url should end with ${expectedValues[number].urlEnd}`); - assert.equal(data.state, expectedValues[number].state, `Should set ${expectedValues[number].state} state for submission`); - _.each(expectedValues[number].data, function(value, key) { - assert.equal(data.data[key], value, `${key} field should contain "${value}" value in submission object`); - }); + done(); + }, 200); + }, 200); + }, 200); + }, 200); + }) + .catch((err) => done(err)); + }); - number = number + 1; - - return new Promise(resolve => resolve({ - _id: 'someId', - data: { - number: 22222, - textArea1: 'test1', - textField: 'test1' - }, - metadata:{}, - state: data.state - }) - ); - }; - - customizedWizard.formio = formio; - - customizedWizard.on('goToNextPage', function() { - customizedWizard.executeSubmit({ state: 'draft' }).then(() => customizedWizard.nextPage()); - }); - customizedWizard.on('goToPrevPage', function() { - customizedWizard.executeSubmit({ state: 'draft' }).then(() => customizedWizard.prevPage()); - }); - customizedWizard.on('saveSubmission', function() { - customizedWizard.executeSubmit(); - }); - - const checkPage = (page) => { - assert.equal(customizedWizard.page, page, `Should set page ${page + 1}`); - }; - - const navigatePage = (btnKey) => { - const customBtn = customizedWizard.components[customizedWizard.page].getComponent(btnKey).refs.button; - const clickEvent = new Event('click'); - customBtn.dispatchEvent(clickEvent); - }; + it('Should not create a new submission on submission of edited draft submission', function (done) { + const formElement = document.createElement('div'); + const customizedWizard = new Wizard(formElement); + const expectedValues = { + 1: { + method: 'post', + urlEnd: 'submission', + state: 'draft', + data: { + textArea1: '', + textField: 'test', + }, + id: undefined, + }, + 2: { + method: 'put', + urlEnd: 'someId', + state: 'draft', + data: { + number: 111111, + textArea1: 'test1', + textField: 'test1', + }, + id: 'someId', + }, + 3: { + method: 'put', + urlEnd: 'someId', + state: 'draft', + data: { + number: 22222, + textArea1: 'test', + textField: 'test1', + }, + id: 'someId', + }, + 4: { + method: 'put', + urlEnd: 'someId', + state: 'draft', + data: { + number: 22222, + textArea1: 'test1', + textField: 'test1', + }, + id: 'someId', + }, + 5: { + method: 'put', + urlEnd: 'someId', + state: 'submitted', + data: { + number: 22222, + textArea1: 'test1', + textField: 'test1', + }, + id: 'someId', + }, + }; - const setPageCompValue = (compKey, value) => { - customizedWizard.components[customizedWizard.page].getComponent(compKey).setValue(value); - }; + customizedWizard + .setForm(customWizard) + .then(() => { + const formio = new Formio( + 'http://test.localhost/draftwizardpages', + {}, + ); + let number = 1; + + formio.makeRequest = (type, url, method, data) => { + assert.equal( + method, + expectedValues[number].method, + `Should send ${expectedValues[number].method} request`, + ); + assert.equal( + data._id, + expectedValues[number].id, + `Submission data should ${ + expectedValues[number].id ? '' : 'not' + } contain id of editted submission`, + ); + assert.equal( + url.endsWith(expectedValues[number].urlEnd), + true, + `Request url should end with ${expectedValues[number].urlEnd}`, + ); + assert.equal( + data.state, + expectedValues[number].state, + `Should set ${expectedValues[number].state} state for submission`, + ); + _.each(expectedValues[number].data, function (value, key) { + assert.equal( + data.data[key], + value, + `${key} field should contain "${value}" value in submission object`, + ); + }); + + number = number + 1; + + return new Promise((resolve) => + resolve({ + _id: 'someId', + data: { + number: 22222, + textArea1: 'test1', + textField: 'test1', + }, + metadata: {}, + state: data.state, + }), + ); + }; + + customizedWizard.formio = formio; + + customizedWizard.on('goToNextPage', function () { + customizedWizard + .executeSubmit({ state: 'draft' }) + .then(() => customizedWizard.nextPage()); + }); + customizedWizard.on('goToPrevPage', function () { + customizedWizard + .executeSubmit({ state: 'draft' }) + .then(() => customizedWizard.prevPage()); + }); + customizedWizard.on('saveSubmission', function () { + customizedWizard.executeSubmit(); + }); + + const checkPage = (page) => { + assert.equal( + customizedWizard.page, + page, + `Should set page ${page + 1}`, + ); + }; + + const navigatePage = (btnKey) => { + const customBtn = + customizedWizard.components[ + customizedWizard.page + ].getComponent(btnKey).refs.button; + const clickEvent = new Event('click'); + customBtn.dispatchEvent(clickEvent); + }; + + const setPageCompValue = (compKey, value) => { + customizedWizard.components[customizedWizard.page] + .getComponent(compKey) + .setValue(value); + }; + + checkPage(0); + setPageCompValue('textField', 'test'); + navigatePage('nextPage'); - checkPage(0); - setPageCompValue('textField', 'test'); - navigatePage('nextPage'); + setTimeout(() => { + checkPage(1); + setPageCompValue('number', 111111); + navigatePage('nextPage1'); - setTimeout(() => { - checkPage(1); - setPageCompValue('number', 111111); - navigatePage('nextPage1'); + setTimeout(() => { + checkPage(2); + setPageCompValue('textArea1', 'test'); + navigatePage('prevPage1'); + + setTimeout(() => { + checkPage(1); + navigatePage('nextPage1'); + + setTimeout(() => { + checkPage(2); + navigatePage('save'); + setTimeout(() => { + customizedWizard.destroy(); + done(); + }, 200); + }, 200); + }, 200); + }, 200); + }, 200); + }) + .catch((err) => done(err)); + }); - setTimeout(() => { - checkPage(2); - setPageCompValue('textArea1', 'test'); - navigatePage('prevPage1'); + it('Should show validation alert and components` errors and navigate pages after clicking alert error', function (done) { + const formElement = document.createElement('div'); + const wizard = new Wizard(formElement); + + wizard + .setForm(wizardTestForm.form) + .then(() => { + const clickWizardBtn = (pathPart, clickError) => { + const btn = _.get( + wizard.refs, + clickError + ? pathPart + : `${wizard.wizardKey}-${pathPart}`, + ); + const clickEvent = new Event('click'); + btn.dispatchEvent(clickEvent); + }; + + const checkPage = (pageNumber) => { + assert.equal( + wizard.page, + pageNumber, + `Should open wizard page ${pageNumber + 1}`, + ); + }; + + const checkInvalidComp = (compKey, highLight) => { + const comp = wizard.getComponent(compKey); + + assert.deepEqual( + !!comp.error, + true, + `${compKey}: should have error`, + ); + assert.deepEqual( + comp.error.message, + `${comp.component.label} is required`, + `${compKey}: should have correct required validation message`, + ); + assert.deepEqual( + comp.pristine, + false, + `${compKey}: should set pristine to false`, + ); + assert.deepEqual( + comp.element.classList.contains( + `${ + highLight ? 'formio-error-wrapper' : 'has-error' + }`, + ), + true, + `${compKey}: should set error class`, + ); + assert.deepEqual( + comp.refs.messageContainer + .querySelector('.error') + .textContent.trim(), + `${comp.component.label} is required`, + `${compKey}: should display error message`, + ); + }; + + checkPage(0); + clickWizardBtn('link[2]'); - setTimeout(() => { - checkPage(1); - navigatePage('nextPage1'); + setTimeout(() => { + checkPage(2); + assert.equal( + wizard.errors.length, + 0, + 'Should not have validation errors', + ); - setTimeout(() => { - checkPage(2); - navigatePage('save'); - setTimeout(() => { - customizedWizard.destroy(); - done(); - }, 200); - }, 200); - }, 200); - }, 200); - }, 200); - }) - .catch((err) => done(err)); - }); - - it('Should show validation alert and components` errors and navigate pages after clicking alert error', function(done) { - const formElement = document.createElement('div'); - const wizard = new Wizard(formElement); - - wizard.setForm(wizardTestForm.form).then(() => { - const clickWizardBtn = (pathPart, clickError) => { - const btn = _.get(wizard.refs, clickError ? pathPart : `${wizard.wizardKey}-${pathPart}`); - const clickEvent = new Event('click'); - btn.dispatchEvent(clickEvent); - }; + clickWizardBtn('submit'); - const checkPage = (pageNumber) => { - assert.equal(wizard.page, pageNumber, `Should open wizard page ${pageNumber + 1}`); - }; + setTimeout(() => { + assert.equal( + wizard.errors.length, + 3, + 'Should have validation errors', + ); + assert.equal( + wizard.refs.errorRef.length, + wizard.errors.length, + 'Should show alert with validation errors', + ); + assert.equal( + !!wizard.element.querySelector('.alert-danger'), + true, + 'Should have alert with validation errors', + ); + checkInvalidComp('select', true); + clickWizardBtn('errorRef[0]', true); - const checkInvalidComp = (compKey, highLight) => { - const comp = wizard.getComponent(compKey); + setTimeout(() => { + checkPage(0); + + assert.equal( + wizard.errors.length, + 1, + 'Should have page validation error', + ); + assert.equal( + wizard.refs.errorRef.length, + 3, + 'Should keep alert with validation errors', + ); + checkInvalidComp('textField'); + clickWizardBtn('errorRef[1]', true); + + setTimeout(() => { + checkPage(1); + + assert.equal( + wizard.errors.length, + 1, + 'Should have page validation error', + ); + assert.equal( + wizard.refs.errorRef.length, + 3, + 'Should keep alert with validation errors', + ); + checkInvalidComp('checkbox'); + wizard.getComponent('checkbox').setValue(true); + + setTimeout(() => { + checkPage(1); + assert.equal( + wizard.errors.length, + 0, + 'Should not have page validation error', + ); + assert.equal( + wizard.refs.errorRef.length, + 2, + 'Should keep alert with validation errors', + ); + clickWizardBtn('errorRef[1]', true); + + setTimeout(() => { + checkPage(2); + + assert.equal( + wizard.errors.length, + 2, + 'Should have wizard validation errors', + ); + assert.equal( + wizard.refs.errorRef.length, + 2, + 'Should keep alert with validation errors', + ); + wizard + .getComponent('select') + .setValue('value1'); + + setTimeout(() => { + assert.equal( + wizard.errors.length, + 1, + 'Should have wizard validation error', + ); + assert.equal( + wizard.refs.errorRef.length, + 1, + 'Should keep alert with validation errors', + ); + clickWizardBtn('errorRef[0]', true); + + setTimeout(() => { + checkPage(0); + + assert.equal( + wizard.errors.length, + 1, + 'Should have page validation error', + ); + assert.equal( + wizard.refs.errorRef.length, + 1, + 'Should keep alert with validation errors', + ); + wizard + .getComponent('textField') + .setValue('valid'); + + setTimeout(() => { + assert.equal( + wizard.errors.length, + 0, + 'Should not have page validation error', + ); + assert.equal( + !!wizard.element.querySelector( + '.alert-danger', + ), + false, + 'Should not have alert with validation errors', + ); + clickWizardBtn('link[2]'); + + setTimeout(() => { + clickWizardBtn( + 'submit', + ); + setTimeout(() => { + assert.equal( + wizard + .submission + .state, + 'submitted', + 'Should submit the form', + ); + done(); + }, 300); + }, 300); + }, 300); + }, 300); + }, 300); + }, 300); + }, 300); + }, 100); + }, 100); + }, 100); + }, 100); + }) + .catch((err) => done(err)); + }).timeout(3500); + + it('Should navigate wizard pages using navigation buttons and breadcrumbs', function (done) { + const formElement = document.createElement('div'); + const wizard = new Wizard(formElement); + + wizard + .setForm(wizardTestForm.form) + .then(() => { + const clickNavigationBtn = (pathPart) => { + const btn = _.get( + wizard.refs, + `${wizard.wizardKey}-${pathPart}`, + ); + const clickEvent = new Event('click'); + btn.dispatchEvent(clickEvent); + }; + + const checkPage = (pageNumber) => { + assert.equal( + wizard.page, + pageNumber, + `Should open wizard page ${pageNumber + 1}`, + ); + }; + checkPage(0); + clickNavigationBtn('next'); - assert.deepEqual(!!comp.error, true, `${compKey}: should have error`); - assert.deepEqual(comp.error.message, `${comp.component.label} is required`, `${compKey}: should have correct required validation message`); - assert.deepEqual(comp.pristine, false, `${compKey}: should set pristine to false`); - assert.deepEqual(comp.element.classList.contains(`${highLight ? 'formio-error-wrapper' : 'has-error'}`), true, `${compKey}: should set error class`); - assert.deepEqual(comp.refs.messageContainer.querySelector('.error').textContent.trim(), `${comp.component.label} is required`, `${compKey}: should display error message`); - }; + setTimeout(() => { + checkPage(0); - checkPage(0); - clickWizardBtn('link[2]'); + assert.equal( + wizard.errors.length, + 1, + 'Should have validation error', + ); + assert.equal( + wizard.refs.errorRef.length, + wizard.errors.length, + 'Should show alert with validation error', + ); - setTimeout(() => { - checkPage(2); - assert.equal(wizard.errors.length, 0, 'Should not have validation errors'); + wizard.getComponent('textField').setValue('valid'); - clickWizardBtn('submit'); + clickNavigationBtn('next'); - setTimeout(() => { - assert.equal(wizard.errors.length, 3, 'Should have validation errors'); - assert.equal(wizard.refs.errorRef.length, wizard.errors.length, 'Should show alert with validation errors'); - assert.equal(!!wizard.element.querySelector('.alert-danger'), true, 'Should have alert with validation errors'); - checkInvalidComp('select', true); - clickWizardBtn('errorRef[0]', true); + setTimeout(() => { + checkPage(1); + clickNavigationBtn('next'); - setTimeout(() => { - checkPage(0); + setTimeout(() => { + checkPage(1); + + assert.equal( + wizard.errors.length, + 1, + 'Should have validation error', + ); + assert.equal( + wizard.refs.errorRef.length, + wizard.errors.length, + 'Should show alert with validation error', + ); + + clickNavigationBtn('previous'); + + setTimeout(() => { + checkPage(0); + + assert.equal( + wizard.errors.length, + 0, + 'Should not have validation error', + ); + assert.equal( + !!wizard.refs.errorRef, + false, + 'Should not have alert with validation error', + ); + + clickNavigationBtn('next'); + + setTimeout(() => { + checkPage(1); + assert.equal( + wizard.errors.length, + 1, + 'Should have validation error', + ); + wizard + .getComponent('checkbox') + .setValue(true); + + clickNavigationBtn('next'); + + setTimeout(() => { + checkPage(2); + assert.equal( + wizard.errors.length, + 0, + 'Should not have validation error', + ); + clickNavigationBtn('link[0]'); + + setTimeout(() => { + checkPage(0); + assert.equal( + wizard.errors.length, + 0, + 'Should not have validation error', + ); + clickNavigationBtn('link[2]'); + + setTimeout(() => { + checkPage(2); + done(); + }, 50); + }, 50); + }, 50); + }, 50); + }, 50); + }, 50); + }, 50); + }, 50); + }) + .catch((err) => done(err)); + }); - assert.equal(wizard.errors.length, 1, 'Should have page validation error'); - assert.equal(wizard.refs.errorRef.length, 3, 'Should keep alert with validation errors'); - checkInvalidComp('textField'); - clickWizardBtn('errorRef[1]', true); + it('Should stay on current page when changing language', function (done) { + const formElement = document.createElement('div'); + const wizard = new Wizard(formElement, { language: 'en' }); + wizard.setForm(wizardTestForm.form).then(() => { + const checkPage = (page) => { + assert.equal( + wizard.page, + page, + `Page ${page + 1} should be the current page`, + ); + }; + + Harness.clickElement( + wizard, + wizard.refs[`${wizard.wizardKey}-link`][2], + ); + checkPage(2); + wizard.language = 'es'; setTimeout(() => { - checkPage(1); + checkPage(2); + done(); + }, 50); + }); + }); - assert.equal(wizard.errors.length, 1, 'Should have page validation error'); - assert.equal(wizard.refs.errorRef.length, 3, 'Should keep alert with validation errors'); - checkInvalidComp('checkbox'); - wizard.getComponent('checkbox').setValue(true); + it('Should correctly set values in HTML render mode', function (done) { + const formElement = document.createElement('div'); + const formHTMLMode = new Wizard(formElement, { + readOnly: true, + renderMode: 'html', + }); - setTimeout(() => { - checkPage(1); - assert.equal(wizard.errors.length, 0, 'Should not have page validation error'); - assert.equal(wizard.refs.errorRef.length, 2, 'Should keep alert with validation errors'); - clickWizardBtn('errorRef[1]', true); + formHTMLMode + .setForm(wizardForHtmlModeTest.form) + .then(() => { + formHTMLMode.setSubmission(wizardForHtmlModeTest.submission); setTimeout(() => { - checkPage(2); - - assert.equal(wizard.errors.length, 2, 'Should have wizard validation errors'); - assert.equal(wizard.refs.errorRef.length, 2, 'Should keep alert with validation errors'); - wizard.getComponent('select').setValue('value1'); - - setTimeout(() => { - assert.equal(wizard.errors.length, 1, 'Should have wizard validation error'); - assert.equal(wizard.refs.errorRef.length, 1, 'Should keep alert with validation errors'); - clickWizardBtn('errorRef[0]', true); + const numberValue = + formHTMLMode.element.querySelector( + '[ref="value"]', + ).textContent; + assert.equal( + +numberValue, + wizardForHtmlModeTest.submission.data.number, + ); + + const nextPageBtn = + formHTMLMode.refs[`${formHTMLMode.wizardKey}-next`]; + const clickEvent = new Event('click'); + nextPageBtn.dispatchEvent(clickEvent); setTimeout(() => { - checkPage(0); + const textValue = + formHTMLMode.element.querySelector( + '[ref="value"]', + ).textContent; + assert.equal( + textValue, + wizardForHtmlModeTest.submission.data.textField, + ); + + done(); + }, 250); + }, 200); + }) + .catch((err) => done(err)); + }); - assert.equal(wizard.errors.length, 1, 'Should have page validation error'); - assert.equal(wizard.refs.errorRef.length, 1, 'Should keep alert with validation errors'); - wizard.getComponent('textField').setValue('valid'); + it('Should show tooltip for wizard pages', function (done) { + const formElement = document.createElement('div'); + const wizardWithPageTooltip = new Wizard(formElement); - setTimeout(() => { - assert.equal(wizard.errors.length, 0, 'Should not have page validation error'); - assert.equal(!!wizard.element.querySelector('.alert-danger'), false, 'Should not have alert with validation errors'); - clickWizardBtn('link[2]'); + wizardWithPageTooltip + .setForm(wizardWithTooltip) + .then(() => { + const clickEvent = new Event('click'); - setTimeout(() => { - clickWizardBtn('submit'); - setTimeout(() => { - assert.equal(wizard.submission.state, 'submitted', 'Should submit the form'); - done(); - }, 300); - }, 300); - }, 300); - }, 300); - }, 300); - }, 300); - }, 300); - }, 100); - }, 100); - }, 100); - }, 100); - }) - .catch((err) => done(err)); - }).timeout(3500); - - it('Should navigate wizard pages using navigation buttons and breadcrumbs', function(done) { - const formElement = document.createElement('div'); - const wizard = new Wizard(formElement); - - wizard.setForm(wizardTestForm.form).then(() => { - const clickNavigationBtn = (pathPart) => { - const btn = _.get(wizard.refs, `${wizard.wizardKey}-${pathPart}`); - const clickEvent = new Event('click'); - btn.dispatchEvent(clickEvent); - }; + assert.equal(wizardWithPageTooltip.tooltips.length, 1); - const checkPage = (pageNumber) => { - assert.equal(wizard.page, pageNumber, `Should open wizard page ${pageNumber + 1}`); - }; - checkPage(0); - clickNavigationBtn('next'); + const pageTooltipIcon = + wizardWithPageTooltip.refs[ + `${wizardWithPageTooltip.wizardKey}-tooltip` + ][0]; - setTimeout(() => { - checkPage(0); + assert.equal(!!pageTooltipIcon, true); - assert.equal(wizard.errors.length, 1, 'Should have validation error'); - assert.equal(wizard.refs.errorRef.length, wizard.errors.length, 'Should show alert with validation error'); + pageTooltipIcon.dispatchEvent(clickEvent); - wizard.getComponent('textField').setValue('valid'); + setTimeout(() => { + const tooltipText = + wizardWithPageTooltip.element.querySelector( + '.tippy-content', + ).textContent; + assert.equal( + tooltipText, + wizardWithPageTooltip.currentPanel.tooltip, + ); - clickNavigationBtn('next'); + done(); + }, 300); + }) + .catch((err) => done(err)); + }); - setTimeout(() => { - checkPage(1); - clickNavigationBtn('next'); + it('Should not clear wizard data when navigating between wizard pages with hidden panel', function (done) { + const formElement = document.createElement('div'); + const formWithHiddenPage = new Wizard(formElement); - setTimeout(() => { - checkPage(1); + formWithHiddenPage + .setForm(wizardWithHiddenPanel) + .then(() => { + const clickEvent = new Event('click'); + const inputEvent = new Event('input'); - assert.equal(wizard.errors.length, 1, 'Should have validation error'); - assert.equal(wizard.refs.errorRef.length, wizard.errors.length, 'Should show alert with validation error'); + assert.equal(formWithHiddenPage.pages.length, 2); - clickNavigationBtn('previous'); + const page1Field = formWithHiddenPage.element.querySelector( + '[name="data[number]"]', + ); - setTimeout(() => { - checkPage(0); + page1Field.value = '555'; + page1Field.dispatchEvent(inputEvent); - assert.equal(wizard.errors.length, 0, 'Should not have validation error'); - assert.equal(!!wizard.refs.errorRef, false, 'Should not have alert with validation error'); + setTimeout(() => { + assert.equal( + formWithHiddenPage.element.querySelector( + '[name="data[number]"]', + ).value, + 555, + ); + + const nextPageBtn = + formWithHiddenPage.refs[ + `${formWithHiddenPage.wizardKey}-next` + ]; + nextPageBtn.dispatchEvent(clickEvent); - clickNavigationBtn('next'); + setTimeout(() => { + assert.equal(formWithHiddenPage.page, 1); - setTimeout(() => { - checkPage(1); - assert.equal(wizard.errors.length, 1, 'Should have validation error'); - wizard.getComponent('checkbox').setValue(true); + const prevPageBtn = + formWithHiddenPage.refs[ + `${formWithHiddenPage.wizardKey}-previous` + ]; + prevPageBtn.dispatchEvent(clickEvent); - clickNavigationBtn('next'); + setTimeout(() => { + assert.equal(formWithHiddenPage.page, 0); + assert.equal( + formWithHiddenPage.element.querySelector( + '[name="data[number]"]', + ).value, + 555, + ); - setTimeout(() => { - checkPage(2); - assert.equal(wizard.errors.length, 0, 'Should not have validation error'); - clickNavigationBtn('link[0]'); + done(); + }, 250); + }, 200); + }, 100); + }) + .catch((err) => done(err)); + }); - setTimeout(() => { - checkPage(0); - assert.equal(wizard.errors.length, 0, 'Should not have validation error'); - clickNavigationBtn('link[2]'); + it('Should show signature submission in HTML render mode', function (done) { + const formElement = document.createElement('div'); + const formWithSignatureHTMLMode = new Wizard(formElement, { + readOnly: true, + renderMode: 'html', + }); - setTimeout(() => { - checkPage(2); - done(); - }, 50); - }, 50); - }, 50); - }, 50); - }, 50); - }, 50); - }, 50); - }, 50); - }) - .catch((err) => done(err)); - }); - - it('Should stay on current page when changing language', function(done) { - const formElement = document.createElement('div'); - const wizard = new Wizard(formElement, { language: 'en' }); - wizard.setForm(wizardTestForm.form).then(() => { - const checkPage = (page) => { - assert.equal(wizard.page, page, `Page ${page + 1} should be the current page`); - }; - - Harness.clickElement(wizard, wizard.refs[`${wizard.wizardKey}-link`][2]); - checkPage(2); - - wizard.language = 'es'; - setTimeout(() => { - checkPage(2); - done(); - }, 50); - }); - }); + formWithSignatureHTMLMode + .setForm(formWithSignature.form) + .then(() => { + formWithSignatureHTMLMode.setSubmission( + formWithSignature.submission, + ); + + setTimeout(() => { + const signatureImage = + formWithSignatureHTMLMode.element.querySelector( + '[ref="signatureImage"]', + ); + assert.equal( + signatureImage.src === + formWithSignature.submission.data.signature, + true, + ); - it('Should correctly set values in HTML render mode', function(done) { - const formElement = document.createElement('div'); - const formHTMLMode = new Wizard(formElement, { - readOnly: true, - renderMode: 'html' + done(); + }, 200); + }) + .catch((err) => done(err)); }); - formHTMLMode.setForm(wizardForHtmlModeTest.form).then(() => { - formHTMLMode.setSubmission(wizardForHtmlModeTest.submission); + it('Should display conditional page after setting submission', function (done) { + const formElement = document.createElement('div'); + const wizardWithSimpleConditionalPage = new Wizard(formElement); - setTimeout(() => { - const numberValue = formHTMLMode.element.querySelector('[ref="value"]').textContent; - assert.equal(+numberValue, wizardForHtmlModeTest.submission.data.number); + wizardWithSimpleConditionalPage + .setForm(wizard4) + .then(() => { + setTimeout(() => { + assert.equal( + wizardWithSimpleConditionalPage.pages.length, + 1, + ); + assert.equal( + wizardWithSimpleConditionalPage.components.length, + 1, + ); + const submissionData = { checkbox: true, number: 555 }; + wizardWithSimpleConditionalPage.setSubmission({ + data: submissionData, + }); - const nextPageBtn = formHTMLMode.refs[`${formHTMLMode.wizardKey}-next`]; - const clickEvent = new Event('click'); - nextPageBtn.dispatchEvent(clickEvent); + setTimeout(() => { + assert.equal( + wizardWithSimpleConditionalPage.pages.length, + 2, + ); + assert.equal( + wizardWithSimpleConditionalPage.components.length, + 2, + ); + assert.deepEqual( + wizardWithSimpleConditionalPage.data, + submissionData, + ); + done(); + }, 500); + }, 200); + }) + .catch((err) => done(err)); + }); - setTimeout(() => { - const textValue = formHTMLMode.element.querySelector('[ref="value"]').textContent; - assert.equal(textValue, wizardForHtmlModeTest.submission.data.textField); + it('Should display submission data on page with custom conditional logic in readOnly', function (done) { + const formElement = document.createElement('div'); + const wizardWithCustomConditionalPage = new Wizard(formElement); - done(); - }, 250); - }, 200); - }) - .catch((err) => done(err)); - }); + wizardWithCustomConditionalPage + .setForm(wizard5) + .then(() => { + setTimeout(() => { + wizardWithCustomConditionalPage.disabled = true; -it('Should show tooltip for wizard pages', function(done) { - const formElement = document.createElement('div'); - const wizardWithPageTooltip = new Wizard(formElement); + if (wizardWithCustomConditionalPage.options) { + wizardWithCustomConditionalPage.options.readOnly = true; + } else { + wizardWithCustomConditionalPage.options = { + readOnly: true, + }; + } - wizardWithPageTooltip.setForm(wizardWithTooltip).then(() => { - const clickEvent = new Event('click'); + setTimeout(() => { + assert.equal( + wizardWithCustomConditionalPage.pages.length, + 1, + ); + assert.equal( + wizardWithCustomConditionalPage.components.length, + 1, + ); - assert.equal(wizardWithPageTooltip.tooltips.length, 1); + const submissionData = { checkbox: true, number: 555 }; - const pageTooltipIcon = wizardWithPageTooltip.refs[`${wizardWithPageTooltip.wizardKey}-tooltip`][0]; + wizardWithCustomConditionalPage.setSubmission({ + data: submissionData, + }); - assert.equal(!!pageTooltipIcon, true); + setTimeout(() => { + assert.equal( + wizardWithCustomConditionalPage.pages.length, + 2, + ); + assert.equal( + wizardWithCustomConditionalPage.components + .length, + 2, + ); + assert.deepEqual( + wizardWithCustomConditionalPage.data, + submissionData, + ); + + const clickEvent = new Event('click'); + const secondPageBtn = + wizardWithCustomConditionalPage.refs[ + `${wizardWithCustomConditionalPage.wizardKey}-link` + ][1]; + + secondPageBtn.dispatchEvent(clickEvent); + + setTimeout(() => { + assert.equal( + wizardWithCustomConditionalPage.page, + 1, + ); + + const numberComponent = + wizardWithCustomConditionalPage.element.querySelector( + '[name="data[number]"]', + ); + + assert.equal(numberComponent.value, 555); + + done(); + }, 400); + }, 300); + }, 200); + }, 100); + }) + .catch((err) => done(err)); + }); - pageTooltipIcon.dispatchEvent(clickEvent); + it('Should show conditional wizard page', function (done) { + const formElement = document.createElement('div'); + const wizardWithConditionalPage = new Wizard(formElement); - setTimeout(() => { - const tooltipText = wizardWithPageTooltip.element.querySelector('.tippy-content').textContent; - assert.equal(tooltipText, wizardWithPageTooltip.currentPanel.tooltip); + wizardWithConditionalPage + .setForm(wizard3) + .then(() => { + setTimeout(() => { + assert.equal(wizardWithConditionalPage.pages.length, 1); + assert.equal( + wizardWithConditionalPage.components.length, + 1, + ); - done(); - }, 300); - }) - .catch((err) => done(err)); - }); + const inputEvent = new Event('input'); + const numberComponent = + wizardWithConditionalPage.element.querySelector( + '[name="data[number]"]', + ); - it('Should not clear wizard data when navigating between wizard pages with hidden panel', function(done) { - const formElement = document.createElement('div'); - const formWithHiddenPage = new Wizard(formElement); + numberComponent.value = 5; + numberComponent.dispatchEvent(inputEvent); - formWithHiddenPage.setForm(wizardWithHiddenPanel).then(() => { - const clickEvent = new Event('click'); - const inputEvent = new Event('input'); + setTimeout(() => { + assert.equal(wizardWithConditionalPage.pages.length, 2); + assert.equal( + wizardWithConditionalPage.components.length, + 2, + ); - assert.equal(formWithHiddenPage.pages.length, 2); + done(); + }, 300); + }, 200); + }) + .catch((err) => done(err)); + }); - const page1Field = formWithHiddenPage.element.querySelector('[name="data[number]"]'); + it('Should show first conditional wizard page', function (done) { + const formElement = document.createElement('div'); + const wizard = new Wizard(formElement); + + wizard + .setForm(wizard6) + .then(() => { + assert.equal(wizard.pages.length, 1); + assert.equal(wizard.components.length, 1); + assert.equal(wizard.page, 0); + assert.equal(wizard.refs[`wizard-${wizard.id}-previous`], null); + assert.equal( + wizard.refs[ + `wizard-${wizard.id}-link` + ][0].parentElement.classList.contains('active'), + true, + ); + wizard.setValue({ + data: { b: 'true' }, + }); + setTimeout(() => { + assert.equal(wizard.pages.length, 2); + assert.equal(wizard.components.length, 2); + assert.equal(wizard.page, 1); + assert.notEqual( + wizard.refs[`wizard-${wizard.id}-previous`], + null, + ); + assert.equal( + wizard.refs[ + `wizard-${wizard.id}-link` + ][1].parentElement.classList.contains('active'), + true, + ); + done(); + }, 300); + }) + .catch((err) => done(err)); + }); - page1Field.value = '555'; - page1Field.dispatchEvent(inputEvent); + it('Should display editGrid submission data in readOnly mode', function (done) { + const formElement = document.createElement('div'); + const wizardForm = new Wizard(formElement, { readOnly: true }); + wizardForm + .setForm(wizard2) + .then(() => { + wizardForm.setValue({ + data: { editGrid: [{ textField: '111' }], number: 222 }, + }); + setTimeout(() => { + assert.equal( + wizardForm.element.querySelector( + '[name="data[number]"]', + ).value, + '222', + ); + + Harness.clickElement( + wizardForm, + wizardForm.refs[`${wizardForm.wizardKey}-link`][1], + ); - setTimeout(() => { - assert.equal(formWithHiddenPage.element.querySelector('[name="data[number]"]').value, 555); + setTimeout(() => { + assert.equal(wizardForm.page, 1); + + const ditGridRowValue = wizardForm.element + .querySelector('[ref = "editgrid-editGrid-row"]') + .querySelector('.col-sm-2') + .textContent.trim(); + assert.equal(ditGridRowValue, '111'); + done(); + }, 300); + }, 100); + }) + .catch((err) => done(err)); + }); - const nextPageBtn = formWithHiddenPage.refs[`${formWithHiddenPage.wizardKey}-next`]; - nextPageBtn.dispatchEvent(clickEvent); + let wizardForm = null; + + it('Should set components errors if they are after page was changed with navigation', function (done) { + const formElement = document.createElement('div'); + wizardForm = new Wizard(formElement); + wizardForm + .setForm(wizard) + .then(() => { + Harness.testErrors( + wizardForm, + { + data: { + a: '1', + c: '', + textField: '', + }, + }, + [ + { + component: 'a', + message: 'a must have at least 4 characters.', + }, + ], + done, + ); + Harness.clickElement( + wizardForm, + wizardForm.refs[`${wizardForm.wizardKey}-link`][2], + ); + assert.equal(wizardForm.page, 2); + setTimeout(() => { + Harness.clickElement( + wizardForm, + wizardForm.refs[`${wizardForm.wizardKey}-link`][0], + ); + assert.equal(wizardForm.page, 0); + setTimeout(() => { + const aInput = wizardForm.currentPage.getComponent('a'); + assert.equal(aInput.errors.length, 1); + assert.equal( + aInput.errors[0].message, + 'a must have at least 4 characters.', + ); + done(); + }, 100); + }, 100); + }) + .catch((err) => done(err)); + }); - setTimeout(() => { - assert.equal(formWithHiddenPage.page, 1); + it('Should leave errors for invalid fields after validation on next button and entering valid data in one of the fields', function (done) { + const formElement = document.createElement('div'); + wizardForm = new Wizard(formElement); + wizardForm + .setForm(wizard1) + .then(() => { + Harness.clickElement( + wizardForm, + wizardForm.refs[`${wizardForm.wizardKey}-next`], + ); + setTimeout(() => { + assert.equal(wizardForm.errors.length, 2); - const prevPageBtn = formWithHiddenPage.refs[`${formWithHiddenPage.wizardKey}-previous`]; - prevPageBtn.dispatchEvent(clickEvent); + const inputEvent = new Event('input', { + bubbles: true, + cancelable: true, + }); + const inputA = formElement.querySelector( + 'input[name="data[a]"]', + ); - setTimeout(() => { - assert.equal(formWithHiddenPage.page, 0); - assert.equal(formWithHiddenPage.element.querySelector('[name="data[number]"]').value, 555); + for (let i = 0; i < 5; i++) { + inputA.value += i; + inputA.dispatchEvent(inputEvent); + } - done(); - }, 250); - }, 200); - }, 100); - }) - .catch((err) => done(err)); - }); - - it('Should show signature submission in HTML render mode', function(done) { - const formElement = document.createElement('div'); - const formWithSignatureHTMLMode = new Wizard(formElement, { - readOnly: true, - renderMode: 'html' + setTimeout(() => { + assert.equal(wizardForm.errors.length, 1); + done(); + }, 250); + }, 250); + }) + .catch((err) => done(err)); }); - formWithSignatureHTMLMode.setForm(formWithSignature.form).then(() => { - formWithSignatureHTMLMode.setSubmission(formWithSignature.submission); - - setTimeout(() => { - const signatureImage = formWithSignatureHTMLMode.element.querySelector('[ref="signatureImage"]'); - assert.equal(signatureImage.src === formWithSignature.submission.data.signature, true); - - done(); - }, 200); - }) - .catch((err) => done(err)); - }); - - it('Should display conditional page after setting submission', function(done) { - const formElement = document.createElement('div'); - const wizardWithSimpleConditionalPage = new Wizard(formElement); - - wizardWithSimpleConditionalPage.setForm(wizard4).then(() => { - setTimeout(() => { - assert.equal(wizardWithSimpleConditionalPage.pages.length, 1); - assert.equal(wizardWithSimpleConditionalPage.components.length, 1); - const submissionData = { checkbox: true, number: 555 }; - wizardWithSimpleConditionalPage.setSubmission({ data:submissionData }); - - setTimeout(() => { - assert.equal(wizardWithSimpleConditionalPage.pages.length, 2); - assert.equal(wizardWithSimpleConditionalPage.components.length, 2); - assert.deepEqual(wizardWithSimpleConditionalPage.data, submissionData); - done(); - }, 500); - }, 200); - }) - .catch((err) => done(err)); - }); - - it('Should display submission data on page with custom conditional logic in readOnly', function(done) { - const formElement = document.createElement('div'); - const wizardWithCustomConditionalPage = new Wizard(formElement); - - wizardWithCustomConditionalPage.setForm(wizard5).then(() => { - setTimeout(() => { - wizardWithCustomConditionalPage.disabled = true; - - if (wizardWithCustomConditionalPage.options) { - wizardWithCustomConditionalPage.options.readOnly = true; - } - else { - wizardWithCustomConditionalPage.options = { readOnly: true }; - } - - setTimeout(() => { - assert.equal(wizardWithCustomConditionalPage.pages.length, 1); - assert.equal(wizardWithCustomConditionalPage.components.length, 1); - - const submissionData = { checkbox: true, number: 555 }; - - wizardWithCustomConditionalPage.setSubmission({ data:submissionData }); - - setTimeout(() => { - assert.equal(wizardWithCustomConditionalPage.pages.length, 2); - assert.equal(wizardWithCustomConditionalPage.components.length, 2); - assert.deepEqual(wizardWithCustomConditionalPage.data, submissionData); - - const clickEvent = new Event('click'); - const secondPageBtn = wizardWithCustomConditionalPage.refs[`${wizardWithCustomConditionalPage.wizardKey}-link`][1]; - - secondPageBtn.dispatchEvent(clickEvent); + it('Should not set components errors if in readOnly mode', function (done) { + const formElement = document.createElement('div'); + wizardForm = new Wizard(formElement, { readOnly: true }); + wizardForm.setForm(wizard).then(() => { + Harness.testSubmission(wizardForm, { + data: { + a: '1', + textField: 'aaa', + c: '0', + }, + }); - setTimeout(() => { - assert.equal(wizardWithCustomConditionalPage.page, 1); - - const numberComponent = wizardWithCustomConditionalPage.element.querySelector('[name="data[number]"]'); - - assert.equal(numberComponent.value, 555); - - done(); - }, 400); - }, 300); - }, 200); - }, 100); - }) - .catch((err) => done(err)); - }); - - it('Should show conditional wizard page', function(done) { - const formElement = document.createElement('div'); - const wizardWithConditionalPage = new Wizard(formElement); - - wizardWithConditionalPage.setForm(wizard3).then(() => { - setTimeout(() => { - assert.equal(wizardWithConditionalPage.pages.length, 1); - assert.equal(wizardWithConditionalPage.components.length, 1); - - const inputEvent = new Event('input'); - const numberComponent = wizardWithConditionalPage.element.querySelector('[name="data[number]"]'); - - numberComponent.value = 5; - numberComponent.dispatchEvent(inputEvent); - - setTimeout(() => { - assert.equal(wizardWithConditionalPage.pages.length, 2); - assert.equal(wizardWithConditionalPage.components.length, 2); - - done(); - }, 300); - }, 200); - }) - .catch((err) => done(err)); - }); - - it('Should show first conditional wizard page', function(done) { - const formElement = document.createElement('div'); - const wizard = new Wizard(formElement); - - wizard.setForm(wizard6).then(() => { - assert.equal(wizard.pages.length, 1); - assert.equal(wizard.components.length, 1); - assert.equal(wizard.page, 0); - assert.equal(wizard.refs[`wizard-${wizard.id}-previous`], null); - assert.equal( - wizard.refs[ - `wizard-${wizard.id}-link` - ][0].parentElement.classList.contains('active'), - true - ); - wizard.setValue({ - data: { b: 'true' }, - }); - setTimeout(() => { - assert.equal(wizard.pages.length, 2); - assert.equal(wizard.components.length, 2); - assert.equal(wizard.page, 1); - assert.notEqual(wizard.refs[`wizard-${wizard.id}-previous`], null); - assert.equal( - wizard.refs[ - `wizard-${wizard.id}-link` - ][1].parentElement.classList.contains('active'), - true - ); - done(); - }, 300); - }) - .catch((err) => done(err)); - }); - - it('Should display editGrid submission data in readOnly mode', function(done) { - const formElement = document.createElement('div'); - const wizardForm = new Wizard(formElement, { readOnly: true }); - wizardForm.setForm(wizard2).then(() => { - wizardForm.setValue({ data: { editGrid: [{ textField: '111' }], number: 222 } }); - setTimeout(() => { - assert.equal(wizardForm.element.querySelector('[name="data[number]"]').value, '222'); - - Harness.clickElement(wizardForm, wizardForm.refs[`${wizardForm.wizardKey}-link`][1]); - - setTimeout(() => { - assert.equal(wizardForm.page, 1); - - const ditGridRowValue = wizardForm.element.querySelector('[ref = "editgrid-editGrid-row"]').querySelector('.col-sm-2').textContent.trim(); - assert.equal(ditGridRowValue, '111'); - done(); - }, 300); - }, 100); - }) - .catch((err) => done(err)); - }); - - let wizardForm = null; - - it('Should set components errors if they are after page was changed with navigation', function(done) { - const formElement = document.createElement('div'); - wizardForm = new Wizard(formElement); - wizardForm.setForm(wizard).then(() => { - Harness.testErrors(wizardForm, { - data: { - a: '1', - c: '', - textField: '' - } - }, - [{ - component: 'a', - message: 'a must have at least 4 characters.' - }], done); - Harness.clickElement(wizardForm, wizardForm.refs[`${wizardForm.wizardKey}-link`][2]); - assert.equal(wizardForm.page, 2); - setTimeout(() => { - Harness.clickElement(wizardForm, wizardForm.refs[`${wizardForm.wizardKey}-link`][0]); - assert.equal(wizardForm.page, 0); - setTimeout(() => { - const aInput = wizardForm.currentPage.getComponent('a'); - assert.equal(aInput.errors.length, 1); - assert.equal(aInput.errors[0].message, 'a must have at least 4 characters.'); - done(); - }, 100); - }, 100); - }) - .catch((err) => done(err)); - }); - - it('Should leave errors for invalid fields after validation on next button and entering valid data in one of the fields', function(done) { - const formElement = document.createElement('div'); - wizardForm = new Wizard(formElement); - wizardForm.setForm(wizard1).then(() => { - Harness.clickElement(wizardForm, wizardForm.refs[`${wizardForm.wizardKey}-next`]); - setTimeout(() => { - assert.equal(wizardForm.errors.length, 2); - - const inputEvent = new Event('input', { bubbles: true, cancelable: true }); - const inputA = formElement.querySelector('input[name="data[a]"]'); - - for (let i = 0; i < 5; i++) { - inputA.value += i; - inputA.dispatchEvent(inputEvent); - } - - setTimeout(() => { - assert.equal(wizardForm.errors.length, 1); - done(); - }, 250); - }, 250); - }) - .catch((err) => done(err)); - }); - - it('Should not set components errors if in readOnly mode', function(done) { - const formElement = document.createElement('div'); - wizardForm = new Wizard(formElement, { readOnly: true }); - wizardForm.setForm(wizard).then(() => { - Harness.testSubmission(wizardForm, { - data: { - a: '1', - textField: 'aaa', - c: '0' - } - }); - - Harness.clickElement(wizardForm, wizardForm.refs[`${wizardForm.wizardKey}-link`][2]); - assert.equal(wizardForm.page, 2); - Harness.clickElement(wizardForm, wizardForm.refs[`${wizardForm.wizardKey}-link`][0]); - assert.equal(wizardForm.page, 0); - const aInput = wizardForm.currentPage.getComponent('a'); - assert.equal(aInput.errors.length, 0); - done(); + Harness.clickElement( + wizardForm, + wizardForm.refs[`${wizardForm.wizardKey}-link`][2], + ); + assert.equal(wizardForm.page, 2); + Harness.clickElement( + wizardForm, + wizardForm.refs[`${wizardForm.wizardKey}-link`][0], + ); + assert.equal(wizardForm.page, 0); + const aInput = wizardForm.currentPage.getComponent('a'); + assert.equal(aInput.errors.length, 0); + done(); + }); }); - }); - it('Should keep values during validation that are conditionally visible', async function() { - const submission = { - data: { - a: true, - b: 'b', - c: 'c' - } - }; + it('Should keep values during validation that are conditionally visible', async function () { + const submission = { + data: { + a: true, + b: 'b', + c: 'c', + }, + }; - const form = await Formio.createForm(wizardCond, {}); + const form = await Formio.createForm(wizardCond, {}); - form.validator.config = { - db: {}, - token: '', - form: wizardCond, - submission: submission - }; + form.validator.config = { + db: {}, + token: '', + form: wizardCond, + submission: submission, + }; - // Set the submission data - form.data = submission.data; + // Set the submission data + form.data = submission.data; - assert.deepEqual(form.data, submission.data, 'Should set data properly'); - // Perform calculations and conditions. - form.calculateValue(); - form.checkConditions(); + assert.deepEqual( + form.data, + submission.data, + 'Should set data properly', + ); + // Perform calculations and conditions. + form.calculateValue(); + form.checkConditions(); - assert(form.components[2], 'Should contain the 3rd page'); - assert.equal(form.components[2].visible, true, 'Should be visible'); + assert(form.components[2], 'Should contain the 3rd page'); + assert.equal(form.components[2].visible, true, 'Should be visible'); - const textField = form.components[2].components[0]; + const textField = form.components[2].components[0]; - assert.equal(textField.visible, true, 'Inner components of the 3rd page should be visible'); - assert.equal(textField.parentVisible, true, 'parentVisible of the 3rd page\'s child components should be equal to true'); + assert.equal( + textField.visible, + true, + 'Inner components of the 3rd page should be visible', + ); + assert.equal( + textField.parentVisible, + true, + "parentVisible of the 3rd page's child components should be equal to true", + ); - // Reset the data - form.data = {}; + // Reset the data + form.data = {}; - form.setValue(submission, { - sanitize: true - }); + form.setValue(submission, { + sanitize: true, + }); - // Check the validity of the form. - const valid = await form.checkAsyncValidity(null, true); + // Check the validity of the form. + const valid = await form.checkAsyncValidity(null, true); - assert(valid, 'Should be valid'); - assert.equal(form.data.c, 'c', 'Should keep the value of a conditionally visible page.'); - }); + assert(valid, 'Should be valid'); + assert.equal( + form.data.c, + 'c', + 'Should keep the value of a conditionally visible page.', + ); + }); - it('If allowPrevious is given, the breadcrumb bar should be clickable for visited tabs.', function(done) { - const formElement = document.createElement('div'); - wizardForm = new Wizard(formElement, { allowPrevious: true }); - wizardForm.setForm(wizardWithAllowPrevious) - .then(() => { - Harness.clickElement(wizardForm, wizardForm.refs[`${wizardForm.wizardKey}-link`][1]); + it('If allowPrevious is given, the breadcrumb bar should be clickable for visited tabs.', function (done) { + const formElement = document.createElement('div'); + wizardForm = new Wizard(formElement, { allowPrevious: true }); + wizardForm + .setForm(wizardWithAllowPrevious) + .then(() => { + Harness.clickElement( + wizardForm, + wizardForm.refs[`${wizardForm.wizardKey}-link`][1], + ); - setTimeout(() => { - assert.equal(wizardForm.page, 0, 'Should be disabled for unvisited tabs.'); - Harness.clickElement(wizardForm, wizardForm.refs[`${wizardForm.wizardKey}-next`]); + setTimeout(() => { + assert.equal( + wizardForm.page, + 0, + 'Should be disabled for unvisited tabs.', + ); + Harness.clickElement( + wizardForm, + wizardForm.refs[`${wizardForm.wizardKey}-next`], + ); - setTimeout(() => { - assert.equal(wizardForm.enabledIndex, 1, 'Should be clickable for visited tabs.'); - done(); - }, 100); - }, 100); - }) - .catch(done); - }); - - it('Should scroll to the top of the page when the page is changed', function(done) { - const formElement = document.createElement('div'); - wizardForm = new Wizard(formElement); - wizardForm.setForm(wizardWithHighPages) - .then(() => { - wizardForm.scrollIntoView(wizardForm.refs[`${wizardForm.wizardKey}-next`]); - wizardForm.setPage(1); - setTimeout(() => { - assert.equal(wizardForm.refs[wizardForm.wizardKey].scrollTop, 0, 'The top edge of the page should be aligned to the top edge of the window'); - done(); - }, 350); - }) - .catch(done); - }); - - it('Should show the actual page after re-rendering due to nested wizards.', function(done) { - const formElement = document.createElement('div'); - wizardForm = new Wizard(formElement); - wizardForm.setForm(wizardWithNestedWizard) - .then(() => { - assert.equal(wizardForm.element.querySelector('.wizard-page'), wizardForm.allPages[0].components[0].element.parentNode); - done(); - }) - .catch(done); - }); - // BUG - uncomment once fixed (ticket FIO-6043) - // it('Should render all pages as a part of wizard pagination', (done) => { - // const formElement = document.createElement('div'); - // const wizard = new Wizard(formElement); - // const childForm = _.cloneDeep(wizardChildForm); - // const clickEvent = new Event('click'); - - // wizard.setForm(wizardParentForm).then(() => { - // assert.equal(wizard.components.length, 2); - // assert.equal(wizard.allPages.length, 2); - // assert.equal(wizard.allPages[1].component.title, 'Page 3'); - - // const radioComp = wizard.getComponent('radio1'); - - // radioComp.setValue('yes'); - // wizard.render(); - - // setTimeout(() => { - // const nestedFormComp = wizard.getComponent('formNested'); - // nestedFormComp.loadSubForm = () => { - // nestedFormComp.formObj = childForm; - // nestedFormComp.subFormLoading = false; - - // return new Promise((resolve) => resolve(childForm)); - // }; - // nestedFormComp.createSubForm(); - - // setTimeout(() => { - // assert.equal(wizard.components.length, 3); - // assert.equal(wizard.allPages.length, 4); - // assert.equal(wizard.allPages[1].component.title, 'Child Page 1'); - - // const checboxComp = wizard.getComponent('checkbox'); - - // checboxComp.setValue(true); - // wizard.render(); - - // setTimeout(() => { - // assert.equal(wizard.components.length, 3); - // assert.equal(wizard.allPages.length, 5); - // assert.equal(wizard.allPages[1].component.title, 'Page 2'); - // assert.equal(wizard.element.querySelector('input[name="data[textFieldNearForm]"]'), null); - - // const nextPageBtn = wizard.refs[`${wizard.wizardKey}-next`]; - - // nextPageBtn.dispatchEvent(clickEvent); - - // setTimeout(() => { - // assert.equal(wizard.component.title, 'Page 2'); - // assert.ok(wizard.element.querySelector('input[name="data[textFieldNearForm]"]')); - - // done(); - // }, 200); - // }, 200); - // }, 200); - // }, 200); - // }).catch(done); - // }); - - describe('Conditional pages', function() { - it('Should remove page from header when it is hidden', function(done) { - const formElement = document.createElement('div'); - const form = new Wizard(formElement); - form.setForm(wizardWithConditionallyVisiblePage) - .then(() => { - const textField = form.getComponent(['textField']); - Harness.dispatchEvent( - 'input', - textField.element, - '[name="data[textField]"]', - (input) => input.value = 'hide', - ); - assert.equal(form.refs[`wizard-${form.id}-link`].length, 3, 'Should show all the pages in header'); - - setTimeout(() => { - assert.equal(textField.dataValue, 'hide', 'Should set value'); - const page2 = form.getComponent(['page2']); - assert.equal(page2.visible, false, 'Should be hidden by logic'); - assert.equal(form.refs[`wizard-${form.id}-link`].length, 2, 'Should remove invisible pages from header'); - done(); - }, 300); - }) - .catch(done); + setTimeout(() => { + assert.equal( + wizardForm.enabledIndex, + 1, + 'Should be clickable for visited tabs.', + ); + done(); + }, 100); + }, 100); + }) + .catch(done); }); - it('Should open and make visible nested forms', function(done) { - const formElement = document.createElement('div'); - Formio.createForm(formElement, nestedConditionalWizard).then((form) => { - const nestedFormRadio = form.getComponent(['nestedForm']); - - nestedFormRadio.setValue('yes'); - setTimeout(() => { - const secondQuestionToOpenNestedFormRadio = form.getComponent(['secondQuestionToOpenNestedForm']); - assert(secondQuestionToOpenNestedFormRadio.visible, 'Should become visible'); - secondQuestionToOpenNestedFormRadio.setValue('openChildForm'); - - setTimeout(() => { - const nestedForm = form.getComponent(['form']); - assert(nestedForm.visible, 'Should become visible'); - nestedForm.subForm.components.forEach((comp) => { - assert.equal(comp.root, nestedForm.subForm, 'The root of the nested components should be set to the' + - ' Wizard itself'); - }); - const nestedRadio1 = nestedForm.subForm.getComponent(['radio1']); + it('Should scroll to the top of the page when the page is changed', function (done) { + const formElement = document.createElement('div'); + wizardForm = new Wizard(formElement); + wizardForm + .setForm(wizardWithHighPages) + .then(() => { + wizardForm.scrollIntoView( + wizardForm.refs[`${wizardForm.wizardKey}-next`], + ); + wizardForm.setPage(1); + setTimeout(() => { + assert.equal( + wizardForm.refs[wizardForm.wizardKey].scrollTop, + 0, + 'The top edge of the page should be aligned to the top edge of the window', + ); + done(); + }, 350); + }) + .catch(done); + }); - nestedRadio1.setValue('unhidePage3'); + it('Should show the actual page after re-rendering due to nested wizards.', function (done) { + const formElement = document.createElement('div'); + wizardForm = new Wizard(formElement); + wizardForm + .setForm(wizardWithNestedWizard) + .then(() => { + assert.equal( + wizardForm.element.querySelector('.wizard-page'), + wizardForm.allPages[0].components[0].element.parentNode, + ); + done(); + }) + .catch(done); + }); + // BUG - uncomment once fixed (ticket FIO-6043) + // it('Should render all pages as a part of wizard pagination', (done) => { + // const formElement = document.createElement('div'); + // const wizard = new Wizard(formElement); + // const childForm = _.cloneDeep(wizardChildForm); + // const clickEvent = new Event('click'); + + // wizard.setForm(wizardParentForm).then(() => { + // assert.equal(wizard.components.length, 2); + // assert.equal(wizard.allPages.length, 2); + // assert.equal(wizard.allPages[1].component.title, 'Page 3'); + + // const radioComp = wizard.getComponent('radio1'); + + // radioComp.setValue('yes'); + // wizard.render(); + + // setTimeout(() => { + // const nestedFormComp = wizard.getComponent('formNested'); + // nestedFormComp.loadSubForm = () => { + // nestedFormComp.formObj = childForm; + // nestedFormComp.subFormLoading = false; + + // return new Promise((resolve) => resolve(childForm)); + // }; + // nestedFormComp.createSubForm(); + + // setTimeout(() => { + // assert.equal(wizard.components.length, 3); + // assert.equal(wizard.allPages.length, 4); + // assert.equal(wizard.allPages[1].component.title, 'Child Page 1'); + + // const checboxComp = wizard.getComponent('checkbox'); + + // checboxComp.setValue(true); + // wizard.render(); + + // setTimeout(() => { + // assert.equal(wizard.components.length, 3); + // assert.equal(wizard.allPages.length, 5); + // assert.equal(wizard.allPages[1].component.title, 'Page 2'); + // assert.equal(wizard.element.querySelector('input[name="data[textFieldNearForm]"]'), null); + + // const nextPageBtn = wizard.refs[`${wizard.wizardKey}-next`]; + + // nextPageBtn.dispatchEvent(clickEvent); + + // setTimeout(() => { + // assert.equal(wizard.component.title, 'Page 2'); + // assert.ok(wizard.element.querySelector('input[name="data[textFieldNearForm]"]')); + + // done(); + // }, 200); + // }, 200); + // }, 200); + // }, 200); + // }).catch(done); + // }); + + describe('Conditional pages', function () { + it('Should remove page from header when it is hidden', function (done) { + const formElement = document.createElement('div'); + const form = new Wizard(formElement); + form.setForm(wizardWithConditionallyVisiblePage) + .then(() => { + const textField = form.getComponent(['textField']); + Harness.dispatchEvent( + 'input', + textField.element, + '[name="data[textField]"]', + (input) => (input.value = 'hide'), + ); + assert.equal( + form.refs[`wizard-${form.id}-link`].length, + 3, + 'Should show all the pages in header', + ); - setTimeout(() => { - const pages = form.element.querySelectorAll('.formio-form nav .pagination .page-item'); - assert.equal(pages.length, 3, 'Should show the hidden initially page'); + setTimeout(() => { + assert.equal( + textField.dataValue, + 'hide', + 'Should set value', + ); + const page2 = form.getComponent(['page2']); + assert.equal( + page2.visible, + false, + 'Should be hidden by logic', + ); + assert.equal( + form.refs[`wizard-${form.id}-link`].length, + 2, + 'Should remove invisible pages from header', + ); + done(); + }, 300); + }) + .catch(done); + }); - secondQuestionToOpenNestedFormRadio.setValue(2); + it('Should open and make visible nested forms', function (done) { + const formElement = document.createElement('div'); + Formio.createForm(formElement, nestedConditionalWizard) + .then((form) => { + const nestedFormRadio = form.getComponent(['nestedForm']); - setTimeout(() => { - assert(!nestedForm.visible, 'Should become hidden'); - secondQuestionToOpenNestedFormRadio.setValue('openChildForm'); + nestedFormRadio.setValue('yes'); + setTimeout(() => { + const secondQuestionToOpenNestedFormRadio = + form.getComponent([ + 'secondQuestionToOpenNestedForm', + ]); + assert( + secondQuestionToOpenNestedFormRadio.visible, + 'Should become visible', + ); + secondQuestionToOpenNestedFormRadio.setValue( + 'openChildForm', + ); - setTimeout(() => { - assert(nestedForm.visible, 'Should become visible again'); - secondQuestionToOpenNestedFormRadio.setValue('openChildForm'); + setTimeout(() => { + const nestedForm = form.getComponent(['form']); + assert(nestedForm.visible, 'Should become visible'); + nestedForm.subForm.components.forEach((comp) => { + assert.equal( + comp.root, + nestedForm.subForm, + 'The root of the nested components should be set to the' + + ' Wizard itself', + ); + }); + const nestedRadio1 = + nestedForm.subForm.getComponent(['radio1']); + + nestedRadio1.setValue('unhidePage3'); + + setTimeout(() => { + const pages = form.element.querySelectorAll( + '.formio-form nav .pagination .page-item', + ); + assert.equal( + pages.length, + 3, + 'Should show the hidden initially page', + ); + + secondQuestionToOpenNestedFormRadio.setValue(2); + + setTimeout(() => { + assert( + !nestedForm.visible, + 'Should become hidden', + ); + secondQuestionToOpenNestedFormRadio.setValue( + 'openChildForm', + ); + + setTimeout(() => { + assert( + nestedForm.visible, + 'Should become visible again', + ); + secondQuestionToOpenNestedFormRadio.setValue( + 'openChildForm', + ); + + nestedForm.subForm.components.forEach( + (comp) => { + assert.equal( + comp.root, + nestedForm.subForm, + 'The root of the nested components should be set to the' + + ' Wizard itself', + ); + }, + ); + const nestedRadio1 = + nestedForm.subForm.getComponent([ + 'radio1', + ]); + + nestedRadio1.setValue('unhidePage3'); + + setTimeout(() => { + const pages = + form.element.querySelectorAll( + '.formio-form nav .pagination .page-item', + ); + assert.equal( + pages.length, + 3, + 'Should show the hidden initially page', + ); + + done(); + }); + }, 400); + }, 500); + }, 400); + }, 500); + }, 400); + }) + .catch(done); + }); + }); - nestedForm.subForm.components.forEach((comp) => { - assert.equal(comp.root, nestedForm.subForm, 'The root of the nested components should be set to the' + - ' Wizard itself'); - }); - const nestedRadio1 = nestedForm.subForm.getComponent(['radio1']); + it('Should have proper values for localRoot', function (done) { + const formElement = document.createElement('div'); + const wizard = new Wizard(formElement); + const form = _.cloneDeep(wizardWithPanel); + const nestedMiddleForm = _.cloneDeep(wizardWithWizard); + const childForm = _.cloneDeep(simpleTwoPagesWizard); + + wizard.setForm(form).then(() => { + const nestedMiddleFormComp = wizard.getComponent('middleForm'); + nestedMiddleFormComp.loadSubForm = () => { + nestedMiddleFormComp.formObj = nestedMiddleForm; + nestedMiddleFormComp.subFormLoading = false; + return new Promise((resolve) => resolve(nestedMiddleForm)); + }; + nestedMiddleFormComp.createSubForm(); - nestedRadio1.setValue('unhidePage3'); + setTimeout(() => { + const middleWizard = nestedMiddleFormComp.subForm; - setTimeout(() => { - const pages = form.element.querySelectorAll('.formio-form nav .pagination .page-item'); - assert.equal(pages.length, 3, 'Should show the hidden initially page'); + const nestedChildFormComp = + middleWizard.getComponent('childForm'); + nestedChildFormComp.loadSubForm = () => { + nestedChildFormComp.formObj = childForm; + nestedChildFormComp.subFormLoading = false; + return new Promise((resolve) => resolve(childForm)); + }; + nestedChildFormComp.createSubForm(); + setTimeout(() => { + const childWizard = nestedChildFormComp.subForm; + + assert.equal(wizard.id, wizard.root.id); + assert.equal(wizard.id, wizard.localRoot.id); + assert.equal(wizard.root.id, wizard.localRoot.id); + assert.notEqual(middleWizard.id, middleWizard.root.id); + assert.equal(middleWizard.id, middleWizard.localRoot.id); + assert.notEqual( + middleWizard.root.id, + middleWizard.localRoot.id, + ); + assert.notEqual(childWizard.id, childWizard.root.id); + assert.notEqual(childWizard.id, childWizard.localRoot.id); + assert.equal(childWizard.root.id, childWizard.localRoot.id); done(); - }); - }, 400); - }, 500); - }, 400); - }, 500); - }, 400); - }).catch(done); - }); - }); - - it('Should have proper values for localRoot', function(done) { - const formElement = document.createElement('div'); - const wizard = new Wizard(formElement); - const form = _.cloneDeep(wizardWithPanel); - const nestedMiddleForm = _.cloneDeep(wizardWithWizard); - const childForm = _.cloneDeep(simpleTwoPagesWizard); - - wizard.setForm(form).then(() => { - const nestedMiddleFormComp = wizard.getComponent('middleForm'); - nestedMiddleFormComp.loadSubForm = () => { - nestedMiddleFormComp.formObj = nestedMiddleForm; - nestedMiddleFormComp.subFormLoading = false; - return new Promise((resolve) => resolve(nestedMiddleForm)); - }; - nestedMiddleFormComp.createSubForm(); - - setTimeout(() => { - const middleWizard = nestedMiddleFormComp.subForm; - - const nestedChildFormComp = middleWizard.getComponent('childForm'); - nestedChildFormComp.loadSubForm = () => { - nestedChildFormComp.formObj = childForm; - nestedChildFormComp.subFormLoading = false; - return new Promise((resolve) => resolve(childForm)); - }; - nestedChildFormComp.createSubForm(); - - setTimeout(() => { - const childWizard = nestedChildFormComp.subForm; - - assert.equal(wizard.id, wizard.root.id); - assert.equal(wizard.id, wizard.localRoot.id); - assert.equal(wizard.root.id, wizard.localRoot.id); - assert.notEqual(middleWizard.id, middleWizard.root.id); - assert.equal(middleWizard.id, middleWizard.localRoot.id); - assert.notEqual(middleWizard.root.id, middleWizard.localRoot.id); - assert.notEqual(childWizard.id, childWizard.root.id); - assert.notEqual(childWizard.id, childWizard.localRoot.id); - assert.equal(childWizard.root.id, childWizard.localRoot.id); - done(); - }, 200); - }, 200); + }, 200); + }, 200); + }); }); - }); - it('Should keep wizard pages separate from edit grid inner wizard pages', function(done) { - const formElement = document.createElement('div'); - const wizard = new Wizard(formElement); - const form = _.cloneDeep(wizardWithNestedWizardInEditGrid); - const childForm = _.cloneDeep(simpleTwoPagesWizard); + it('Should keep wizard pages separate from edit grid inner wizard pages', function (done) { + const formElement = document.createElement('div'); + const wizard = new Wizard(formElement); + const form = _.cloneDeep(wizardWithNestedWizardInEditGrid); + const childForm = _.cloneDeep(simpleTwoPagesWizard); - wizard.setForm(form).then(() => { - assert.equal(wizard.components.length, 1); - assert.equal(wizard.allPages.length, 1); + wizard.setForm(form).then(() => { + assert.equal(wizard.components.length, 1); + assert.equal(wizard.allPages.length, 1); - const editGrid = wizard.getComponent('editGrid'); - editGrid.addRow(); + const editGrid = wizard.getComponent('editGrid'); + editGrid.addRow(); - setTimeout(() => { - const nestedFormComp = wizard.getComponent('formNested'); + setTimeout(() => { + const nestedFormComp = wizard.getComponent('formNested'); - nestedFormComp.loadSubForm = () => { - nestedFormComp.formObj = childForm; - nestedFormComp.subFormLoading = false; + nestedFormComp.loadSubForm = () => { + nestedFormComp.formObj = childForm; + nestedFormComp.subFormLoading = false; - return new Promise((resolve) => resolve(childForm)); - }; - nestedFormComp.createSubForm(); - - setTimeout(() => { - assert.equal(nestedFormComp.subForm.components.length, 2); - assert.equal(nestedFormComp.subForm.allPages.length, 2); - assert.equal(wizard.components.length, 1); - assert.equal(wizard.allPages.length, 1); - done(); - }, 200); - }, 200); - }); - }); - - it('Should navigate wizard pages and submit form using \'Save on Enter\' option', function(done) { - const formElement = document.createElement('div'); - const wizard = new Wizard(formElement); - - wizard.setForm(wizardNavigateOrSaveOnEnter.form).then(() => { - const pressEnter = () => { - const event = new KeyboardEvent('keyup', { - bubbles : true, - cancelable : true, - key : 'Enter', - keyCode : 13 + return new Promise((resolve) => resolve(childForm)); + }; + nestedFormComp.createSubForm(); + + setTimeout(() => { + assert.equal(nestedFormComp.subForm.components.length, 2); + assert.equal(nestedFormComp.subForm.allPages.length, 2); + assert.equal(wizard.components.length, 1); + assert.equal(wizard.allPages.length, 1); + done(); + }, 200); + }, 200); }); - document.dispatchEvent(event); - }; + }); - const checkPage = (pageNumber) => { - assert.equal(wizard.page, pageNumber, `Should open wizard page ${pageNumber + 1}`); - }; - checkPage(0); - pressEnter(); + it("Should navigate wizard pages and submit form using 'Save on Enter' option", function (done) { + const formElement = document.createElement('div'); + const wizard = new Wizard(formElement); + + wizard + .setForm(wizardNavigateOrSaveOnEnter.form) + .then(() => { + const pressEnter = () => { + const event = new KeyboardEvent('keyup', { + bubbles: true, + cancelable: true, + key: 'Enter', + keyCode: 13, + }); + document.dispatchEvent(event); + }; + + const checkPage = (pageNumber) => { + assert.equal( + wizard.page, + pageNumber, + `Should open wizard page ${pageNumber + 1}`, + ); + }; + checkPage(0); + pressEnter(); - setTimeout(() => { - checkPage(1); + setTimeout(() => { + checkPage(1); - pressEnter(); + pressEnter(); - setTimeout(() => { - checkPage(2); + setTimeout(() => { + checkPage(2); - pressEnter(); + pressEnter(); - setTimeout(() => { - assert.equal(wizard.submission.state, 'submitted', 'Should submit the form'); + setTimeout(() => { + assert.equal( + wizard.submission.state, + 'submitted', + 'Should submit the form', + ); - done(); - }, 50); - }, 50); - }, 50); - }) - .catch((err) => done(err)); - }); - - it('Should proper validate nested wizard fields', function(done) { - const formElement = document.createElement('div'); - const wizard = new Wizard(formElement); - const childForm = _.cloneDeep(wizardWithFieldsValidationChild); - const parentForm = _.cloneDeep(wizardWithFieldsValidationParent); - const clickEvent = new Event('click'); - - wizard.setForm(parentForm).then(() => { - const nestedFormComp = wizard.getComponent('formNested'); - nestedFormComp.loadSubForm = () => { - nestedFormComp.formObj = childForm; - nestedFormComp.subFormLoading = false; - return new Promise((resolve) => resolve(childForm)); - }; - nestedFormComp.createSubForm(); - - setTimeout(() => { - const textField = wizard.getComponent('textField'); - const testValidation = wizard.getComponent('testValidation'); - textField.setValue('one'); - testValidation.setValue('two'); - wizard.render(); - - const checkPage = (pageNumber) => { - assert.equal(wizard.page, pageNumber); - }; - const nextPageBtn = wizard.refs[`${wizard.wizardKey}-next`]; + done(); + }, 50); + }, 50); + }, 50); + }) + .catch((err) => done(err)); + }); - setTimeout(() => { - nextPageBtn.dispatchEvent(clickEvent); + it('Should proper validate nested wizard fields', function (done) { + const formElement = document.createElement('div'); + const wizard = new Wizard(formElement); + const childForm = _.cloneDeep(wizardWithFieldsValidationChild); + const parentForm = _.cloneDeep(wizardWithFieldsValidationParent); + const clickEvent = new Event('click'); - setTimeout(() => { - checkPage(0); - assert.equal(wizard.errors.length, 1); - assert.equal(wizard.refs.errorRef.length, wizard.errors.length); - testValidation.setValue('one'); - nextPageBtn.dispatchEvent(clickEvent); + wizard.setForm(parentForm).then(() => { + const nestedFormComp = wizard.getComponent('formNested'); + nestedFormComp.loadSubForm = () => { + nestedFormComp.formObj = childForm; + nestedFormComp.subFormLoading = false; + return new Promise((resolve) => resolve(childForm)); + }; + nestedFormComp.createSubForm(); setTimeout(() => { - checkPage(1); - assert.equal(wizard.errors.length, 0); - done(); + const textField = wizard.getComponent('textField'); + const testValidation = wizard.getComponent('testValidation'); + textField.setValue('one'); + testValidation.setValue('two'); + wizard.render(); + + const checkPage = (pageNumber) => { + assert.equal(wizard.page, pageNumber); + }; + const nextPageBtn = wizard.refs[`${wizard.wizardKey}-next`]; + + setTimeout(() => { + nextPageBtn.dispatchEvent(clickEvent); + + setTimeout(() => { + checkPage(0); + assert.equal(wizard.errors.length, 1); + assert.equal( + wizard.refs.errorRef.length, + wizard.errors.length, + ); + testValidation.setValue('one'); + nextPageBtn.dispatchEvent(clickEvent); + + setTimeout(() => { + checkPage(1); + assert.equal(wizard.errors.length, 0); + done(); + }, 200); + }, 200); + }, 200); }, 200); - }, 200); - }, 200); - }, 200); + }); }); - }); }); diff --git a/src/WizardBuilder.js b/src/WizardBuilder.js index 235c5e9171..0965a783dc 100644 --- a/src/WizardBuilder.js +++ b/src/WizardBuilder.js @@ -5,305 +5,359 @@ import _ from 'lodash'; import { fastCloneDeep } from './utils/utils'; export default class WizardBuilder extends WebformBuilder { - constructor() { - let element, options; - if (arguments[0] instanceof HTMLElement || arguments[1]) { - element = arguments[0]; - options = arguments[1]; + constructor() { + let element, options; + if (arguments[0] instanceof HTMLElement || arguments[1]) { + element = arguments[0]; + options = arguments[1]; + } else { + options = arguments[0]; + } + // Reset skipInit in case PDFBuilder has set it. + options.skipInit = false; + options.display = 'wizard'; + + super(element, options); + + this._form = { + components: [this.getPageConfig(1)], + }; + + this.page = 0; + + // Need to create a component order for each group. + for (const group in this.groups) { + if (this.groups[group] && this.groups[group].components) { + this.groups[group].componentOrder = Object.keys( + this.groups[group].components, + ) + .map((key) => this.groups[group].components[key]) + .filter((component) => component && !component.ignore) + .sort((a, b) => a.weight - b.weight) + .map((component) => component.key); + } + } + + const originalRenderComponentsHook = + this.options.hooks.renderComponents; + this.options.hooks.renderComponents = (html, { components, self }) => { + if (self.type === 'form' && !self.root) { + return html; + } else { + return originalRenderComponentsHook(html, { components, self }); + } + }; + + const originalAttachComponentsHook = + this.options.hooks.attachComponents; + this.options.hooks.attachComponents = ( + element, + components, + container, + component, + ) => { + if (component.type === 'form' && !component.root) { + return element; + } + + return originalAttachComponentsHook( + element, + components, + container, + component, + ); + }; + + // Wizard pages don't replace themselves in the right array. Do that here. + this.on( + 'saveComponent', + (component, originalComponent) => { + const webformComponents = this.webform.components.map( + ({ component }) => component, + ); + if (this._form.components.includes(originalComponent)) { + this._form.components[ + this._form.components.indexOf(originalComponent) + ] = component; + this.rebuild(); + } else if (webformComponents.includes(originalComponent)) { + this._form.components.push(component); + this.rebuild(); + } else { + // Fallback to look for panel based on key. + const formComponentIndex = this._form.components.findIndex( + (comp) => originalComponent.key === comp.key, + ); + if (formComponentIndex !== -1) { + this._form.components[formComponentIndex] = component; + this.rebuild(); + } + } + }, + true, + ); } - else { - options = arguments[0]; + + removeComponent(component, parent, original) { + const remove = super.removeComponent(component, parent, original); + // If user agrees to remove the whole group of the components and it could be a Wizard page, find it and remove + if (remove && component.type === 'panel') { + const pageIndex = this.pages.findIndex( + (page) => page.key === component.key, + ); + const componentIndex = this._form.components.findIndex( + (comp) => comp.key === component.key, + ); + if (pageIndex !== -1) { + this.removePage(pageIndex, componentIndex); + } + } + return remove; } - // Reset skipInit in case PDFBuilder has set it. - options.skipInit = false; - options.display = 'wizard'; - - super(element, options); - - this._form = { - components: [ - this.getPageConfig(1), - ], - }; - - this.page = 0; - - // Need to create a component order for each group. - for (const group in this.groups) { - if (this.groups[group] && this.groups[group].components) { - this.groups[group].componentOrder = Object.keys(this.groups[group].components) - .map(key => this.groups[group].components[key]) - .filter(component => component && !component.ignore) - .sort((a, b) => a.weight - b.weight) - .map(component => component.key); - } + + allowDrop(element) { + return this.webform && + this.webform.refs && + this.webform.refs.webform === element + ? false + : true; } - const originalRenderComponentsHook = this.options.hooks.renderComponents; - this.options.hooks.renderComponents = (html, { components, self }) => { - if (self.type === 'form' && !self.root) { - return html; - } - else { - return originalRenderComponentsHook(html, { components, self }); - } - }; - - const originalAttachComponentsHook = this.options.hooks.attachComponents; - this.options.hooks.attachComponents = (element, components, container, component) => { - if (component.type === 'form' && !component.root) { - return element; - } - - return originalAttachComponentsHook(element, components, container, component); - }; - - // Wizard pages don't replace themselves in the right array. Do that here. - this.on('saveComponent', (component, originalComponent) => { - const webformComponents = this.webform.components.map(({ component }) => component); - if (this._form.components.includes(originalComponent)) { - this._form.components[this._form.components.indexOf(originalComponent)] = component; - this.rebuild(); - } - else if (webformComponents.includes(originalComponent)) { - this._form.components.push(component); - this.rebuild(); - } - else { - // Fallback to look for panel based on key. - const formComponentIndex = this._form.components.findIndex((comp) => originalComponent.key === comp.key); - if (formComponentIndex !== -1) { - this._form.components[formComponentIndex] = component; - this.rebuild(); - } - } - }, true); - } - - removeComponent(component, parent, original) { - const remove = super.removeComponent(component, parent, original); - // If user agrees to remove the whole group of the components and it could be a Wizard page, find it and remove - if (remove && component.type === 'panel') { - const pageIndex = this.pages.findIndex((page) => page.key === component.key); - const componentIndex = this._form.components.findIndex((comp) => comp.key === component.key); - if (pageIndex !== -1) { - this.removePage(pageIndex, componentIndex); - } + get pages() { + return _.filter(this._form.components, { type: 'panel' }); } - return remove; - } - - allowDrop(element) { - return (this.webform && this.webform.refs && this.webform.refs.webform === element) ? false : true; - } - - get pages() { - return _.filter(this._form.components, { type: 'panel' }); - } - - get currentPage() { - const pages = this.pages; - return (pages && (pages.length >= this.page)) ? pages[this.page] : null; - } - - set form(value) { - this._form = value; - if (!this._form.components || !Array.isArray(this._form.components)) { - this._form.components = []; + + get currentPage() { + const pages = this.pages; + return pages && pages.length >= this.page ? pages[this.page] : null; } - if (this.pages.length === 0) { - const components = this._form.components.filter((component) => component.type !== 'button'); - this._form.components = [this.getPageConfig(1, components)]; + set form(value) { + this._form = value; + if (!this._form.components || !Array.isArray(this._form.components)) { + this._form.components = []; + } + + if (this.pages.length === 0) { + const components = this._form.components.filter( + (component) => component.type !== 'button', + ); + this._form.components = [this.getPageConfig(1, components)]; + } else { + const components = this._form.components.filter( + (component) => + component.type !== 'button' || + component.action !== 'submit', + ); + this._form.components = components; + } + this.rebuild(); } - else { - const components = this._form.components - .filter((component) => component.type !== 'button' || component.action !== 'submit'); - this._form.components = components; + + get form() { + return this._form; } - this.rebuild(); - } - - get form() { - return this._form; - } - - get schema() { - _.assign(this.currentPage, this.webform._form.components[0]); - const webform = new Webform(this.options); - webform.setForm(this._form, { noEmit: true }); - return webform.schema; - } - - render() { - return this.renderTemplate('builderWizard', { - sidebar: this.renderTemplate('builderSidebar', { - scrollEnabled: this.sideBarScroll, - groupOrder: this.groupOrder, - groupId: `builder-sidebar-${this.id}`, - groups: this.groupOrder.map((groupKey) => this.renderTemplate('builderSidebarGroup', { - group: this.groups[groupKey], - groupKey, - groupId: `builder-sidebar-${this.id}`, - subgroups: this.groups[groupKey].subgroups.map((group) => this.renderTemplate('builderSidebarGroup', { - group, - groupKey: group.key, - groupId: `group-container-${groupKey}`, - subgroups: [] - })), - })), - }), - pages: this.pages, - form: this.webform.render(), - }); - } - - attach(element) { - this.loadRefs(element, { - addPage: 'multiple', - gotoPage: 'multiple', - }); - - this.refs.gotoPage.forEach((page, index) => { - page.parentNode.dragInfo = { index }; - }); - - if (this.dragulaLib) { - this.navigationDragula = this.dragulaLib([this.element.querySelector('.wizard-pages')], { - moves: (el) => (!el.classList.contains('wizard-add-page')), - accepts: (el, target, source, sibling) => (sibling ? true : false), - }) - .on('drop', this.onReorder.bind(this)); + + get schema() { + _.assign(this.currentPage, this.webform._form.components[0]); + const webform = new Webform(this.options); + webform.setForm(this._form, { noEmit: true }); + return webform.schema; } - this.refs.addPage.forEach(link => { - this.addEventListener(link, 'click', (event) => { - event.preventDefault(); - this.addPage(); - }); - }); - - this.refs.gotoPage.forEach((link, index) => { - this.addEventListener(link, 'click', (event) => { - event.preventDefault(); - this.setPage(index); - }); - }); - - return super.attach(element); - } - - detach() { - if (this.navigationDragula) { - this.navigationDragula.destroy(); + render() { + return this.renderTemplate('builderWizard', { + sidebar: this.renderTemplate('builderSidebar', { + scrollEnabled: this.sideBarScroll, + groupOrder: this.groupOrder, + groupId: `builder-sidebar-${this.id}`, + groups: this.groupOrder.map((groupKey) => + this.renderTemplate('builderSidebarGroup', { + group: this.groups[groupKey], + groupKey, + groupId: `builder-sidebar-${this.id}`, + subgroups: this.groups[groupKey].subgroups.map( + (group) => + this.renderTemplate('builderSidebarGroup', { + group, + groupKey: group.key, + groupId: `group-container-${groupKey}`, + subgroups: [], + }), + ), + }), + ), + }), + pages: this.pages, + form: this.webform.render(), + }); } - this.navigationDragula = null; - - super.detach(); - } - - rebuild() { - const page = this.currentPage; - this.webform.setForm({ - display: 'form', - type: 'form', - components: page ? [page] : [], - controller: this._form?.controller || '' - }, { keepAsReference: true }); - return this.redraw(); - } - - addPage(page) { - const newPage = page && page.schema ? fastCloneDeep(page.schema) : this.getPageConfig(this.pages.length + 1); - - BuilderUtils.uniquify(this._form.components, newPage); - this._form.components.push(newPage); - - this.emitSaveComponentEvent( - newPage, - newPage, - this._form, - 'components', - (this._form.components.length - 1), - true, - newPage - ); - - this.emit('change', this._form); - return this.rebuild(); - } - - removePage(pageIndex, componentIndex) { - this._form.components.splice(componentIndex, 1); - this.emit('change', this._form); - - if (pageIndex === this.pages.length) { - // If the last page is removed. - if (pageIndex === 0) { - this._form.components.push(this.getPageConfig(1)); - return this.rebuild(); - } - else { - return this.setPage(pageIndex - 1); - } + + attach(element) { + this.loadRefs(element, { + addPage: 'multiple', + gotoPage: 'multiple', + }); + + this.refs.gotoPage.forEach((page, index) => { + page.parentNode.dragInfo = { index }; + }); + + if (this.dragulaLib) { + this.navigationDragula = this.dragulaLib( + [this.element.querySelector('.wizard-pages')], + { + moves: (el) => !el.classList.contains('wizard-add-page'), + accepts: (el, target, source, sibling) => + sibling ? true : false, + }, + ).on('drop', this.onReorder.bind(this)); + } + + this.refs.addPage.forEach((link) => { + this.addEventListener(link, 'click', (event) => { + event.preventDefault(); + this.addPage(); + }); + }); + + this.refs.gotoPage.forEach((link, index) => { + this.addEventListener(link, 'click', (event) => { + event.preventDefault(); + this.setPage(index); + }); + }); + + return super.attach(element); } - else { - return this.rebuild(); + + detach() { + if (this.navigationDragula) { + this.navigationDragula.destroy(); + } + this.navigationDragula = null; + + super.detach(); } - } - - onReorder(element, _target, _source, sibling) { - const isSiblingAnAddPageButton = sibling?.classList.contains('wizard-add-page'); - // We still can paste before Add Page button - if (!element.dragInfo || (sibling && !sibling.dragInfo && !isSiblingAnAddPageButton)) { - console.warn('There is no Drag Info available for either dragged or sibling element'); - return; + + rebuild() { + const page = this.currentPage; + this.webform.setForm( + { + display: 'form', + type: 'form', + components: page ? [page] : [], + controller: this._form?.controller || '', + }, + { keepAsReference: true }, + ); + return this.redraw(); + } + + addPage(page) { + const newPage = + page && page.schema + ? fastCloneDeep(page.schema) + : this.getPageConfig(this.pages.length + 1); + + BuilderUtils.uniquify(this._form.components, newPage); + this._form.components.push(newPage); + + this.emitSaveComponentEvent( + newPage, + newPage, + this._form, + 'components', + this._form.components.length - 1, + true, + newPage, + ); + + this.emit('change', this._form); + return this.rebuild(); } - const oldPosition = element.dragInfo.index; - //should drop at next sibling position; no next sibling means drop to last position - const newPosition = (sibling && sibling.dragInfo ? sibling.dragInfo.index : this.pages.length); - const movedBelow = newPosition > oldPosition; - const formComponents = fastCloneDeep(this._form.components); - const draggedRowData = this._form.components[oldPosition]; - - //insert element at new position - formComponents.splice(newPosition, 0, draggedRowData); - //remove element from old position (if was moved above, after insertion it's at +1 index) - formComponents.splice(movedBelow ? oldPosition : oldPosition + 1, 1); - this._form.components = fastCloneDeep(formComponents); - - return this.rebuild().then(() => { + + removePage(pageIndex, componentIndex) { + this._form.components.splice(componentIndex, 1); this.emit('change', this._form); - }); - } - setPage(index) { - if (index === this.page) { - return; + if (pageIndex === this.pages.length) { + // If the last page is removed. + if (pageIndex === 0) { + this._form.components.push(this.getPageConfig(1)); + return this.rebuild(); + } else { + return this.setPage(pageIndex - 1); + } + } else { + return this.rebuild(); + } + } + + onReorder(element, _target, _source, sibling) { + const isSiblingAnAddPageButton = + sibling?.classList.contains('wizard-add-page'); + // We still can paste before Add Page button + if ( + !element.dragInfo || + (sibling && !sibling.dragInfo && !isSiblingAnAddPageButton) + ) { + console.warn( + 'There is no Drag Info available for either dragged or sibling element', + ); + return; + } + const oldPosition = element.dragInfo.index; + //should drop at next sibling position; no next sibling means drop to last position + const newPosition = + sibling && sibling.dragInfo + ? sibling.dragInfo.index + : this.pages.length; + const movedBelow = newPosition > oldPosition; + const formComponents = fastCloneDeep(this._form.components); + const draggedRowData = this._form.components[oldPosition]; + + //insert element at new position + formComponents.splice(newPosition, 0, draggedRowData); + //remove element from old position (if was moved above, after insertion it's at +1 index) + formComponents.splice(movedBelow ? oldPosition : oldPosition + 1, 1); + this._form.components = fastCloneDeep(formComponents); + + return this.rebuild().then(() => { + this.emit('change', this._form); + }); } - this.page = index; - return this.rebuild(); - } - - getPageConfig(index, components = []) { - return { - title: `Page ${index}`, - label: `Page ${index}`, - type: 'panel', - key: `page${index}`, - components, - }; - } - - pasteComponent(component) { - if (component instanceof WizardBuilder) { - return; + + setPage(index) { + if (index === this.page) { + return; + } + this.page = index; + return this.rebuild(); } - if (this._form.components.find(comp => _.isEqual(component.component, comp))) { - this.addPage(component); + + getPageConfig(index, components = []) { + return { + title: `Page ${index}`, + label: `Page ${index}`, + type: 'panel', + key: `page${index}`, + components, + }; } - else { - return super.pasteComponent(component); + + pasteComponent(component) { + if (component instanceof WizardBuilder) { + return; + } + if ( + this._form.components.find((comp) => + _.isEqual(component.component, comp), + ) + ) { + this.addPage(component); + } else { + return super.pasteComponent(component); + } } - } } diff --git a/src/WizardBuilder.unit.js b/src/WizardBuilder.unit.js index 4083f0e5f2..d596df4ade 100644 --- a/src/WizardBuilder.unit.js +++ b/src/WizardBuilder.unit.js @@ -8,119 +8,166 @@ import { fastCloneDeep } from './utils/utils'; global.requestAnimationFrame = (cb) => cb(); global.cancelAnimationFrame = () => {}; -describe('WizardBuilder tests', function() { - let formBuilderElement, formBuilder; - - after(function(done) { - destroyWizardBuilder(); - done(); - }); - - function destroyWizardBuilder() { - if (formBuilder && formBuilder.instance) { - formBuilder.instance.destroy(); - document.body.removeChild(formBuilderElement); - } - } - - function createWizardBuilder(form = { display: 'wizard', components: [] }) { - destroyWizardBuilder(); - formBuilderElement = document.createElement('div'); - document.body.appendChild(formBuilderElement); - formBuilder = new FormBuilder(formBuilderElement, { ...form, components: [...form.components] }, {}); - return formBuilder; - } - - it('Test page remove with cancellation', function(done) { - const builder = createWizardBuilder(simpleWizard); - - setTimeout(() => { - const panel = builder.instance.webform.components[0]; - const removeComponentRef = panel.refs.removeComponent; - window.confirm = () => { - return false; - }; - Harness.clickElement(panel, removeComponentRef); - setTimeout(() => { - assert.equal(builder.instance._form.components.length, 5, 'Should not remove the page on cancel'); - assert.equal(builder.instance.webform.components[0].key, 'page1', 'Should stay on page 1 since we' + - ' canceled the deletion'); - done(); - }, 300); - }, 500); - }); - - it('Test page remove with confirmation', function(done) { - const builder = createWizardBuilder(simpleWizard); - - setTimeout(() => { - const panel = builder.instance.webform.components[0]; - const removeComponentRef = panel.refs.removeComponent; - window.confirm = () => { - return true; - }; - Harness.clickElement(panel, removeComponentRef); - setTimeout(() => { - assert.equal(builder.instance._form.components.length, 4, 'Should remove the page on confirm'); - assert.equal(builder.instance.webform.components[0].key, 'page2', 'Should switch to the next page when' + - ' deletion is confirmed'); +describe('WizardBuilder tests', function () { + let formBuilderElement, formBuilder; + + after(function (done) { + destroyWizardBuilder(); done(); - }, 300); - }, 500); - }); - - it('Test page remove with confirmation when remove from component settings window', function(done) { - const builder = createWizardBuilder(simpleWizard); - - setTimeout(() => { - const panel = builder.instance.webform.components[0]; - const editComponentRef = panel.refs.editComponent; - window.confirm = () => { - return true; - }; - - Harness.clickElement(panel, editComponentRef); - setTimeout(() => { - assert(builder.instance.editForm, 'Should create the settings form on component edit'); - const removeButton = builder.instance.componentEdit.querySelector('[ref="removeButton"]'); - Harness.clickElement(panel, removeButton); + }); + + function destroyWizardBuilder() { + if (formBuilder && formBuilder.instance) { + formBuilder.instance.destroy(); + document.body.removeChild(formBuilderElement); + } + } + + function createWizardBuilder(form = { display: 'wizard', components: [] }) { + destroyWizardBuilder(); + formBuilderElement = document.createElement('div'); + document.body.appendChild(formBuilderElement); + formBuilder = new FormBuilder( + formBuilderElement, + { ...form, components: [...form.components] }, + {}, + ); + return formBuilder; + } + + it('Test page remove with cancellation', function (done) { + const builder = createWizardBuilder(simpleWizard); + + setTimeout(() => { + const panel = builder.instance.webform.components[0]; + const removeComponentRef = panel.refs.removeComponent; + window.confirm = () => { + return false; + }; + Harness.clickElement(panel, removeComponentRef); + setTimeout(() => { + assert.equal( + builder.instance._form.components.length, + 5, + 'Should not remove the page on cancel', + ); + assert.equal( + builder.instance.webform.components[0].key, + 'page1', + 'Should stay on page 1 since we' + ' canceled the deletion', + ); + done(); + }, 300); + }, 500); + }); + + it('Test page remove with confirmation', function (done) { + const builder = createWizardBuilder(simpleWizard); + + setTimeout(() => { + const panel = builder.instance.webform.components[0]; + const removeComponentRef = panel.refs.removeComponent; + window.confirm = () => { + return true; + }; + Harness.clickElement(panel, removeComponentRef); + setTimeout(() => { + assert.equal( + builder.instance._form.components.length, + 4, + 'Should remove the page on confirm', + ); + assert.equal( + builder.instance.webform.components[0].key, + 'page2', + 'Should switch to the next page when' + + ' deletion is confirmed', + ); + done(); + }, 300); + }, 500); + }); + + it('Test page remove with confirmation when remove from component settings window', function (done) { + const builder = createWizardBuilder(simpleWizard); + + setTimeout(() => { + const panel = builder.instance.webform.components[0]; + const editComponentRef = panel.refs.editComponent; + window.confirm = () => { + return true; + }; + + Harness.clickElement(panel, editComponentRef); + setTimeout(() => { + assert( + builder.instance.editForm, + 'Should create the settings form on component edit', + ); + const removeButton = + builder.instance.componentEdit.querySelector( + '[ref="removeButton"]', + ); + Harness.clickElement(panel, removeButton); + setTimeout(() => { + assert.equal( + builder.instance._form.components.length, + 4, + 'Should remove the page on confirm', + ); + assert.equal( + builder.instance.webform.components[0].key, + 'page2', + 'Should switch to the next page when' + + ' deletion is confirmed', + ); + done(); + }, 300); + }, 300); + }, 500); + }); + + it('Should execute form controller', function (done) { + const form = fastCloneDeep(formWithFormController); + form.display = 'wizard'; + const builder = createWizardBuilder(form).instance; + + setTimeout(() => { + const textF = builder.webform.getComponent('textField'); + assert.equal(textF.getValue(), 'Hello World'); + assert.equal(textF.disabled, true); + assert.equal(builder.webform.components[0].disabled, true); + + done(); + }, 500); + }); + + it('Test pages reorder', function (done) { + const builder = createWizardBuilder(simpleWizard); + setTimeout(() => { - assert.equal(builder.instance._form.components.length, 4, 'Should remove the page on confirm'); - assert.equal(builder.instance.webform.components[0].key, 'page2', 'Should switch to the next page when' + - ' deletion is confirmed'); - done(); - }, 300); - }, 300); - }, 500); - }); - - it('Should execute form controller', function(done) { - const form = fastCloneDeep(formWithFormController); - form.display = 'wizard'; - const builder = createWizardBuilder(form).instance; - - setTimeout(() => { - const textF = builder.webform.getComponent('textField'); - assert.equal(textF.getValue(), 'Hello World'); - assert.equal(textF.disabled, true); - assert.equal(builder.webform.components[0].disabled, true); - - done(); - }, 500); - }); - - it('Test pages reorder', function(done) { - const builder = createWizardBuilder(simpleWizard); - - setTimeout(() => { - const drake = builder.instance.navigationDragula; - const addPageBtn = builder.instance.element.querySelector('.wizard-add-page'); - assert.equal(drake.canMove(addPageBtn), false, 'Should not be able to move Add Page button'); - const pagesElements = builder.instance.element.querySelectorAll('.wizard-pages li'); - assert.equal(pagesElements.length, 6, 'Should contain all buttons for all the pages + Add Page button'); - const firstPage = pagesElements[0]; - assert.equal(drake.canMove(firstPage), true, 'Should be able to move page button'); - done(); - }, 500); - }); + const drake = builder.instance.navigationDragula; + const addPageBtn = + builder.instance.element.querySelector('.wizard-add-page'); + assert.equal( + drake.canMove(addPageBtn), + false, + 'Should not be able to move Add Page button', + ); + const pagesElements = + builder.instance.element.querySelectorAll('.wizard-pages li'); + assert.equal( + pagesElements.length, + 6, + 'Should contain all buttons for all the pages + Add Page button', + ); + const firstPage = pagesElements[0]; + assert.equal( + drake.canMove(firstPage), + true, + 'Should be able to move page button', + ); + done(); + }, 500); + }); }); diff --git a/src/addons/PasswordStrength/PasswordStrengthAddon.form.js b/src/addons/PasswordStrength/PasswordStrengthAddon.form.js index 0f9e9b135b..3bb5839056 100644 --- a/src/addons/PasswordStrength/PasswordStrengthAddon.form.js +++ b/src/addons/PasswordStrength/PasswordStrengthAddon.form.js @@ -1,327 +1,345 @@ import EditFormUtils from '../../components/_classes/component/editForm/utils'; export default [ - { - label: 'Strength Levels', - reorder: false, - addAnotherPosition: 'bottom', - layoutFixed: false, - enableRowGroups: false, - initEmpty: false, - tableView: false, - defaultValue: [{}], - key: 'levels', - type: 'editgrid', - input: true, - components: [ - { - label: 'Name', - tableView: true, - validate: { - required: true - }, - key: 'name', - type: 'textfield', - input: true - }, - { - label: 'Max Entropy', - description: "Specifies the top boundary of the password's entropy(strength) which belongs to this level.\nCommon entropy values are:\n
    \n
  • < 28 bits = Very Weak;
  • \n
  • 28 - 35 bits = Weak; should keep out most people;
  • \n
  • 36 - 59 bits = Reasonable; fairly secure passwords for network and company passwords;
  • \n
  • 60 - 127 bits = Strong; can be good for guarding financial information;
  • \n
  • > 128 bits = Very Strong; often overkill;
  • \n
\n", - mask: false, - spellcheck: true, + { + label: 'Strength Levels', + reorder: false, + addAnotherPosition: 'bottom', + layoutFixed: false, + enableRowGroups: false, + initEmpty: false, tableView: false, - delimiter: false, - requireDecimal: false, - inputFormat: 'plain', - validate: { - required: true, - min: 1, - max: 128 - }, - key: 'maxEntropy', - type: 'number', - input: true - }, - { - label: 'Style', - tooltip: 'Specifies the background color style using bootstrap classes', - tableView: true, - data: { - values: [ + defaultValue: [{}], + key: 'levels', + type: 'editgrid', + input: true, + components: [ { - label: 'Danger', - value: 'danger' + label: 'Name', + tableView: true, + validate: { + required: true, + }, + key: 'name', + type: 'textfield', + input: true, }, { - label: 'Warning', - value: 'warning' + label: 'Max Entropy', + description: + "Specifies the top boundary of the password's entropy(strength) which belongs to this level.\nCommon entropy values are:\n
    \n
  • < 28 bits = Very Weak;
  • \n
  • 28 - 35 bits = Weak; should keep out most people;
  • \n
  • 36 - 59 bits = Reasonable; fairly secure passwords for network and company passwords;
  • \n
  • 60 - 127 bits = Strong; can be good for guarding financial information;
  • \n
  • > 128 bits = Very Strong; often overkill;
  • \n
\n", + mask: false, + spellcheck: true, + tableView: false, + delimiter: false, + requireDecimal: false, + inputFormat: 'plain', + validate: { + required: true, + min: 1, + max: 128, + }, + key: 'maxEntropy', + type: 'number', + input: true, }, { - label: 'Info', - value: 'info' + label: 'Style', + tooltip: + 'Specifies the background color style using bootstrap classes', + tableView: true, + data: { + values: [ + { + label: 'Danger', + value: 'danger', + }, + { + label: 'Warning', + value: 'warning', + }, + { + label: 'Info', + value: 'info', + }, + { + label: 'Success', + value: 'success', + }, + ], + }, + selectThreshold: 0.3, + validate: { + onlyAvailableItems: false, + }, + key: 'style', + type: 'select', + indexeddb: { filter: {} }, + input: true, }, { - label: 'Success', - value: 'success' - } - ] + label: 'Color', + placeholder: '#0079c0', + tooltip: 'Specifies a color of the indicator element', + tableView: true, + key: 'color', + type: 'textfield', + input: true, + }, + ], + }, + { + label: 'Update On', + tableView: true, + data: { + values: [ + { + label: 'Strength Level Change', + value: 'levelChange', + }, + { + label: 'Entropy Change', + value: 'entropyChange', + }, + ], }, selectThreshold: 0.3, validate: { - onlyAvailableItems: false + onlyAvailableItems: false, }, - key: 'style', + key: 'updateOn', type: 'select', - indexeddb: { filter: {} }, - input: true - }, - { - label: 'Color', - placeholder: '#0079c0', - tooltip: 'Specifies a color of the indicator element', - tableView: true, - key: 'color', - type: 'textfield', - input: true - } - ] - }, - { - label: 'Update On', - tableView: true, - data: { - values: [ - { - label: 'Strength Level Change', - value: 'levelChange' + indexeddb: { + filter: {}, }, - { - label: 'Entropy Change', - value: 'entropyChange' - } - ] - }, - selectThreshold: 0.3, - validate: { - onlyAvailableItems: false - }, - key: 'updateOn', - type: 'select', - indexeddb: { - filter: {} + input: true, }, - input: true - }, - { - label: 'Rules', - reorder: false, - addAnotherPosition: 'bottom', - layoutFixed: false, - enableRowGroups: false, - initEmpty: false, - tableView: false, - defaultValue: [ - {} - ], - key: 'rulesSettings', - type: 'datagrid', - input: true, - components: [ - { - label: 'Name', - tableView: true, - data: { - values: [ + { + label: 'Rules', + reorder: false, + addAnotherPosition: 'bottom', + layoutFixed: false, + enableRowGroups: false, + initEmpty: false, + tableView: false, + defaultValue: [{}], + key: 'rulesSettings', + type: 'datagrid', + input: true, + components: [ { - label: 'Length', - value: 'length' + label: 'Name', + tableView: true, + data: { + values: [ + { + label: 'Length', + value: 'length', + }, + { + label: 'Lower Case', + value: 'lowerCase', + }, + { + label: 'Upper Case', + value: 'upperCase', + }, + { + label: 'Numeric', + value: 'numeric', + }, + { + label: 'Symbols', + value: 'symbols', + }, + ], + }, + selectThreshold: 0.3, + validate: { + required: true, + onlyAvailableItems: false, + }, + key: 'name', + type: 'select', + indexeddb: { + filter: {}, + }, + input: true, }, { - label: 'Lower Case', - value: 'lowerCase' + label: 'Error Message', + tableView: true, + key: 'errorMessage', + type: 'textfield', + input: true, }, { - label: 'Upper Case', - value: 'upperCase' + label: 'Required', + tableView: false, + key: 'required', + type: 'checkbox', + input: true, + defaultValue: false, }, + ], + }, + { + label: 'Custom Rules', + tableView: false, + rowDrafts: false, + key: 'customRules', + type: 'editgrid', + input: true, + components: [ { - label: 'Numeric', - value: 'numeric' + label: 'Name', + tableView: true, + validate: { + required: true, + }, + key: 'name', + type: 'textfield', + input: true, }, + EditFormUtils.javaScriptValue( + 'Check', + 'check', + '', + 1100, + '

Example:

valid = !value.includes(data.email) ? true : "Password should not be variation of the email";

', + '', + '', + true, + ), { - label: 'Symbols', - value: 'symbols' - } - ] - }, - selectThreshold: 0.3, - validate: { - required: true, - onlyAvailableItems: false - }, - key: 'name', - type: 'select', - indexeddb: { - filter: {} - }, - input: true - }, - { - label: 'Error Message', - tableView: true, - key: 'errorMessage', - type: 'textfield', - input: true - }, - { + label: 'Increase Characters Pool Size', + description: + 'Set this to amount of characters that may be used in the password if there is a specific group of characters is used.\nE.g., if your validation checks if there is any numeric symbol in the password, then you should set it to 10 (there are 10 possible numbers).\n', + mask: false, + spellcheck: true, + tableView: false, + delimiter: false, + requireDecimal: false, + inputFormat: 'plain', + key: 'increaseCharactersPoolSize', + type: 'number', + input: true, + }, + { + label: 'Required', + tooltip: 'Check if this check is required to proceed', + tableView: false, + key: 'required', + type: 'checkbox', + input: true, + defaultValue: false, + }, + ], + }, + EditFormUtils.javaScriptValue( + 'Is Valid', + 'isValid', + '', + 1100, + '

Example:

valid = entropy > maxEntropy / 2 ;

', + '', + 'entropyCurrent entropy bits of the password.' + + 'levelCurrent strength level of the password.', + true, + ), + { label: 'Required', + description: + "Check this if you don't want to allow submitting password which does not correspond to the minimal strength requirements.", tableView: false, key: 'required', type: 'checkbox', input: true, - defaultValue: false - } - ] - }, - { - label: 'Custom Rules', - tableView: false, - rowDrafts: false, - key: 'customRules', - type: 'editgrid', - input: true, - components: [ - { - label: 'Name', + defaultValue: false, + }, + { + label: 'Black List', + tooltip: + 'Add words to search in the password. If there are some words from that list were found, the entropy of the password will be recalculated.\n', tableView: true, - validate: { - required: true - }, - key: 'name', + multiple: true, + key: 'blackList', type: 'textfield', - input: true - }, - EditFormUtils.javaScriptValue('Check', 'check', '', 1100, - '

Example:

valid = !value.includes(data.email) ? true : "Password should not be variation of the email";

', + input: true, + }, + EditFormUtils.javaScriptValue( + 'Custom Blacklisted Words', + 'customBlackListedWords', '', + 1100, + '

Example:

values = [ data.name, data.dataOfBirth, data.favoriteColor ];

', '', - true - ), - { - label: 'Increase Characters Pool Size', - description: 'Set this to amount of characters that may be used in the password if there is a specific group of characters is used.\nE.g., if your validation checks if there is any numeric symbol in the password, then you should set it to 10 (there are 10 possible numbers).\n', - mask: false, - spellcheck: true, - tableView: false, - delimiter: false, - requireDecimal: false, - inputFormat: 'plain', - key: 'increaseCharactersPoolSize', - type: 'number', - input: true - }, - { - label: 'Required', - tooltip: 'Check if this check is required to proceed', + '', + true, + ), + { + label: 'Disable Blacklisted Words', + tooltip: + 'Check if you want to disable submitting passwords containing words form the clack list', tableView: false, - key: 'required', + key: 'disableBlacklistedWords', type: 'checkbox', input: true, - defaultValue: false - } - ] - }, - EditFormUtils.javaScriptValue('Is Valid', 'isValid', '', 1100, - '

Example:

valid = entropy > maxEntropy / 2 ;

', - '', - 'entropyCurrent entropy bits of the password.' + - 'levelCurrent strength level of the password.', - true - ), - { - label: 'Required', - description: "Check this if you don't want to allow submitting password which does not correspond to the minimal strength requirements.", - tableView: false, - key: 'required', - type: 'checkbox', - input: true, - defaultValue: false - }, - { - label: 'Black List', - tooltip: 'Add words to search in the password. If there are some words from that list were found, the entropy of the password will be recalculated.\n', - tableView: true, - multiple: true, - key: 'blackList', - type: 'textfield', - input: true - }, - EditFormUtils.javaScriptValue('Custom Blacklisted Words', 'customBlackListedWords', '', 1100, - '

Example:

values = [ data.name, data.dataOfBirth, data.favoriteColor ];

', - '', - '', - true - ), - { - label: 'Disable Blacklisted Words', - tooltip: 'Check if you want to disable submitting passwords containing words form the clack list', - tableView: false, - key: 'disableBlacklistedWords', - type: 'checkbox', - input: true, - defaultValue: false - }, - { - label: 'Location', - hideLabel: false, - tableView: false, - key: 'location', - type: 'container', - input: true, - components: [ - { - label: 'Insert', - tooltip: 'Specifies where the indicator will be inserted: before or aftre an element', - tableView: true, - data: { - values: [ + defaultValue: false, + }, + { + label: 'Location', + hideLabel: false, + tableView: false, + key: 'location', + type: 'container', + input: true, + components: [ { - label: 'Before', - value: 'before' + label: 'Insert', + tooltip: + 'Specifies where the indicator will be inserted: before or aftre an element', + tableView: true, + data: { + values: [ + { + label: 'Before', + value: 'before', + }, + { + label: 'After', + value: 'after', + }, + ], + }, + selectThreshold: 0.3, + validate: { + onlyAvailableItems: false, + }, + key: 'insert', + type: 'select', + indexeddb: { filter: {} }, + input: true, }, { - label: 'After', - value: 'after' - } - ] - }, - selectThreshold: 0.3, - validate: { - onlyAvailableItems: false - }, - key: 'insert', - type: 'select', - indexeddb: { filter: {} }, - input: true - }, - { - label: 'Selector', - placeholder: "[ref='element']", - description: 'Specifies the selector of the element which will be used as a reference to insert the indicator template', + label: 'Selector', + placeholder: "[ref='element']", + description: + 'Specifies the selector of the element which will be used as a reference to insert the indicator template', + tableView: true, + key: 'selector', + type: 'textfield', + input: true, + }, + ], + }, + { + label: 'Template', + editor: 'ace', tableView: true, - key: 'selector', - type: 'textfield', - input: true - } - ] - }, - { - label: 'Template', - editor: 'ace', - tableView: true, - key: 'template', - type: 'textarea', - input: true, - as: 'html' - }, + key: 'template', + type: 'textarea', + input: true, + as: 'html', + }, ]; diff --git a/src/addons/PasswordStrength/PasswordStrengthAddon.js b/src/addons/PasswordStrength/PasswordStrengthAddon.js index e1ea5578c4..a3ec03cefd 100644 --- a/src/addons/PasswordStrength/PasswordStrengthAddon.js +++ b/src/addons/PasswordStrength/PasswordStrengthAddon.js @@ -3,30 +3,50 @@ import FormioAddon from '../FormioAddon'; import PasswordStrengthEditForm from './PasswordStrengthAddon.form'; export default class PasswordStrengthAddon extends FormioAddon { - static get info() { - return { - supportedComponents: ['password'], - name: 'passwordStrength', - components: PasswordStrengthEditForm, - label: 'Password Strength', - defaultSettings: { - rulesSettings: [ - { name: 'length', required: false, message: 'Value should be longer' }, - { name: 'upperCase', required: false, message: 'Value should have uppercase letters' }, - { name: 'numeric', required: false, message: 'Value should have numeric symbols' }, - { name: 'lowerCase', required: false, message: 'Value should be have lowercase letters' }, - { name: 'symbols', required: false, message: 'Value should have symbols' } - ], - updateOn: 'levelChange', - required: true, - levels: [ - { name: 'Low', maxEntropy: 28, style: 'danger' }, - { name: 'Medium', maxEntropy: 45, style: 'warning' }, - { name: 'High', maxEntropy: 59, style: 'info' }, - { name: 'Very High', maxEntropy: 85, style: 'success' }, - ], - blackList: [], - template: ` + static get info() { + return { + supportedComponents: ['password'], + name: 'passwordStrength', + components: PasswordStrengthEditForm, + label: 'Password Strength', + defaultSettings: { + rulesSettings: [ + { + name: 'length', + required: false, + message: 'Value should be longer', + }, + { + name: 'upperCase', + required: false, + message: 'Value should have uppercase letters', + }, + { + name: 'numeric', + required: false, + message: 'Value should have numeric symbols', + }, + { + name: 'lowerCase', + required: false, + message: 'Value should be have lowercase letters', + }, + { + name: 'symbols', + required: false, + message: 'Value should have symbols', + }, + ], + updateOn: 'levelChange', + required: true, + levels: [ + { name: 'Low', maxEntropy: 28, style: 'danger' }, + { name: 'Medium', maxEntropy: 45, style: 'warning' }, + { name: 'High', maxEntropy: 59, style: 'info' }, + { name: 'Very High', maxEntropy: 85, style: 'success' }, + ], + blackList: [], + template: `
{% if (!ctx.readOnly && !ctx.pristine) { %}
`, - location: { - insert: 'after', - selector: '[ref="element"]' - } - } - }; - } - - get defaultSettings() { - return PasswordStrengthAddon.info.defaultSettings; - } - - get rules() { - return { - length: { - check: (value, options) => { - const minLength = options.minLength || this.component.component.validate.minLength || 6; - if (value.length < minLength) { - return `Value must be longer than ${minLength} characters`; - } - return true; - } - }, - upperCase: { - check: (value) => { - if (/[A-Z]/g.test(value)) { - return true; - } - return 'Value must contain uppercased alphabetical characters'; - }, - increaseCharactersPoolSize: 26 - }, - numeric: { - check: (value) => { - if (/[0-9]/g.test(value)) { - return true; - } - return 'Value must contain numeric characters'; - }, - increaseCharactersPoolSize: 10, - }, - lowerCase: { - check: (value) => { - if (/[a-z]/g.test(value)) { - return true; - } - return 'Value must contain lowercased alphabetical characters'; - }, - increaseCharactersPoolSize: 26, - }, - symbols: { - check: (value) => { - if (/[ `!@#$%^&*()_+\-=[\]{};':"\\|,.<>/?~]/.test(value)) { - return true; - } - return 'Value must contain symbols'; - }, - increaseCharactersPoolSize: 32, - }, - }; - } - - get charactersPoolLength() { - return this._charactersPoolLength; - } - - set charactersPoolLength(value) { - this._charactersPoolLength = value; - } - - get level() { - return this._level || this.getLevel(); - } - - set level(level) { - this._level = level; - } - - get entropy() { - return this._entropy; - } - - get dictionarySize() { - return this.settings.dictionarySize || 171476; - } - - set entropy(value) { - const oldLevel = this.getLevel(); - const updateOnEntropyChange = this.settings.updateOn === 'entropyChange' && this._entropy !== value; - this._entropy = value; - this.level = this.getLevel(); - const updateOnLevelChange = this.settings.updateOn === 'levelChange' && oldLevel.name !== this.level.name; - if (updateOnLevelChange || updateOnEntropyChange) { - this.updateView(); + location: { + insert: 'after', + selector: '[ref="element"]', + }, + }, + }; } - } - get template() { - return this.settings.template; - } + get defaultSettings() { + return PasswordStrengthAddon.info.defaultSettings; + } - get tooltip() { - return this.level?.tooltip || `${this.level?.name} strongness`; - } + get rules() { + return { + length: { + check: (value, options) => { + const minLength = + options.minLength || + this.component.component.validate.minLength || + 6; + if (value.length < minLength) { + return `Value must be longer than ${minLength} characters`; + } + return true; + }, + }, + upperCase: { + check: (value) => { + if (/[A-Z]/g.test(value)) { + return true; + } + return 'Value must contain uppercased alphabetical characters'; + }, + increaseCharactersPoolSize: 26, + }, + numeric: { + check: (value) => { + if (/[0-9]/g.test(value)) { + return true; + } + return 'Value must contain numeric characters'; + }, + increaseCharactersPoolSize: 10, + }, + lowerCase: { + check: (value) => { + if (/[a-z]/g.test(value)) { + return true; + } + return 'Value must contain lowercased alphabetical characters'; + }, + increaseCharactersPoolSize: 26, + }, + symbols: { + check: (value) => { + if (/[ `!@#$%^&*()_+\-=[\]{};':"\\|,.<>/?~]/.test(value)) { + return true; + } + return 'Value must contain symbols'; + }, + increaseCharactersPoolSize: 32, + }, + }; + } - get rulesSettings() { - return this.settings.rulesSettings || []; - } + get charactersPoolLength() { + return this._charactersPoolLength; + } - get customRules() { - return this.settings.customRules || []; - } + set charactersPoolLength(value) { + this._charactersPoolLength = value; + } - log2(value) { - if (typeof Math.log2 === 'function') { - return Math.log2(value); + get level() { + return this._level || this.getLevel(); } - return Math.log(value) * Math.LOG2E; - } - - calculatePasswordEntropy(passwordLength, charactersPoolSize) { - return !passwordLength || !charactersPoolSize ? 0 : this.log2(Math.pow(charactersPoolSize, passwordLength)); - } - - calculatePasswordEntropyWords(wordsCount) { - return !this.dictionarySize ? 0 : this.log2(this.dictionarySize) * wordsCount; - } - - render() { - const view = this.component.interpolate(this.template, { - entropy: this.entropy, - maxEntropy: this.maxEntropy, - level: this.level, - levelName: this.level.name.replace(' ', '-').toLowerCase(), - levels: this.levels, - readOnly: this.component.options.readOnly, - pristine: this.component.pristine, - t: this.t.bind(this), - tooltip: this.tooltip, - }); - - return this.component.sanitize(view); - } - - checkBlackList(value) { - const blackList = [...this.settings.blackList]; - let customBlacklistedWords = this.settings.customBlacklistedWords; - - if (customBlacklistedWords && typeof customBlacklistedWords === 'string') { - customBlacklistedWords = this.evaluate(customBlacklistedWords, this.component.evalContext({ value }), 'values'); - if (customBlacklistedWords && customBlacklistedWords.length) { - blackList.push(...customBlacklistedWords); - } + + set level(level) { + this._level = level; } - let restValue = value; - const blacklistedWords = []; + get entropy() { + return this._entropy; + } - for (let i = 0; i < blackList.length; i++) { - const word = blackList[i]; - const regExp = new RegExp(`${word}`, 'gi'); + get dictionarySize() { + return this.settings.dictionarySize || 171476; + } - if (regExp.test(value)) { - blacklistedWords.push(word); - restValue = restValue.replace(regExp, ''); - } + set entropy(value) { + const oldLevel = this.getLevel(); + const updateOnEntropyChange = + this.settings.updateOn === 'entropyChange' && + this._entropy !== value; + this._entropy = value; + this.level = this.getLevel(); + const updateOnLevelChange = + this.settings.updateOn === 'levelChange' && + oldLevel.name !== this.level.name; + if (updateOnLevelChange || updateOnEntropyChange) { + this.updateView(); + } + } - // If less the 3 symboles left, just stop iterating - if (restValue.length < 3) { - break; - } + get template() { + return this.settings.template; } - if (blacklistedWords.length) { - // If there are some random characters except of blacklisted words in the password, - // calculate the entropy for them - const { charactersPoolSize } = restValue.length ? this.performChecks(restValue) : 0; - const entropyOfNonblacklistedValue = this.calculatePasswordEntropy(restValue.length, charactersPoolSize); - // Calculate the entropy if the biggest part of the password could be picked up from dictionary words - const dictionaryCheckEntropy = this.calculatePasswordEntropyWords(blacklistedWords.length); - const entropy = dictionaryCheckEntropy + entropyOfNonblacklistedValue; - return { entropy, blacklistedWords }; + get tooltip() { + return this.level?.tooltip || `${this.level?.name} strongness`; } - return true; - } - - /** - * Determines is a password is secure enough to submit - * @return {boolean} - */ - isValid() { - const isValidCheck = this.settings.isValid; - if (isValidCheck && typeof isValidCheck === 'string') { - const valid = this.evaluate(isValidCheck, this.component.evalContext({ - entropy: this.entropy, - level: this.level - }), 'valid'); - return valid; + get rulesSettings() { + return this.settings.rulesSettings || []; } - return this.entropy >= Math.round(this.maxEntropy / 2); - } - - /** - * Handles the result of check and constructs a new error object or returns an amount of points to add to the current entropy - * @param {boolean|number} valid - Determines if the validation was failed or an amount of points if it was passed - * @param {*} validation - Validation configuration - * @param {string} value - Value which was validated - * @param {string} message - Message which should be shown if validation was not passed - */ - handleRuleCheckResult(valid, validation, message, errors) { - if (valid !== true) { - errors.push({ - validation: validation.name, - message, - level: validation.required ? 'error' : 'warning' - }); + get customRules() { + return this.settings.customRules || []; } - else if (validation.increaseCharactersPoolSize) { - return validation.increaseCharactersPoolSize; + + log2(value) { + if (typeof Math.log2 === 'function') { + return Math.log2(value); + } + return Math.log(value) * Math.LOG2E; } - return 0; - } - - performChecks(value) { - const errors = []; - let charactersPoolSize = 0; - - this.rulesSettings.forEach((settings) => { - if (this.rules[settings.name]) { - const rule = _.merge({}, this.rules[settings.name], settings); - const valid = rule.check(value, settings.options || {}); - const message = settings.message || valid; - charactersPoolSize += this.handleRuleCheckResult(valid, rule, message, errors); - } - }); - - this.customRules.forEach((rule) => { - if (rule.check && typeof rule.check === 'string') { - const valid = this.evaluate(rule.check, this.component.evalContext({ value }), 'valid'); - const message = typeof valid === 'string' ? valid : `Password does not meet ${rule.name} validation`; - charactersPoolSize += this.handleRuleCheckResult(valid, rule, message, errors); - } - }); - - return { - charactersPoolSize, - errors - }; - } - - /** - * Performs checks to validate password security - * @param {string} value - Suggested password - */ - checkValidity(value) { - const passwordLength = value.length; - - const { charactersPoolSize, errors } = this.performChecks(value); - this.errors = errors; - - const entropy = this.calculatePasswordEntropy(passwordLength, charactersPoolSize); - const blackListCheck = this.settings.blackList?.length || this.settings.customBlacklistedWords ? - this.checkBlackList(value) - : null; - - // If there were found some words from the black list - if (blackListCheck && blackListCheck !== true) { - this.handleBlackListCheckResult(blackListCheck); - // Select the mininal entropy based on the dictionary check or symbolic check - this.entropy = Math.min(entropy, blackListCheck.entropy); + calculatePasswordEntropy(passwordLength, charactersPoolSize) { + return !passwordLength || !charactersPoolSize + ? 0 + : this.log2(Math.pow(charactersPoolSize, passwordLength)); } - else { - this.entropy = entropy; + + calculatePasswordEntropyWords(wordsCount) { + return !this.dictionarySize + ? 0 + : this.log2(this.dictionarySize) * wordsCount; } - const isValid = this.isValid(); - if (!isValid) { - this.errors.push({ - message: 'Password is not strong enough', - level: this.settings.required ? 'error' : 'warning' - }); + render() { + const view = this.component.interpolate(this.template, { + entropy: this.entropy, + maxEntropy: this.maxEntropy, + level: this.level, + levelName: this.level.name.replace(' ', '-').toLowerCase(), + levels: this.levels, + readOnly: this.component.options.readOnly, + pristine: this.component.pristine, + t: this.t.bind(this), + tooltip: this.tooltip, + }); + + return this.component.sanitize(view); } - return !this.errors.length; - } - - handleBlackListCheckResult(result) { - const blacklistedWords = result.blacklistedWords; - const isRequired = this.settings.disableBlacklistedWords; - const message = `Password ${isRequired ? 'must' : 'should'} not include common words: ${blacklistedWords.join(', ')}`; - const validation = { - name: 'blacklist', - required: isRequired, - }; - - this.handleRuleCheckResult(false, validation, message, this.errors); - } - - constructor(settings, componentInstance) { - super(settings, componentInstance); - this._entropy = 0; // Set initial value of entropy - this.levels = [...(this.settings.levels || this.defaultSettings.levels)]; - this.levels.sort((a, b) => a.maxEntropy - b.maxEntropy); // Sort levels from the lowest one to the highest - this.level = this.levels[0]; // Set currnt level to the lowest one - this.maxEntropy = this.levels[this.levels.length - 1].maxEntropy; // Set maximal amount of security points based on the highest level - } - - attach(element) { - super.attach(element); - const container = this.component.ce('div', { ref: 'passwordStrengthIndicator' }); - - const inserted = this.insertContainer(element, container); - - if (!inserted) { - this.component.append(container); + checkBlackList(value) { + const blackList = [...this.settings.blackList]; + let customBlacklistedWords = this.settings.customBlacklistedWords; + + if ( + customBlacklistedWords && + typeof customBlacklistedWords === 'string' + ) { + customBlacklistedWords = this.evaluate( + customBlacklistedWords, + this.component.evalContext({ value }), + 'values', + ); + if (customBlacklistedWords && customBlacklistedWords.length) { + blackList.push(...customBlacklistedWords); + } + } + + let restValue = value; + const blacklistedWords = []; + + for (let i = 0; i < blackList.length; i++) { + const word = blackList[i]; + const regExp = new RegExp(`${word}`, 'gi'); + + if (regExp.test(value)) { + blacklistedWords.push(word); + restValue = restValue.replace(regExp, ''); + } + + // If less the 3 symboles left, just stop iterating + if (restValue.length < 3) { + break; + } + } + + if (blacklistedWords.length) { + // If there are some random characters except of blacklisted words in the password, + // calculate the entropy for them + const { charactersPoolSize } = restValue.length + ? this.performChecks(restValue) + : 0; + const entropyOfNonblacklistedValue = this.calculatePasswordEntropy( + restValue.length, + charactersPoolSize, + ); + // Calculate the entropy if the biggest part of the password could be picked up from dictionary words + const dictionaryCheckEntropy = this.calculatePasswordEntropyWords( + blacklistedWords.length, + ); + const entropy = + dictionaryCheckEntropy + entropyOfNonblacklistedValue; + return { entropy, blacklistedWords }; + } + + return true; } - this._element = container; - this.component.on('redraw', () => this.updateView()); - this.component.on('componentError', () => this.updateView()); - this.updateView(); - } + /** + * Determines is a password is secure enough to submit + * @return {boolean} + */ + isValid() { + const isValidCheck = this.settings.isValid; + if (isValidCheck && typeof isValidCheck === 'string') { + const valid = this.evaluate( + isValidCheck, + this.component.evalContext({ + entropy: this.entropy, + level: this.level, + }), + 'valid', + ); + return valid; + } - insertContainer(element, container) { - if (!element || !container) { - return false; + return this.entropy >= Math.round(this.maxEntropy / 2); } - const insert = this.settings.location?.insert; - const selector = this.settings.location?.selector; - let reference; + /** + * Handles the result of check and constructs a new error object or returns an amount of points to add to the current entropy + * @param {boolean|number} valid - Determines if the validation was failed or an amount of points if it was passed + * @param {*} validation - Validation configuration + * @param {string} value - Value which was validated + * @param {string} message - Message which should be shown if validation was not passed + */ + handleRuleCheckResult(valid, validation, message, errors) { + if (valid !== true) { + errors.push({ + validation: validation.name, + message, + level: validation.required ? 'error' : 'warning', + }); + } else if (validation.increaseCharactersPoolSize) { + return validation.increaseCharactersPoolSize; + } + + return 0; + } - if (selector) { - reference = element.querySelector(selector); + performChecks(value) { + const errors = []; + let charactersPoolSize = 0; + + this.rulesSettings.forEach((settings) => { + if (this.rules[settings.name]) { + const rule = _.merge({}, this.rules[settings.name], settings); + const valid = rule.check(value, settings.options || {}); + const message = settings.message || valid; + charactersPoolSize += this.handleRuleCheckResult( + valid, + rule, + message, + errors, + ); + } + }); + + this.customRules.forEach((rule) => { + if (rule.check && typeof rule.check === 'string') { + const valid = this.evaluate( + rule.check, + this.component.evalContext({ value }), + 'valid', + ); + const message = + typeof valid === 'string' + ? valid + : `Password does not meet ${rule.name} validation`; + charactersPoolSize += this.handleRuleCheckResult( + valid, + rule, + message, + errors, + ); + } + }); + + return { + charactersPoolSize, + errors, + }; } - if (reference) { - const parent = reference.parentNode; - - switch (insert) { - case 'after': - if (parent) { - parent.insertBefore(container, reference.nextSibling || null); - return true; - } - return false; - case 'before': - if (parent) { - parent.insertBefore(container, reference); - return true; - } - return false; - default: - console.warn(`Unknown insert option: ${insert}`); - return false; - } + /** + * Performs checks to validate password security + * @param {string} value - Suggested password + */ + checkValidity(value) { + const passwordLength = value.length; + + const { charactersPoolSize, errors } = this.performChecks(value); + this.errors = errors; + + const entropy = this.calculatePasswordEntropy( + passwordLength, + charactersPoolSize, + ); + const blackListCheck = + this.settings.blackList?.length || + this.settings.customBlacklistedWords + ? this.checkBlackList(value) + : null; + + // If there were found some words from the black list + if (blackListCheck && blackListCheck !== true) { + this.handleBlackListCheckResult(blackListCheck); + // Select the mininal entropy based on the dictionary check or symbolic check + this.entropy = Math.min(entropy, blackListCheck.entropy); + } else { + this.entropy = entropy; + } + + const isValid = this.isValid(); + if (!isValid) { + this.errors.push({ + message: 'Password is not strong enough', + level: this.settings.required ? 'error' : 'warning', + }); + } + + return !this.errors.length; } - else { - console.warn(`No elements found using selector: ${selector}`); - return false; + + handleBlackListCheckResult(result) { + const blacklistedWords = result.blacklistedWords; + const isRequired = this.settings.disableBlacklistedWords; + const message = `Password ${ + isRequired ? 'must' : 'should' + } not include common words: ${blacklistedWords.join(', ')}`; + const validation = { + name: 'blacklist', + required: isRequired, + }; + + this.handleRuleCheckResult(false, validation, message, this.errors); } - } - - destroy() { - super.destroy(); - } - - /** - * Finds the level which one the passed entropy suits - * @param {number} entropy - Points of password's security - */ - getLevel(entropy = this.entropy) { - const lowestLevel = this.levels[0]; - let prevMaxEntropy = lowestLevel.maxEntropy; - - if (entropy <= lowestLevel.maxEntropy) { - return lowestLevel; + + constructor(settings, componentInstance) { + super(settings, componentInstance); + this._entropy = 0; // Set initial value of entropy + this.levels = [ + ...(this.settings.levels || this.defaultSettings.levels), + ]; + this.levels.sort((a, b) => a.maxEntropy - b.maxEntropy); // Sort levels from the lowest one to the highest + this.level = this.levels[0]; // Set currnt level to the lowest one + this.maxEntropy = this.levels[this.levels.length - 1].maxEntropy; // Set maximal amount of security points based on the highest level } - if (entropy >= this.maxEntropy) { - return this.levels[this.levels.length - 1]; + attach(element) { + super.attach(element); + const container = this.component.ce('div', { + ref: 'passwordStrengthIndicator', + }); + + const inserted = this.insertContainer(element, container); + + if (!inserted) { + this.component.append(container); + } + + this._element = container; + this.component.on('redraw', () => this.updateView()); + this.component.on('componentError', () => this.updateView()); + this.updateView(); } - // Iterate through levels and find the one which the passed entropy belongs to - for (let i = 1; i < this.levels.length; i++) { - const level = this.levels[i]; + insertContainer(element, container) { + if (!element || !container) { + return false; + } - if (entropy > prevMaxEntropy && entropy <= level.maxEntropy) { - return level; - } + const insert = this.settings.location?.insert; + const selector = this.settings.location?.selector; + let reference; - prevMaxEntropy = level.maxEntropy; + if (selector) { + reference = element.querySelector(selector); + } + + if (reference) { + const parent = reference.parentNode; + + switch (insert) { + case 'after': + if (parent) { + parent.insertBefore( + container, + reference.nextSibling || null, + ); + return true; + } + return false; + case 'before': + if (parent) { + parent.insertBefore(container, reference); + return true; + } + return false; + default: + console.warn(`Unknown insert option: ${insert}`); + return false; + } + } else { + console.warn(`No elements found using selector: ${selector}`); + return false; + } } - return lowestLevel; - } + destroy() { + super.destroy(); + } + + /** + * Finds the level which one the passed entropy suits + * @param {number} entropy - Points of password's security + */ + getLevel(entropy = this.entropy) { + const lowestLevel = this.levels[0]; + let prevMaxEntropy = lowestLevel.maxEntropy; + + if (entropy <= lowestLevel.maxEntropy) { + return lowestLevel; + } + + if (entropy >= this.maxEntropy) { + return this.levels[this.levels.length - 1]; + } + + // Iterate through levels and find the one which the passed entropy belongs to + for (let i = 1; i < this.levels.length; i++) { + const level = this.levels[i]; - /** - * Update the current view of the password's security indicator - */ - updateView() { - if (!this.element) { - return; + if (entropy > prevMaxEntropy && entropy <= level.maxEntropy) { + return level; + } + + prevMaxEntropy = level.maxEntropy; + } + + return lowestLevel; } - const view = this.render(); - this.element.innerHTML = view; - } + /** + * Update the current view of the password's security indicator + */ + updateView() { + if (!this.element) { + return; + } + + const view = this.render(); + this.element.innerHTML = view; + } } diff --git a/src/components/_classes/component/Component.form.js b/src/components/_classes/component/Component.form.js index 91809511ef..658770e3d4 100644 --- a/src/components/_classes/component/Component.form.js +++ b/src/components/_classes/component/Component.form.js @@ -9,65 +9,70 @@ import ComponentEditValidation from './editForm/Component.edit.validation'; import ComponentEditLayout from './editForm/Component.edit.layout'; import EditFormUtils from './editForm/utils'; -export default function(...extend) { - const components = _.cloneDeep([ - { - type: 'tabs', - key: 'tabs', - components: [ +export default function (...extend) { + const components = _.cloneDeep([ { - label: 'Display', - key: 'display', - weight: 0, - components: ComponentEditDisplay + type: 'tabs', + key: 'tabs', + components: [ + { + label: 'Display', + key: 'display', + weight: 0, + components: ComponentEditDisplay, + }, + { + label: 'Data', + key: 'data', + weight: 10, + components: ComponentEditData, + }, + { + label: 'Validation', + key: 'validation', + weight: 20, + components: ComponentEditValidation, + }, + { + label: 'API', + key: 'api', + weight: 30, + components: ComponentEditAPI, + }, + { + label: 'Conditional', + key: 'conditional', + weight: 40, + components: ComponentEditConditional, + }, + { + label: 'Logic', + key: 'logic', + weight: 50, + components: ComponentEditLogic, + }, + { + label: 'Layout', + key: 'layout', + weight: 60, + components: ComponentEditLayout, + }, + ], }, - { - label: 'Data', - key: 'data', - weight: 10, - components: ComponentEditData - }, - { - label: 'Validation', - key: 'validation', - weight: 20, - components: ComponentEditValidation - }, - { - label: 'API', - key: 'api', - weight: 30, - components: ComponentEditAPI - }, - { - label: 'Conditional', - key: 'conditional', - weight: 40, - components: ComponentEditConditional - }, - { - label: 'Logic', - key: 'logic', - weight: 50, - components: ComponentEditLogic - }, - { - label: 'Layout', - key: 'layout', - weight: 60, - components: ComponentEditLayout - }, - ] - } - ]).concat(extend.map((items) => ({ - type: 'tabs', - key: 'tabs', - components: _.cloneDeep(items), - }))); - return { - components: _.unionWith(components, EditFormUtils.unifyComponents).concat({ - type: 'hidden', - key: 'type' - }) - }; + ]).concat( + extend.map((items) => ({ + type: 'tabs', + key: 'tabs', + components: _.cloneDeep(items), + })), + ); + return { + components: _.unionWith( + components, + EditFormUtils.unifyComponents, + ).concat({ + type: 'hidden', + key: 'type', + }), + }; } diff --git a/src/components/_classes/component/Component.js b/src/components/_classes/component/Component.js index 75c4af9ebf..a514915c33 100644 --- a/src/components/_classes/component/Component.js +++ b/src/components/_classes/component/Component.js @@ -7,7 +7,11 @@ import { Formio } from '../../../Formio'; import * as FormioUtils from '../../../utils/utils'; import Validator from '../../../validator/Validator'; import { - fastCloneDeep, boolValue, getComponentPath, isInsideScopingComponent, currentTimezone + fastCloneDeep, + boolValue, + getComponentPath, + isInsideScopingComponent, + currentTimezone, } from '../../../utils/utils'; import Element from '../../../Element'; import ComponentModal from '../componentModal/ComponentModal'; @@ -24,3698 +28,4203 @@ const isIEBrowser = FormioUtils.getBrowserInfo().ie; which all elements within the FormioForm derive from. */ export default class Component extends Element { - static schema(...sources) { - return _.merge({ - /** - * Determines if this component provides an input. - */ - input: true, - - /** - * The data key for this component (how the data is stored in the database). - */ - key: '', - - /** - * The input placeholder for this component. - */ - placeholder: '', - - /** - * The input prefix - */ - prefix: '', - - /** - * The custom CSS class to provide to this component. - */ - customClass: '', - - /** - * The input suffix. - */ - suffix: '', - - /** - * If this component should allow an array of values to be captured. - */ - multiple: false, - - /** - * The default value of this component. - */ - defaultValue: null, - - /** - * If the data of this component should be protected (no GET api requests can see the data) - */ - protected: false, - - /** - * Validate if the value of this component should be unique within the form. - */ - unique: false, - - /** - * If the value of this component should be persisted within the backend api database. - */ - persistent: true, - - /** - * Determines if the component should be within the form, but not visible. - */ - hidden: false, - - /** - * If the component should be cleared when hidden. - */ - clearOnHide: true, - - /** - * This will refresh this component options when this field changes. - */ - refreshOn: '', - - /** - * This will redraw the component when this field changes. - */ - redrawOn: '', - - /** - * If this component should be included as a column within a submission table. - */ - tableView: false, - - /** - * If this component should be rendering in modal. - */ - modalEdit: false, - - /** - * The input label provided to this component. - */ - label: '', - dataGridLabel: false, - labelPosition: 'top', - description: '', - errorLabel: '', - tooltip: '', - hideLabel: false, - tabindex: '', - disabled: false, - autofocus: false, - dbIndex: false, - customDefaultValue: '', - calculateValue: '', - calculateServer: false, - widget: null, - - /** - * Attributes that will be assigned to the input elements of this component. - */ - attributes: {}, - - /** - * This will perform the validation on either "change" or "blur" of the input element. - */ - validateOn: 'change', - - /** - * The validation criteria for this component. - */ - validate: { + static schema(...sources) { + return _.merge( + { + /** + * Determines if this component provides an input. + */ + input: true, + + /** + * The data key for this component (how the data is stored in the database). + */ + key: '', + + /** + * The input placeholder for this component. + */ + placeholder: '', + + /** + * The input prefix + */ + prefix: '', + + /** + * The custom CSS class to provide to this component. + */ + customClass: '', + + /** + * The input suffix. + */ + suffix: '', + + /** + * If this component should allow an array of values to be captured. + */ + multiple: false, + + /** + * The default value of this component. + */ + defaultValue: null, + + /** + * If the data of this component should be protected (no GET api requests can see the data) + */ + protected: false, + + /** + * Validate if the value of this component should be unique within the form. + */ + unique: false, + + /** + * If the value of this component should be persisted within the backend api database. + */ + persistent: true, + + /** + * Determines if the component should be within the form, but not visible. + */ + hidden: false, + + /** + * If the component should be cleared when hidden. + */ + clearOnHide: true, + + /** + * This will refresh this component options when this field changes. + */ + refreshOn: '', + + /** + * This will redraw the component when this field changes. + */ + redrawOn: '', + + /** + * If this component should be included as a column within a submission table. + */ + tableView: false, + + /** + * If this component should be rendering in modal. + */ + modalEdit: false, + + /** + * The input label provided to this component. + */ + label: '', + dataGridLabel: false, + labelPosition: 'top', + description: '', + errorLabel: '', + tooltip: '', + hideLabel: false, + tabindex: '', + disabled: false, + autofocus: false, + dbIndex: false, + customDefaultValue: '', + calculateValue: '', + calculateServer: false, + widget: null, + + /** + * Attributes that will be assigned to the input elements of this component. + */ + attributes: {}, + + /** + * This will perform the validation on either "change" or "blur" of the input element. + */ + validateOn: 'change', + + /** + * The validation criteria for this component. + */ + validate: { + /** + * If this component is required. + */ + required: false, + + /** + * Custom JavaScript validation. + */ + custom: '', + + /** + * If the custom validation should remain private (only the backend will see it and execute it). + */ + customPrivate: false, + + /** + * If this component should implement a strict date validation if the Calendar widget is implemented. + */ + strictDateValidation: false, + multiple: false, + unique: false, + }, + + /** + * The simple conditional settings for a component. + */ + conditional: { + show: null, + when: null, + eq: '', + }, + overlay: { + style: '', + left: '', + top: '', + width: '', + height: '', + }, + allowCalculateOverride: false, + encrypted: false, + showCharCount: false, + showWordCount: false, + properties: {}, + allowMultipleMasks: false, + addons: [], + }, + ...sources, + ); + } + + /** + * Return the validator as part of the component. + * + * @return {ValidationChecker} + * @constructor + */ + static get Validator() { + return Validator; + } + /** + * Return the simple condition settings as part of the component. + * + * @return {Object} + * + */ + static get conditionOperatorsSettings() { + return { + operators: ['isEqual', 'isNotEqual', 'isEmpty', 'isNotEmpty'], + valueComponent() { + return { + type: 'textfield', + widget: { + type: 'input', + }, + }; + }, + }; + } + /** + * Return the array of possible types of component value absed on its schema. + * + * @param schema + * @return {Array} + * + */ + + static savedValueTypes(schema) { + schema = schema || {}; + + return ( + FormioUtils.getComponentSavedTypes(schema) || [ + FormioUtils.componentValueTypes.any, + ] + ); + } + /** + * Provides a table view for this component. Override if you wish to do something different than using getView + * method of your instance. + * + * @param value + * @param options + */ + /* eslint-disable no-unused-vars */ + static tableView(value, options) {} + /* eslint-enable no-unused-vars */ + + /** + * Initialize a new Component. + * + * @param {Object} component - The component JSON you wish to initialize. + * @param {Object} options - The options for this component. + * @param {Object} data - The global data submission object this component will belong. + */ + /* eslint-disable max-statements */ + constructor(component, options, data) { + super( + Object.assign( + { + renderMode: 'form', + attachMode: 'full', + noDefaults: false, + }, + options || {}, + ), + ); + + // Restore the component id. + if (component && component.id) { + this.id = component.id; + } + + /** + * Determines if this component has a condition assigned to it. + * @type {null} + * @private + */ + this._hasCondition = null; + + /** + * References to dom elements + */ + this.refs = {}; + + // Allow global override for any component JSON. + if ( + component && + this.options.components && + this.options.components[component.type] + ) { + _.merge(component, this.options.components[component.type]); + } + + /** + * Set the validator instance. + */ + this.validator = Validator; + + /** + * The data path to this specific component instance. + * + * @type {string} + */ + this.path = ''; + + /** + * The Form.io component JSON schema. + * @type {*} + */ + this.component = this.mergeSchema(component || {}); + + // Add the id to the component. + this.component.id = this.id; + + this.afterComponentAssign(); + + // Save off the original component to be used in logic. + this.originalComponent = fastCloneDeep(this.component); + + /** + * If the component has been attached + */ + this.attached = false; + + /** + * If the component has been rendered + */ + this.rendered = false; + + /** + * The data object in which this component resides. + * @type {*} + */ + this._data = data || {}; + + /** + * The existing error that this component has. + * @type {string} + */ + this.error = ''; + + /** + * Tool tip text after processing + * @type {string} + */ + this.tooltip = ''; + + /** + * The row path of this component. + * @type {number} + */ + this.row = this.options.row; + + /** + * Determines if this component is disabled, or not. + * + * @type {boolean} + */ + this._disabled = boolValue(this.component.disabled) + ? this.component.disabled + : false; + + /** + * Points to the root component, usually the FormComponent. + * + * @type {Component} + */ + this.root = this.options.root; + this.localRoot = this.options.localRoot; + + /** + * If this input has been input and provided value. + * + * @type {boolean} + */ + this.pristine = true; + + /** + * Points to the parent component. + * + * @type {Component} + */ + this.parent = this.options.parent; + + this.options.name = this.options.name || 'data'; + + /** + * The validators that are assigned to this component. + * @type {[string]} + */ + this.validators = [ + 'required', + 'minLength', + 'maxLength', + 'minWords', + 'maxWords', + 'custom', + 'pattern', + 'json', + 'mask', + ]; + + this._path = ''; + // Nested forms don't have parents so we need to pass their path in. + this._parentPath = this.options.parentPath || ''; + + // Needs for Nextgen Rules Engine + this.resetCaches(); + + /** + * Determines if this component is visible, or not. + */ + this._parentVisible = Object.prototype.hasOwnProperty.call( + this.options, + 'parentVisible', + ) + ? this.options.parentVisible + : true; + this._visible = + this._parentVisible && this.conditionallyVisible(null, data); + this._parentDisabled = false; + + /** + * Used to trigger a new change in this component. + * @type {function} - Call to trigger a change in this component. + */ + let changes = []; + let lastChanged = null; + let triggerArgs = []; + const _triggerChange = _.debounce((...args) => { + if (this.root) { + this.root.changing = false; + } + triggerArgs = []; + if (!args[1] && lastChanged) { + // Set the changed component if one isn't provided. + args[1] = lastChanged; + } + if (_.isEmpty(args[0]) && lastChanged) { + // Set the flags if it is empty and lastChanged exists. + args[0] = lastChanged.flags; + } + lastChanged = null; + args[3] = changes; + const retVal = this.onChange(...args); + changes = []; + return retVal; + }, 100); + this.triggerChange = (...args) => { + if (args[1]) { + // Make sure that during the debounce that we always track lastChanged component, even if they + // don't provide one later. + lastChanged = args[1]; + changes.push(lastChanged); + } + if (this.root) { + this.root.changing = true; + } + if (args.length) { + triggerArgs = args; + } + return _triggerChange(...triggerArgs); + }; + + /** + * Used to trigger a redraw event within this component. + * + * @type {Function} + */ + this.triggerRedraw = _.debounce(this.redraw.bind(this), 100); + + /** + * list of attached tooltips + * @type {Array} + */ + this.tooltips = []; + /** - * If this component is required. + * List of attached addons + * @type {Array} */ - required: false, + this.addons = []; + + // To force this component to be invalid. + this.invalid = false; + + if (this.component) { + this.type = this.component.type; + if (this.allowData && this.key) { + this.options.name += `[${this.key}]`; + // If component is visible or not set to clear on hide, set the default value. + if (this.visible || !this.component.clearOnHide) { + if (!this.hasValue()) { + if (this.shouldAddDefaultValue) { + this.dataValue = this.defaultValue; + } + } else { + // Ensure the dataValue is set. + /* eslint-disable no-self-assign */ + this.dataValue = this.dataValue; + /* eslint-enable no-self-assign */ + } + } + } + + /** + * The element information for creating the input element. + * @type {*} + */ + this.info = this.elementInfo(); + } + + // Allow anyone to hook into the component creation. + this.hook('component'); + + if (!this.options.skipInit) { + this.init(); + } + } + /* eslint-enable max-statements */ + + get data() { + return this._data; + } + + set data(value) { + this._data = value; + } + + mergeSchema(component = {}) { + return _.defaultsDeep(component, this.defaultSchema); + } + + // Allow componets to notify when ready. + get ready() { + return Promise.resolve(this); + } + + get isPDFReadOnlyMode() { + return ( + this.parent && + this.parent.form && + this.parent.form.display === 'pdf' && + this.options.readOnly + ); + } + + get labelInfo() { + const label = {}; + label.hidden = this.labelIsHidden(); + + label.className = ''; + label.labelPosition = this.component.labelPosition; + label.tooltipClass = `${this.iconClass('question-sign')} text-muted`; + + const isPDFReadOnlyMode = this.isPDFReadOnlyMode; + + if ( + this.hasInput && + this.component.validate && + boolValue(this.component.validate.required) && + !isPDFReadOnlyMode + ) { + label.className += ' field-required'; + } + if (label.hidden) { + label.className += ' control-label--hidden'; + } + if (this.info.attr.id) { + label.for = this.info.attr.id; + } + return label; + } + + init() { + this.disabled = this.shouldDisabled; + this._visible = this.conditionallyVisible(null, null); + if (this.component.addons?.length) { + this.component.addons.forEach((addon) => this.createAddon(addon)); + } + } + + afterComponentAssign() { + //implement in extended classes + } + + createAddon(addonConfiguration) { + const name = addonConfiguration.name; + if (!name) { + return; + } + + const settings = addonConfiguration.settings?.data || {}; + const Addon = Addons[name.value]; + + let addon = null; + + if (Addon) { + const supportedComponents = Addon.info.supportedComponents; + const supportsThisComponentType = + !supportedComponents?.length || + supportedComponents.indexOf(this.component.type) !== -1; + if (supportsThisComponentType) { + addon = new Addon(settings, this); + this.addons.push(addon); + } else { + console.warn( + `Addon ${name.label} does not support component of type ${this.component.type}.`, + ); + } + } + + return addon; + } + + teardown() { + if (this.element) { + delete this.element.component; + delete this.element; + } + delete this._currentForm; + delete this.parent; + delete this.root; + delete this.triggerChange; + delete this.triggerRedraw; + if (this.options) { + delete this.options.root; + delete this.options.parent; + delete this.options.i18next; + } + super.teardown(); + } + + destroy(all = false) { + super.destroy(all); + this.detach(); + this.addons.forEach((addon) => addon.destroy()); + if (all) { + this.teardown(); + } + } + + get shouldDisabled() { + return ( + this.options.readOnly || + this.component.disabled || + (Object.prototype.hasOwnProperty.call(this.options, 'disabled') && + this.options.disabled[this.key]) + ); + } + + get isInputComponent() { + return ( + !Object.prototype.hasOwnProperty.call(this.component, 'input') || + this.component.input + ); + } + + get allowData() { + return this.hasInput; + } + + get hasInput() { + return ( + this.isInputComponent || (this.refs.input && this.refs.input.length) + ); + } + + get defaultSchema() { + return Component.schema(); + } + + get key() { + return _.get(this.component, 'key', ''); + } + + set parentVisible(value) { + this._parentVisible = value; + } + + get parentVisible() { + return this._parentVisible; + } + + set parentDisabled(value) { + this._parentDisabled = value; + } + + get parentDisabled() { + return this._parentDisabled; + } + + shouldForceVisibility(component, visibility) { + if (!this.options[visibility]) { + return false; + } + if (!component) { + component = this.component; + } + if (_.isArray(this.options[visibility])) { + return this.options[visibility].includes(component.key); + } + return this.options[visibility][component.key]; + } + + shouldForceHide(component) { + return this.shouldForceVisibility(component, 'hide'); + } + + shouldForceShow(component) { + return this.shouldForceVisibility(component, 'show'); + } + + /** + * + * @param value {boolean} + */ + set visible(value) { + if (this._visible !== value) { + // Skip if this component is set to visible and is supposed to be hidden. + if (value && this.shouldForceHide()) { + return; + } + // Skip if this component is set to hidden and is supposed to be shown. + if (!value && this.shouldForceShow()) { + return; + } + this._visible = value; + this.clearOnHide(); + this.redraw(); + } + } + + /** + * + * @returns {boolean} + */ + get visible() { + // Show only if visibility changes or if we are in builder mode or if hidden fields should be shown. + if ( + this.builderMode || + this.previewMode || + this.options.showHiddenFields + ) { + return true; + } + if (this.shouldForceHide()) { + return false; + } + if (this.shouldForceShow()) { + return true; + } + return this._visible && this._parentVisible; + } + + get currentForm() { + return this._currentForm; + } + + set currentForm(instance) { + this._currentForm = instance; + } + + get fullMode() { + return this.options.attachMode === 'full'; + } + + get builderMode() { + return this.options.attachMode === 'builder'; + } + + get calculatedPath() { + console.error( + 'component.calculatedPath was deprecated, use component.path instead.', + ); + return this.path; + } + + get labelPosition() { + return this.component.labelPosition; + } + + get labelWidth() { + const width = this.component.labelWidth; + return width >= 0 ? width : 30; + } + + get labelMargin() { + const margin = this.component.labelMargin; + return margin >= 0 ? margin : 3; + } + + get isAdvancedLabel() { + return [ + 'left-left', + 'left-right', + 'right-left', + 'right-right', + ].includes(this.labelPosition); + } + + get labelPositions() { + return this.labelPosition.split('-'); + } + + get skipInEmail() { + return false; + } + + rightDirection(direction) { + if (this.options.condensedMode) { + return false; + } + return direction === 'right'; + } + + getLabelInfo(isCondensed = false) { + const isRightPosition = this.rightDirection(this.labelPositions[0]); + const isLeftPosition = this.labelPositions[0] === 'left' || isCondensed; + const isRightAlign = this.rightDirection(this.labelPositions[1]); + + let contentMargin = ''; + if (this.component.hideLabel) { + const margin = isCondensed ? 0 : this.labelWidth + this.labelMargin; + contentMargin = isRightPosition ? `margin-right: ${margin}%` : ''; + contentMargin = isLeftPosition ? `margin-left: ${margin}%` : ''; + } + + const labelStyles = ` + flex: ${this.labelWidth}; + ${isRightPosition ? 'margin-left' : 'margin-right'}: ${this.labelMargin}%; + `; + const contentStyles = ` + flex: ${100 - this.labelWidth - this.labelMargin}; + ${contentMargin}; + ${ + this.component.hideLabel + ? `max-width: ${100 - this.labelWidth - this.labelMargin}` + : '' + }; + `; + + return { + isRightPosition, + isRightAlign, + labelStyles, + contentStyles, + }; + } + + /** + * Returns only the schema that is different from the default. + * + * @param schema + * @param defaultSchema + */ + getModifiedSchema(schema, defaultSchema, recursion) { + const modified = {}; + if (!defaultSchema) { + return schema; + } + _.each(schema, (val, key) => { + if ( + !_.isArray(val) && + _.isObject(val) && + Object.prototype.hasOwnProperty.call(defaultSchema, key) + ) { + const subModified = this.getModifiedSchema( + val, + defaultSchema[key], + true, + ); + if (!_.isEmpty(subModified)) { + modified[key] = subModified; + } + } else if (_.isArray(val)) { + if (val.length !== 0 && !_.isEqual(val, defaultSchema[key])) { + modified[key] = val; + } + } else if ( + (!recursion && key === 'type') || + (!recursion && key === 'key') || + (!recursion && key === 'label') || + (!recursion && key === 'input') || + (!recursion && key === 'tableView') || + (val !== '' && + !Object.prototype.hasOwnProperty.call( + defaultSchema, + key, + )) || + (val !== '' && val !== defaultSchema[key]) || + (defaultSchema[key] && val !== defaultSchema[key]) + ) { + modified[key] = val; + } + }); + return modified; + } + + /** + * Returns the JSON schema for this component. + */ + get schema() { + return fastCloneDeep( + this.getModifiedSchema( + _.omit(this.component, 'id'), + this.defaultSchema, + ), + ); + } + + /** + * Returns true if component is inside DataGrid + */ + get isInDataGrid() { + return this.inDataGrid; + } + + /** + * Translate a text using the i18n system. + * + * @param {string} text - The i18n identifier. + * @param {Object} params - The i18n parameters to use for translation. + */ + t(text, params = {}, ...args) { + if (!text) { + return ''; + } + // Use _userInput: true to ignore translations from defaults + if (text in enTranslation && params._userInput) { + return text; + } + params.data = this.rootValue; + params.row = this.data; + params.component = this.component; + return super.t(text, params, ...args); + } + + labelIsHidden() { + return ( + !this.component.label || + (((!this.isInDataGrid && this.component.hideLabel) || + (this.isInDataGrid && !this.component.dataGridLabel) || + this.options.floatingLabels || + this.options.inputsOnly) && + !this.builderMode) + ); + } + + transform(type, value) { + const frameworkTemplates = this.options.template + ? Templates.templates[this.options.template] + : Templates.current; + return Object.prototype.hasOwnProperty.call( + frameworkTemplates, + 'transform', + ) + ? frameworkTemplates.transform(type, value, this) + : (type, value) => value; + } + + getTemplate(names, modes) { + modes = Array.isArray(modes) ? modes : [modes]; + names = Array.isArray(names) ? names : [names]; + if (!modes.includes('form')) { + modes.push('form'); + } + + let result = null; + + if (this.options.templates) { + result = this.checkTemplate(this.options.templates, names, modes); + if (result) { + return result; + } + } + + const frameworkTemplates = this.options.template + ? Templates.templates[this.options.template] + : Templates.current; + result = this.checkTemplate(frameworkTemplates, names, modes); + if (result) { + return result; + } + + // Default back to bootstrap if not defined. + const name = names[names.length - 1]; + const templatesByName = Templates.defaultTemplates[name]; + + if (!templatesByName) { + return `Unknown template: ${name}`; + } + + const templateByMode = this.checkTemplateMode(templatesByName, modes); + if (templateByMode) { + return templateByMode; + } + + return templatesByName.form; + } + + checkTemplate(templates, names, modes) { + for (const name of names) { + const templatesByName = templates[name]; + + if (templatesByName) { + const templateByMode = this.checkTemplateMode( + templatesByName, + modes, + ); + if (templateByMode) { + return templateByMode; + } + } + } + + return null; + } + + checkTemplateMode(templatesByName, modes) { + for (const mode of modes) { + const templateByMode = templatesByName[mode]; + + if (templateByMode) { + return templateByMode; + } + } + + return null; + } + + getFormattedAttribute(attr) { + return attr + ? this.t(attr, { _userInput: true }).replace(/"/g, '"') + : ''; + } + + getFormattedTooltip(tooltipValue) { + const tooltip = this.interpolate(tooltipValue || '').replace( + /(?:\r\n|\r|\n)/g, + '
', + ); + return this.getFormattedAttribute(tooltip); + } + + isHtmlRenderMode() { + return this.options.renderMode === 'html'; + } + + renderTemplate(name, data = {}, modeOption) { + // Need to make this fall back to form if renderMode is not found similar to how we search templates. + const mode = modeOption || this.options.renderMode || 'form'; + data.component = this.component; + data.self = this; + data.options = this.options; + data.readOnly = this.options.readOnly; + data.iconClass = this.iconClass.bind(this); + data.size = this.size.bind(this); + data.t = this.t.bind(this); + data.transform = this.transform.bind(this); + data.id = data.id || this.id; + data.key = data.key || this.key; + data.value = data.value || this.dataValue; + data.disabled = this.disabled; + data.builder = this.builderMode; + data.render = (...args) => { + console.warn(`Form.io 'render' template function is deprecated. + If you need to render template (template A) inside of another template (template B), + pass pre-compiled template A (use this.renderTemplate('template_A_name') as template context variable for template B`); + return this.renderTemplate(...args); + }; + data.label = data.labelInfo || this.labelInfo; + data.tooltip = this.getFormattedTooltip(this.component.tooltip); + + // Allow more specific template names + const names = [ + `${name}-${this.component.type}-${this.key}`, + `${name}-${this.component.type}`, + `${name}-${this.key}`, + `${name}`, + ]; + + // Allow template alters. + return this.hook( + `render${ + name.charAt(0).toUpperCase() + name.substring(1, name.length) + }`, + this.interpolate(this.getTemplate(names, mode), data), + data, + mode, + ); + } + + /** + * Sanitize an html string. + * + * @param string + * @returns {*} + */ + sanitize(dirty, forceSanitize, options) { + if (!this.shouldSanitizeValue && !forceSanitize) { + return dirty; + } + return FormioUtils.sanitize(dirty, { + sanitizeConfig: _.merge( + this.options?.sanitizeConfig || {}, + options || {}, + ), + }); + } + + /** + * Render a template string into html. + * + * @param template + * @param data + * @param actions + * + * @return {HTMLElement|String} - The created element or an empty string if template is not specified. + */ + renderString(template, data) { + if (!template) { + return ''; + } + // Interpolate the template and populate + return this.interpolate(template, data); + } + + performInputMapping(input) { + return input; + } + + get widget() { + const settings = this.component.widget; + + if (settings && this.root?.shadowRoot) { + settings.shadowRoot = this.root.shadowRoot; + } + + const widget = + settings && Widgets[settings.type] + ? new Widgets[settings.type](settings, this.component, this) + : null; + return widget; + } + + getBrowserLanguage() { + const nav = window.navigator; + const browserLanguagePropertyKeys = [ + 'language', + 'browserLanguage', + 'systemLanguage', + 'userLanguage', + ]; + let language; + + // support for HTML 5.1 "navigator.languages" + if (Array.isArray(nav.languages)) { + for (let i = 0; i < nav.languages.length; i++) { + language = nav.languages[i]; + if (language && language.length) { + return language.split(';')[0]; + } + } + } + + // support for other well known properties in browsers + for (let i = 0; i < browserLanguagePropertyKeys.length; i++) { + language = nav[browserLanguagePropertyKeys[i]]; + if (language && language.length) { + return language.split(';')[0]; + } + } + + return null; + } + + /** + * Called before a next and previous page is triggered allowing the components + * to perform special functions. + * + * @return {*} + */ + beforePage() { + return Promise.resolve(true); + } + + beforeNext() { + return this.beforePage(true); + } + + /** + * Called before a submission is triggered allowing the components + * to perform special async functions. + * + * @return {*} + */ + beforeSubmit() { + return Promise.resolve(true); + } + + /** + * Return the submission timezone. + * + * @return {*} + */ + get submissionTimezone() { + this.options.submissionTimezone = + this.options.submissionTimezone || + _.get(this.root, 'options.submissionTimezone'); + return this.options.submissionTimezone; + } + + get timezone() { + return this.getTimezone(this.component); + } + + getTimezone(settings) { + if (settings.timezone) { + return settings.timezone; + } + if (settings.displayInTimezone === 'utc') { + return 'UTC'; + } + const submissionTimezone = this.submissionTimezone; + if ( + submissionTimezone && + (settings.displayInTimezone === 'submission' || + ((this.options.pdf || this.options.server) && + settings.displayInTimezone === 'viewer')) + ) { + return submissionTimezone; + } + + // Return current timezone if none are provided. + return currentTimezone(); + } + + loadRefs(element, refs) { + for (const ref in refs) { + const refType = refs[ref]; + const isString = typeof refType === 'string'; + + const selector = + isString && refType.includes('scope') + ? `:scope > [ref="${ref}"]` + : `[ref="${ref}"]`; + + if (isString && refType.startsWith('single')) { + this.refs[ref] = element.querySelector(selector); + } else { + this.refs[ref] = element.querySelectorAll(selector); + } + } + } + + setOpenModalElement(template) { + this.componentModal.setOpenModalElement( + template || this.getModalPreviewTemplate(), + ); + } + + getModalPreviewTemplate() { + const dataValue = + this.component.type === 'password' + ? this.dataValue.replace(/./g, '•') + : this.dataValue; + const message = this.error + ? { + level: 'error', + message: this.error.message, + } + : ''; + + let modalLabel; + + if ( + this.hasInput && + this.component.validate?.required && + !this.isPDFReadOnlyMode + ) { + modalLabel = { className: 'field-required' }; + } + + return this.renderTemplate('modalPreview', { + previewText: + this.getValueAsString(dataValue, { modalPreview: true }) || + this.t('Click to set value'), + messages: message && this.renderTemplate('message', message), + labelInfo: modalLabel, + }); + } + + build(element) { + element = element || this.element; + this.empty(element); + this.setContent(element, this.render()); + return this.attach(element); + } + + get hasModalSaveButton() { + return true; + } + + render( + children = `Unknown component: ${this.component.type}`, + topLevel = false, + ) { + const isVisible = this.visible; + this.rendered = true; + + if ( + !this.builderMode && + !this.previewMode && + this.component.modalEdit + ) { + return ComponentModal.render( + this, + { + visible: isVisible, + showSaveButton: this.hasModalSaveButton, + id: this.id, + classes: this.className, + styles: this.customStyle, + children, + }, + topLevel, + ); + } else { + return this.renderTemplate( + 'component', + { + visible: isVisible, + id: this.id, + classes: this.className, + styles: this.customStyle, + children, + }, + topLevel, + ); + } + } + + attachTooltips(toolTipsRefs) { + toolTipsRefs?.forEach((tooltip, index) => { + if (tooltip) { + const tooltipAttribute = tooltip.getAttribute('data-tooltip'); + const tooltipDataTitle = tooltip.getAttribute('data-title'); + const tooltipText = this.interpolate( + tooltipDataTitle || tooltipAttribute, + ).replace(/(?:\r\n|\r|\n)/g, '
'); + + this.tooltips[index] = tippy(tooltip, { + allowHTML: true, + trigger: 'mouseenter click focus', + placement: 'right', + zIndex: 10000, + interactive: true, + content: this.t(this.sanitize(tooltipText), { + _userInput: true, + }), + }); + } + }); + } + + createComponentModal(element, modalShouldBeOpened, currentValue) { + return new ComponentModal( + this, + element, + modalShouldBeOpened, + currentValue, + ); + } + + attach(element) { + if ( + !this.builderMode && + !this.previewMode && + this.component.modalEdit + ) { + const modalShouldBeOpened = this.componentModal + ? this.componentModal.isOpened + : false; + const currentValue = modalShouldBeOpened + ? this.componentModal.currentValue + : this.dataValue; + const openModalTemplate = + this.componentModal && modalShouldBeOpened + ? this.componentModal.openModalTemplate + : null; + this.componentModal = this.createComponentModal( + element, + modalShouldBeOpened, + currentValue, + ); + this.setOpenModalElement(openModalTemplate); + } + + this.attached = true; + this.setElement(element); + element.component = this; + + // If this already has an id, get it from the dom. If SSR, it could be different from the initiated id. + if (this.element.id) { + this.id = this.element.id; + this.component.id = this.id; + } + + this.loadRefs(element, { + messageContainer: 'single', + tooltip: 'multiple', + }); + + this.attachTooltips(this.refs.tooltip); + + // Attach logic. + this.attachLogic(); + this.autofocus(); + + // Allow global attach. + this.hook('attachComponent', element, this); + // Allow attach per component type. + const type = this.component.type; + if (type) { + this.hook( + `attach${ + type.charAt(0).toUpperCase() + + type.substring(1, type.length) + }`, + element, + this, + ); + } + + this.restoreFocus(); + + this.addons.forEach((addon) => addon.attach(element)); + + return Promise.resolve(); + } + + restoreFocus() { + const isFocused = this.root?.focusedComponent?.path === this.path; + if (isFocused) { + this.loadRefs(this.element, { input: 'multiple' }); + this.focus(this.root.currentSelection?.index); + this.restoreCaretPosition(); + } + } + + addShortcut(element, shortcut) { + // Avoid infinite recursion. + if (!element || !this.root || this.root === this) { + return; + } + + if (!shortcut) { + shortcut = this.component.shortcut; + } + + this.root.addShortcut(element, shortcut); + } + + removeShortcut(element, shortcut) { + // Avoid infinite recursion. + if (!element || this.root === this) { + return; + } + + if (!shortcut) { + shortcut = this.component.shortcut; + } + + this.root.removeShortcut(element, shortcut); + } + + /** + * Remove all event handlers. + */ + detach() { + // First iterate through each ref and delete the component so there are no dangling component references. + _.each(this.refs, (ref) => { + if (typeof ref === NodeList) { + ref.forEach((elem) => { + delete elem.component; + }); + } else if (ref) { + delete ref.component; + } + }); + this.refs = {}; + this.removeEventListeners(); + this.detachLogic(); + if (this.tooltip) { + this.tooltip.destroy(); + } + } + + checkRefresh(refreshData, changed, flags) { + const changePath = _.get(changed, 'instance.path', false); + // Don't let components change themselves. + if (changePath && this.path === changePath) { + return; + } + if (refreshData === 'data') { + this.refresh(this.data, changed, flags); + } else if ( + changePath && + getComponentPath(changed.instance) === refreshData && + changed && + changed.instance && + // Make sure the changed component is not in a different "context". Solves issues where refreshOn being set + // in fields inside EditGrids could alter their state from other rows (which is bad). + this.inContext(changed.instance) + ) { + this.refresh(changed.value, changed, flags); + } + } + + checkRefreshOn(changes, flags = {}) { + changes = changes || []; + if (flags.noRefresh) { + return; + } + if (!changes.length && flags.changed) { + changes = [flags.changed]; + } + const refreshOn = flags.fromBlur + ? this.component.refreshOnBlur + : this.component.refreshOn || this.component.redrawOn; + // If they wish to refresh on a value, then add that here. + if (refreshOn) { + if (Array.isArray(refreshOn)) { + refreshOn.forEach((refreshData) => + changes.forEach((changed) => + this.checkRefresh(refreshData, changed, flags), + ), + ); + } else { + changes.forEach((changed) => + this.checkRefresh(refreshOn, changed, flags), + ); + } + } + } + + /** + * Refreshes the component with a new value. + * + * @param value + */ + refresh(value) { + if (Object.prototype.hasOwnProperty.call(this, 'refreshOnValue')) { + this.refreshOnChanged = !_.isEqual(value, this.refreshOnValue); + } else { + this.refreshOnChanged = true; + } + this.refreshOnValue = fastCloneDeep(value); + if (this.refreshOnChanged) { + if (this.component.clearOnRefresh) { + this.setValue(null); + } + this.triggerRedraw(); + } + } + + /** + * Checks to see if a separate component is in the "context" of this component. This is determined by first checking + * if they share the same "data" object. It will then walk up the parent tree and compare its parents data objects + * with the components data and returns true if they are in the same context. + * + * Different rows of the same EditGrid, for example, are in different contexts. + * + * @param component + */ + inContext(component) { + if (component.data === this.data) { + return true; + } + let parent = this.parent; + while (parent) { + if (parent.data === component.data) { + return true; + } + parent = parent.parent; + } + + return false; + } + + get viewOnly() { + return this.options.readOnly && this.options.viewAsHtml; + } + + setElement(element) { + if (this.element) { + delete this.element.component; + delete this.element; + } + this.element = element; + } + + createViewOnlyElement() { + this.setElement( + this.ce('dl', { + id: this.id, + }), + ); + + if (this.element) { + // Ensure you can get the component info from the element. + this.element.component = this; + } + + return this.element; + } + + get defaultViewOnlyValue() { + return '-'; + } + + /** + * Uses the widget to determine the output string. + * + * @param value + * @return {*} + */ + getWidgetValueAsString(value, options) { + const noInputWidget = + !this.refs.input || + !this.refs.input[0] || + !this.refs.input[0].widget; + if (!value || noInputWidget) { + if (!this.widget || !value) { + return value; + } else { + return this.widget.getValueAsString(value); + } + } + if (Array.isArray(value)) { + const values = []; + value.forEach((val, index) => { + const widget = + this.refs.input[index] && this.refs.input[index].widget; + if (widget) { + values.push(widget.getValueAsString(val, options)); + } + }); + return values; + } + + const widget = this.refs.input[0].widget; + return widget.getValueAsString(value, options); + } + + getValueAsString(value, options) { + if (!value) { + return ''; + } + value = this.getWidgetValueAsString(value, options); + if (Array.isArray(value)) { + return value.join(', '); + } + if (_.isPlainObject(value)) { + return JSON.stringify(value); + } + if (value === null || value === undefined) { + return ''; + } + const stringValue = value.toString(); + return this.sanitize(stringValue); + } + + getView(value, options) { + if (this.component.protected) { + return '--- PROTECTED ---'; + } + return this.getValueAsString(value, options); + } + + updateItems(...args) { + this.restoreValue(); + this.onChange(...args); + } + + /** + * @param {*} data + * @param {boolean} [forceUseValue=false] - if true, return 'value' property of the data + * @return {*} + */ + itemValue(data, forceUseValue = false) { + if (_.isObject(data) && !_.isArray(data)) { + if (this.valueProperty) { + return _.get(data, this.valueProperty); + } + + if (forceUseValue) { + return data.value; + } + } + + return data; + } + + itemValueForHTMLMode(value) { + if (Array.isArray(value)) { + const values = value.map((item) => + Array.isArray(item) + ? this.itemValueForHTMLMode(item) + : this.itemValue(item), + ); + + return values.join(', '); + } + + return this.itemValue(value); + } + + createModal(element, attr, confirm) { + const dialog = this.ce('div', attr || {}); + this.setContent(dialog, this.renderTemplate('dialog')); + + // Add refs to dialog, not "this". + dialog.refs = {}; + this.loadRefs.call(dialog, dialog, { + dialogOverlay: 'single', + dialogContents: 'single', + dialogClose: 'single', + }); + + dialog.refs.dialogContents.appendChild(element); + document.body.appendChild(dialog); + document.body.classList.add('modal-open'); + + dialog.close = () => { + document.body.classList.remove('modal-open'); + dialog.dispatchEvent(new CustomEvent('close')); + }; + this.addEventListener(dialog, 'close', () => + this.removeChildFrom(dialog, document.body), + ); + + const close = (event) => { + event.preventDefault(); + dialog.close(); + }; + + const handleCloseClick = (e) => { + if (confirm) { + confirm() + .then(() => close(e)) + .catch(() => {}); + } else { + close(e); + } + }; + + this.addEventListener( + dialog.refs.dialogOverlay, + 'click', + handleCloseClick, + ); + this.addEventListener( + dialog.refs.dialogClose, + 'click', + handleCloseClick, + ); + + return dialog; + } + + get optimizeRedraw() { + if (this.options.optimizeRedraw && this.element && !this.visible) { + this.addClass(this.element, 'formio-removed'); + return true; + } + return false; + } + + /** + * Retrieves the CSS class name of this component. + * @returns {string} - The class name of this component. + */ + get className() { + let className = this.hasInput + ? `${this.transform('class', 'form-group')} has-feedback ` + : ''; + className += `formio-component formio-component-${this.component.type} `; + // TODO: find proper way to avoid overriding of default type-based component styles + if (this.key && this.key !== 'form') { + className += `formio-component-${this.key} `; + } + if (this.component.multiple) { + className += 'formio-component-multiple '; + } + if (this.component.customClass) { + className += this.component.customClass; + } + if ( + this.hasInput && + this.component.validate && + boolValue(this.component.validate.required) + ) { + className += ' required'; + } + if (this.labelIsHidden()) { + className += ' formio-component-label-hidden'; + } + if (!this.visible) { + className += ' formio-hidden'; + } + return className; + } + + /** + * Build the custom style from the layout values + * @return {string} - The custom style + */ + get customStyle() { + let customCSS = ''; + _.each(this.component.style, (value, key) => { + if (value !== '') { + customCSS += `${key}:${value};`; + } + }); + return customCSS; + } + + static get serverConditionSettings() { + return Component.conditionOperatorsSettings; + } + + get isMobile() { + return isMobile(); + } + + /** + * Returns the outside wrapping element of this component. + * @returns {HTMLElement} + */ + getElement() { + return this.element; + } + + /** + * Create an evaluation context for all script executions and interpolations. + * + * @param additional + * @return {*} + */ + evalContext(additional) { + return super.evalContext( + Object.assign( + { + component: this.component, + row: this.data, + rowIndex: this.rowIndex, + data: this.rootValue, + iconClass: this.iconClass.bind(this), + // Bind the translate function to the data context of any interpolated string. + // It is useful to translate strings in different scenarions (eg: custom edit grid templates, custom error messages etc.) + // and desirable to be publicly available rather than calling the internal {instance.t} function in the template string. + t: this.t.bind(this), + submission: this.root + ? this.root._submission + : { + data: this.rootValue, + }, + form: this.root ? this.root._form : {}, + options: this.options, + }, + additional, + ), + ); + } + + /** + * Sets the pristine flag for this component. + * + * @param pristine {boolean} - TRUE to make pristine, FALSE not pristine. + */ + setPristine(pristine) { + this.pristine = pristine; + } + + get isPristine() { + return this.pristine; + } + + setDirty(dirty) { + this.dirty = dirty; + } + + get isDirty() { + return this.dirty; + } + + /** + * Removes a value out of the data array and rebuild the rows. + * @param {number} index - The index of the data element to remove. + */ + removeValue(index) { + this.splice(index); + this.redraw(); + this.restoreValue(); + this.triggerRootChange(); + } + + iconClass(name, spinning) { + const iconset = + this.options.iconset || Templates.current.defaultIconset || 'fa'; + return Object.prototype.hasOwnProperty.call( + Templates.current, + 'iconClass', + ) + ? Templates.current.iconClass(iconset, name, spinning) + : this.options.iconset === 'fa' + ? Templates.defaultTemplates.iconClass(iconset, name, spinning) + : name; + } + + size(size) { + return Object.prototype.hasOwnProperty.call(Templates.current, 'size') + ? Templates.current.size(size) + : size; + } + + /** + * The readible name for this component. + * @returns {string} - The name of the component. + */ + get name() { + return this.t( + this.component.label || this.component.placeholder || this.key, + { _userInput: true }, + ); + } + + /** + * Returns the error label for this component. + * @return {*} + */ + get errorLabel() { + return this.t( + this.component.errorLabel || + this.component.label || + this.component.placeholder || + this.key, + ); + } + + /** + * Get the error message provided a certain type of error. + * @param type + * @return {*} + */ + errorMessage(type) { + return this.component.errors && this.component.errors[type] + ? this.component.errors[type] + : type; + } + + setContent(element, content, forceSanitize, sanitizeOptions) { + if (element instanceof HTMLElement) { + element.innerHTML = this.sanitize( + content, + forceSanitize, + sanitizeOptions, + ); + return true; + } + return false; + } + + restoreCaretPosition() { + if (this.root?.currentSelection) { + if (this.refs.input?.length) { + const { selection, index } = this.root.currentSelection; + let input = this.refs.input[index]; + const isInputRangeSelectable = (i) => + /text|search|password|tel|url/i.test(i?.type || ''); + if (input) { + if (isInputRangeSelectable(input)) { + input.setSelectionRange(...selection); + } + } else { + input = this.refs.input[this.refs.input.length]; + const lastCharacter = input.value?.length || 0; + if (isInputRangeSelectable(input)) { + input.setSelectionRange(lastCharacter, lastCharacter); + } + } + } + } + } + + redraw() { + // Don't bother if we have not built yet. + if (!this.element || !this.element.parentNode || this.optimizeRedraw) { + // Return a non-resolving promise. + return Promise.resolve(); + } + this.detach(); + this.emit('redraw'); + // Since we are going to replace the element, we need to know it's position so we can find it in the parent's children. + const parent = this.element.parentNode; + const index = Array.prototype.indexOf.call( + parent.children, + this.element, + ); + this.element.outerHTML = this.sanitize(this.render()); + this.setElement(parent.children[index]); + return this.attach(this.element); + } + + rebuild() { + this.destroy(); + this.init(); + this.visible = this.conditionallyVisible(null, null); + return this.redraw(); + } + + removeEventListeners() { + super.removeEventListeners(); + this.tooltips.forEach((tooltip) => tooltip.destroy()); + this.tooltips = []; + } + + hasClass(element, className) { + if (!element) { + return; + } + + return super.hasClass(element, this.transform('class', className)); + } + + addClass(element, className) { + if (!element) { + return; + } + + return super.addClass(element, this.transform('class', className)); + } + + removeClass(element, className) { + if (!element) { + return; + } + + return super.removeClass(element, this.transform('class', className)); + } + + /** + * Determines if this component has a condition defined. + * + * @return {null} + */ + hasCondition() { + if (this._hasCondition !== null) { + return this._hasCondition; + } + + this._hasCondition = FormioUtils.hasCondition(this.component); + return this._hasCondition; + } + + /** + * Check if this component is conditionally visible. + * + * @param data + * @return {boolean} + */ + conditionallyVisible(data, row) { + data = data || this.rootValue; + row = row || this.data; + if (this.builderMode || this.previewMode || !this.hasCondition()) { + return !this.component.hidden; + } + data = data || (this.root ? this.root.data : {}); + return this.checkCondition(row, data); + } + + /** + * Checks the condition of this component. + * + * TODO: Switch row and data parameters to be consistent with other methods. + * + * @param row - The row contextual data. + * @param data - The global data object. + * @return {boolean} - True if the condition applies to this component. + */ + checkCondition(row, data) { + return FormioUtils.checkCondition( + this.component, + row || this.data, + data || this.rootValue, + this.root ? this.root._form : {}, + this, + ); + } + + /** + * Check for conditionals and hide/show the element based on those conditions. + */ + checkComponentConditions(data, flags, row) { + data = data || this.rootValue; + flags = flags || {}; + row = row || this.data; + + if ( + !this.builderMode & !this.previewMode && + this.fieldLogic(data, row) + ) { + this.redraw(); + } + + // Check advanced conditions + const visible = this.conditionallyVisible(data, row); + + if (this.visible !== visible) { + this.visible = visible; + } + + return visible; + } + + /** + * Checks conditions for this component and any sub components. + * @param args + * @return {boolean} + */ + checkConditions(data, flags, row) { + data = data || this.rootValue; + flags = flags || {}; + row = row || this.data; + return this.checkComponentConditions(data, flags, row); + } + + get logic() { + return this.component.logic || []; + } + + /** + * Check all triggers and apply necessary actions. + * + * @param data + */ + fieldLogic(data, row) { + data = data || this.rootValue; + row = row || this.data; + const logics = this.logic; + + // If there aren't logic, don't go further. + if (logics.length === 0) { + return; + } + + const newComponent = fastCloneDeep(this.originalComponent); + + let changed = logics.reduce((changed, logic) => { + const result = FormioUtils.checkTrigger( + newComponent, + logic.trigger, + row, + data, + this.root ? this.root._form : {}, + this, + ); + + return ( + (result + ? this.applyActions( + newComponent, + logic.actions, + result, + row, + data, + ) + : false) || changed + ); + }, false); + + // If component definition changed, replace and mark as changed. + if (!_.isEqual(this.component, newComponent)) { + this.component = newComponent; + changed = true; + const disabled = this.shouldDisabled; + // Change disabled state if it has changed + if (this.disabled !== disabled) { + this.disabled = disabled; + } + } + + return changed; + } - /** - * Custom JavaScript validation. - */ - custom: '', + isIE() { + if (typeof window === 'undefined') { + return false; + } - /** - * If the custom validation should remain private (only the backend will see it and execute it). - */ - customPrivate: false, + const userAgent = window.navigator.userAgent; - /** - * If this component should implement a strict date validation if the Calendar widget is implemented. - */ - strictDateValidation: false, - multiple: false, - unique: false - }, - - /** - * The simple conditional settings for a component. - */ - conditional: { - show: null, - when: null, - eq: '' - }, - overlay: { - style: '', - left: '', - top: '', - width: '', - height: '', - }, - allowCalculateOverride: false, - encrypted: false, - showCharCount: false, - showWordCount: false, - properties: {}, - allowMultipleMasks: false, - addons: [], - }, ...sources); - } - - /** - * Return the validator as part of the component. - * - * @return {ValidationChecker} - * @constructor - */ - static get Validator() { - return Validator; - } - /** - * Return the simple condition settings as part of the component. - * - * @return {Object} - * - */ - static get conditionOperatorsSettings() { - return { - operators: ['isEqual', 'isNotEqual', 'isEmpty', 'isNotEmpty'], - valueComponent() { - return { - type: 'textfield', - widget: { - type: 'input' - } - }; - } - }; - } - /** - * Return the array of possible types of component value absed on its schema. - * - * @param schema - * @return {Array} - * - */ - - static savedValueTypes(schema) { - schema = schema || {}; - - return FormioUtils.getComponentSavedTypes(schema) || [FormioUtils.componentValueTypes.any]; - } - /** - * Provides a table view for this component. Override if you wish to do something different than using getView - * method of your instance. - * - * @param value - * @param options - */ - /* eslint-disable no-unused-vars */ - static tableView(value, options) {} - /* eslint-enable no-unused-vars */ - - /** - * Initialize a new Component. - * - * @param {Object} component - The component JSON you wish to initialize. - * @param {Object} options - The options for this component. - * @param {Object} data - The global data submission object this component will belong. - */ - /* eslint-disable max-statements */ - constructor(component, options, data) { - super(Object.assign({ - renderMode: 'form', - attachMode: 'full', - noDefaults: false - }, options || {})); - - // Restore the component id. - if (component && component.id) { - this.id = component.id; - } - - /** - * Determines if this component has a condition assigned to it. - * @type {null} - * @private - */ - this._hasCondition = null; - - /** - * References to dom elements - */ - this.refs = {}; - - // Allow global override for any component JSON. - if ( - component && - this.options.components && - this.options.components[component.type] - ) { - _.merge(component, this.options.components[component.type]); + const msie = userAgent.indexOf('MSIE '); + if (msie > 0) { + // IE 10 or older => return version number + return parseInt( + userAgent.substring(msie + 5, userAgent.indexOf('.', msie)), + 10, + ); + } + + const trident = userAgent.indexOf('Trident/'); + if (trident > 0) { + // IE 11 => return version number + const rv = userAgent.indexOf('rv:'); + return parseInt( + userAgent.substring(rv + 3, userAgent.indexOf('.', rv)), + 10, + ); + } + + const edge = userAgent.indexOf('Edge/'); + if (edge > 0) { + // IE 12 (aka Edge) => return version number + return parseInt( + userAgent.substring(edge + 5, userAgent.indexOf('.', edge)), + 10, + ); + } + + // other browser + return false; } - /** - * Set the validator instance. - */ - this.validator = Validator; + defineActionValue(action, argsObject) { + return this.evaluate(action.value, argsObject, 'value'); + } + + applyActions(newComponent, actions, result, row, data) { + data = data || this.rootValue; + row = row || this.data; + + return actions.reduce((changed, action) => { + switch (action.type) { + case 'property': { + FormioUtils.setActionProperty( + newComponent, + action, + result, + row, + data, + this, + ); + + const property = action.property.value; + if ( + !_.isEqual( + _.get(this.component, property), + _.get(newComponent, property), + ) + ) { + changed = true; + } + + break; + } + case 'value': { + const oldValue = this.getValue(); + const newValue = this.defineActionValue(action, { + value: _.clone(oldValue), + data, + row, + component: newComponent, + result, + }); + + if ( + !_.isEqual(oldValue, newValue) && + !(this.component.clearOnHide && !this.visible) + ) { + this.setValue(newValue); + + if (this.viewOnly) { + this.dataValue = newValue; + } + + changed = true; + } + + break; + } + case 'mergeComponentSchema': { + const schema = this.evaluate( + action.schemaDefinition, + { + value: _.clone(this.getValue()), + data, + row, + component: newComponent, + result, + }, + 'schema', + ); + + _.assign(newComponent, schema); + + if (!_.isEqual(this.component, newComponent)) { + changed = true; + } + + break; + } + case 'customAction': { + const oldValue = this.getValue(); + const newValue = this.evaluate( + action.customAction, + { + value: _.clone(oldValue), + data, + row, + input: oldValue, + component: newComponent, + result, + }, + 'value', + ); + + if ( + !_.isEqual(oldValue, newValue) && + !(this.component.clearOnHide && !this.visible) + ) { + this.setValue(newValue); + + if (this.viewOnly) { + this.dataValue = newValue; + } + + changed = true; + } + + break; + } + } + + return changed; + }, false); + } + + // Deprecated + addInputError(message, dirty, elements) { + this.addMessages(message); + this.setErrorClasses(elements, dirty, !!message); + } + + // Deprecated + removeInputError(elements) { + this.setErrorClasses(elements, true, false); + } /** - * The data path to this specific component instance. + * Add a new input error to this element. * - * @type {string} + * @param message + * @param dirty */ - this.path = ''; + addMessages(messages) { + if (!messages) { + return; + } - /** - * The Form.io component JSON schema. - * @type {*} - */ - this.component = this.mergeSchema(component || {}); + // Standardize on array of objects for message. + if (typeof messages === 'string') { + messages = { + messages, + level: 'error', + }; + } + + if (!Array.isArray(messages)) { + messages = [messages]; + } - // Add the id to the component. - this.component.id = this.id; + messages = _.uniqBy(messages, (message) => message.message); + + if (this.refs.messageContainer) { + this.setContent( + this.refs.messageContainer, + messages + .map((message) => { + if ( + message.message && + typeof message.message === 'string' + ) { + message.message = message.message + .replaceAll('<', '<') + .replaceAll('>', '>'); + } + return this.renderTemplate('message', message); + }) + .join(''), + ); + } + } - this.afterComponentAssign(); + setErrorClasses( + elements, + dirty, + hasErrors, + hasMessages, + element = this.element, + ) { + this.clearErrorClasses(); + elements.forEach((element) => { + this.setElementInvalid(this.performInputMapping(element), false); + }); + this.setInputWidgetErrorClasses(elements, hasErrors); + // do not set error classes for hidden components + if (!this.visible) { + return; + } - // Save off the original component to be used in logic. - this.originalComponent = fastCloneDeep(this.component); + if (hasErrors) { + // Add error classes + elements.forEach((input) => { + this.setElementInvalid(this.performInputMapping(input), true); + }); - /** - * If the component has been attached - */ - this.attached = false; + if (dirty && this.options.highlightErrors) { + this.addClass(element, this.options.componentErrorClass); + } else { + this.addClass(element, 'has-error'); + } + } + if (hasMessages) { + this.addClass(element, 'has-message'); + } + } + + setElementInvalid(element, invalid) { + if (!element) return; + + if (invalid) { + this.addClass(element, 'is-invalid'); + } else { + this.removeClass(element, 'is-invalid'); + } + element.setAttribute('aria-invalid', invalid ? 'true' : 'false'); + } + + clearOnHide() { + // clearOnHide defaults to true for old forms (without the value set) so only trigger if the value is false. + if ( + // if change happens inside EditGrid's row, it doesn't trigger change on the root level, so rootPristine will be true + (!this.rootPristine || + this.options.server || + isInsideScopingComponent(this)) && + this.component.clearOnHide !== false && + !this.options.readOnly && + !this.options.showHiddenFields + ) { + if (!this.visible) { + this.deleteValue(); + } else if (!this.hasValue() && this.shouldAddDefaultValue) { + // If shown, ensure the default is set. + this.setValue(this.defaultValue, { + noUpdateEvent: true, + }); + } + } + } + + triggerRootChange(...args) { + if (this.options.onChange) { + this.options.onChange(...args); + } else if (this.root) { + this.root.triggerChange(...args); + } + } + + onChange(flags, fromRoot) { + flags = flags || {}; + if (flags.modified) { + if (!flags.noPristineChangeOnModified) { + this.pristine = false; + } + this.addClass(this.getElement(), 'formio-modified'); + } + + // If we are supposed to validate on blur, then don't trigger validation yet. + if (this.component.validateOn === 'blur' && !this.errors.length) { + flags.noValidate = true; + } + + if (this.component.onChange) { + this.evaluate(this.component.onChange, { + flags, + }); + } + + // Set the changed variable. + const changed = { + instance: this, + component: this.component, + value: this.dataValue, + flags: flags, + }; + + // Emit the change. + this.emit('componentChange', changed); + + // Do not propogate the modified flag. + let modified = false; + if (flags.modified) { + modified = true; + delete flags.modified; + } + + // Bubble this change up to the top. + if (!fromRoot) { + this.triggerRootChange(flags, changed, modified); + } + return changed; + } + + get wysiwygDefault() { + return { + quill: { + theme: 'snow', + placeholder: this.t(this.component.placeholder, { + _userInput: true, + }), + modules: { + toolbar: [ + [{ size: ['small', false, 'large', 'huge'] }], // custom dropdown + [{ header: [1, 2, 3, 4, 5, 6, false] }], + [{ font: [] }], + [ + 'bold', + 'italic', + 'underline', + 'strike', + { script: 'sub' }, + { script: 'super' }, + 'clean', + ], + [{ color: [] }, { background: [] }], + [ + { list: 'ordered' }, + { list: 'bullet' }, + { indent: '-1' }, + { indent: '+1' }, + { align: [] }, + ], + ['blockquote', 'code-block'], + ['link', 'image', 'video', 'formula', 'source'], + ], + }, + }, + ace: { + theme: 'ace/theme/xcode', + maxLines: 12, + minLines: 12, + tabSize: 2, + mode: 'ace/mode/javascript', + placeholder: this.t(this.component.placeholder, { + _userInput: true, + }), + }, + ckeditor: { + image: { + toolbar: [ + 'imageTextAlternative', + '|', + 'imageStyle:full', + 'imageStyle:alignLeft', + 'imageStyle:alignCenter', + 'imageStyle:alignRight', + ], + styles: ['full', 'alignLeft', 'alignCenter', 'alignRight'], + }, + extraPlugins: [], + }, + default: {}, + }; + } + + addCKE(element, settings, onChange) { + settings = _.isEmpty(settings) ? {} : settings; + settings.base64Upload = this.component.isUploadEnabled ? false : true; + settings.mediaEmbed = { previewsInData: true }; + settings = _.merge( + this.wysiwygDefault.ckeditor, + _.get(this.options, 'editors.ckeditor.settings', {}), + settings, + ); + + if (this.component.isUploadEnabled) { + settings.extraPlugins.push( + getFormioUploadAdapterPlugin(this.fileService, this), + ); + } + + return Formio.requireLibrary( + 'ckeditor', + isIEBrowser ? 'CKEDITOR' : 'ClassicEditor', + _.get( + this.options, + 'editors.ckeditor.src', + `${Formio.cdn.ckeditor}/ckeditor.js`, + ), + true, + ).then(() => { + if (!element.parentNode) { + return Promise.reject(); + } + if (isIEBrowser) { + const editor = CKEDITOR.replace(element); + editor.on('change', () => onChange(editor.getData())); + return Promise.resolve(editor); + } else { + return ClassicEditor.create(element, settings).then( + (editor) => { + editor.model.document.on('change', () => + onChange(editor.data.get()), + ); + return editor; + }, + ); + } + }); + } + + addQuill(element, settings, onChange) { + settings = _.isEmpty(settings) ? this.wysiwygDefault.quill : settings; + settings = _.merge( + this.wysiwygDefault.quill, + _.get(this.options, 'editors.quill.settings', {}), + settings, + ); + settings = { + ...settings, + modules: { + table: true, + ...settings.modules, + }, + }; + // Lazy load the quill css. + Formio.requireLibrary( + `quill-css-${settings.theme}`, + 'Quill', + [ + { + type: 'styles', + src: `${Formio.cdn.quill}/quill.${settings.theme}.css`, + }, + ], + true, + ); + + // Lazy load the quill library. + return Formio.requireLibrary( + 'quill', + 'Quill', + _.get( + this.options, + 'editors.quill.src', + `${Formio.cdn.quill}/quill.min.js`, + ), + true, + ).then(() => { + return Formio.requireLibrary( + 'quill-table', + 'Quill', + `${Formio.cdn.baseUrl}/quill/quill-table.js`, + true, + ).then(() => { + if (!element.parentNode) { + return Promise.reject(); + } + this.quill = new Quill( + element, + isIEBrowser ? { ...settings, modules: {} } : settings, + ); + + /** This block of code adds the [source] capabilities. See https://codepen.io/anon/pen/ZyEjrQ **/ + const txtArea = document.createElement('textarea'); + txtArea.setAttribute('class', 'quill-source-code'); + this.quill.addContainer('ql-custom').appendChild(txtArea); + const qlSource = element.parentNode.querySelector('.ql-source'); + if (qlSource) { + this.addEventListener(qlSource, 'click', (event) => { + event.preventDefault(); + if (txtArea.style.display === 'inherit') { + this.quill.setContents( + this.quill.clipboard.convert({ + html: txtArea.value, + }), + ); + } + txtArea.style.display = + txtArea.style.display === 'none' + ? 'inherit' + : 'none'; + }); + } + /** END CODEBLOCK **/ + + // Make sure to select cursor when they click on the element. + this.addEventListener(element, 'click', () => + this.quill.focus(), + ); + + // Allows users to skip toolbar items when tabbing though form + const elm = document.querySelectorAll('.ql-formats > button'); + for (let i = 0; i < elm.length; i++) { + elm[i].setAttribute('tabindex', '-1'); + } + + this.quill.on('text-change', () => { + txtArea.value = this.quill.root.innerHTML; + onChange(txtArea); + }); + return this.quill; + }); + }); + } + + get shouldSanitizeValue() { + // Sanitize value if sanitizing for thw whole content is turned off + return this.options?.sanitize !== false; + } + + addAce(element, settings, onChange) { + if (!settings || settings.theme === 'snow') { + const mode = settings ? settings.mode : ''; + settings = {}; + if (mode) { + settings.mode = mode; + } + } + settings = _.merge( + this.wysiwygDefault.ace, + _.get(this.options, 'editors.ace.settings', {}), + settings || {}, + ); + return Formio.requireLibrary( + 'ace', + 'ace', + _.get(this.options, 'editors.ace.src', `${Formio.cdn.ace}/ace.js`), + true, + ).then((editor) => { + editor = editor.edit(element); + editor.removeAllListeners('change'); + editor.setOptions(settings); + editor.getSession().setMode(settings.mode); + editor.on('change', () => onChange(editor.getValue())); + if (settings.isUseWorkerDisabled) { + editor.session.setUseWorker(false); + } + return editor; + }); + } + + get tree() { + return this.component.tree || false; + } /** - * If the component has been rendered + * The empty value for this component. + * + * @return {null} */ - this.rendered = false; + get emptyValue() { + return null; + } /** - * The data object in which this component resides. - * @type {*} + * Returns if this component has a value set. + * */ - this._data = data || {}; + hasValue(data) { + return !_.isUndefined(_.get(data || this.data, this.key)); + } /** - * The existing error that this component has. - * @type {string} + * Get the data value at the root level. + * + * @return {*} */ - this.error = ''; + get rootValue() { + return this.root ? this.root.data : this.data; + } + + get rootPristine() { + return _.get(this, 'root.pristine', false); + } /** - * Tool tip text after processing - * @type {string} + * Get the static value of this component. + * @return {*} */ - this.tooltip = ''; + get dataValue() { + if ( + !this.key || + (!this.visible && this.component.clearOnHide && !this.rootPristine) + ) { + return this.emptyValue; + } + if (!this.hasValue() && this.shouldAddDefaultValue) { + const empty = this.component.multiple ? [] : this.emptyValue; + if (!this.rootPristine) { + this.dataValue = empty; + } + return empty; + } + return _.get(this._data, this.key); + } /** - * The row path of this component. - * @type {number} + * Sets the static value of this component. + * + * @param value */ - this.row = this.options.row; + set dataValue(value) { + if ( + !this.allowData || + !this.key || + (!this.visible && this.component.clearOnHide && !this.rootPristine) + ) { + return; + } + if (value !== null && value !== undefined) { + value = this.hook('setDataValue', value, this.key, this._data); + } + if (value === null || value === undefined) { + this.unset(); + return; + } + _.set(this._data, this.key, value); + return; + } /** - * Determines if this component is disabled, or not. + * Splice a value from the dataValue. * - * @type {boolean} + * @param index */ - this._disabled = boolValue(this.component.disabled) ? this.component.disabled : false; + splice(index, flags = {}) { + if (this.hasValue()) { + const dataValue = this.dataValue || []; + if ( + _.isArray(dataValue) && + Object.prototype.hasOwnProperty.call(dataValue, index) + ) { + dataValue.splice(index, 1); + this.dataValue = dataValue; + this.triggerChange(flags); + } + } + } + + unset() { + _.unset(this._data, this.key); + } /** - * Points to the root component, usually the FormComponent. - * - * @type {Component} + * Deletes the value of the component. */ - this.root = this.options.root; - this.localRoot = this.options.localRoot; + deleteValue() { + this.setValue(null, { + noUpdateEvent: true, + noDefault: true, + }); + this.unset(); + } + + getCustomDefaultValue(defaultValue) { + if (this.component.customDefaultValue && !this.options.preview) { + defaultValue = this.evaluate( + this.component.customDefaultValue, + { value: '' }, + 'value', + ); + } + return defaultValue; + } + + get shouldAddDefaultValue() { + return ( + !this.options.noDefaults || + (this.component.defaultValue && + !this.isEmpty(this.component.defaultValue)) || + this.component.customDefaultValue + ); + } + + get defaultValue() { + let defaultValue = this.emptyValue; + if (this.component.defaultValue) { + defaultValue = this.component.defaultValue; + } + + defaultValue = this.getCustomDefaultValue(defaultValue); + + const checkMask = (value) => { + if (typeof value === 'string') { + if (this.component.type !== 'textfield') { + const placeholderChar = this.placeholderChar; + + value = conformToMask(value, this.defaultMask, { + placeholderChar, + }).conformedValue; + if (!FormioUtils.matchInputMask(value, this.defaultMask)) { + value = ''; + } + } + } else { + value = ''; + } + return value; + }; + + if (this.defaultMask) { + if (Array.isArray(defaultValue)) { + defaultValue = defaultValue.map(checkMask); + } else { + defaultValue = checkMask(defaultValue); + } + } + + // Clone so that it creates a new instance. + return _.cloneDeep(defaultValue); + } /** - * If this input has been input and provided value. + * Get the input value of this component. * - * @type {boolean} + * @return {*} */ - this.pristine = true; + getValue() { + if ( + !this.hasInput || + this.viewOnly || + !this.refs.input || + !this.refs.input.length + ) { + return this.dataValue; + } + const values = []; + for (const i in this.refs.input) { + if (Object.prototype.hasOwnProperty.call(this.refs.input, i)) { + if (!this.component.multiple) { + return this.getValueAt(i); + } + values.push(this.getValueAt(i)); + } + } + if (values.length === 0 && !this.component.multiple) { + return ''; + } + + return values; + } /** - * Points to the parent component. + * Get the value at a specific index. * - * @type {Component} - */ - this.parent = this.options.parent; - - this.options.name = this.options.name || 'data'; - - /** - * The validators that are assigned to this component. - * @type {[string]} - */ - this.validators = ['required', 'minLength', 'maxLength', 'minWords', 'maxWords', 'custom', 'pattern', 'json', 'mask']; - - this._path = ''; - // Nested forms don't have parents so we need to pass their path in. - this._parentPath = this.options.parentPath || ''; - - // Needs for Nextgen Rules Engine - this.resetCaches(); - - /** - * Determines if this component is visible, or not. - */ - this._parentVisible = Object.prototype.hasOwnProperty.call(this.options, 'parentVisible') ? this.options.parentVisible : true; - this._visible = this._parentVisible && this.conditionallyVisible(null, data); - this._parentDisabled = false; - - /** - * Used to trigger a new change in this component. - * @type {function} - Call to trigger a change in this component. - */ - let changes = []; - let lastChanged = null; - let triggerArgs = []; - const _triggerChange = _.debounce((...args) => { - if (this.root) { - this.root.changing = false; - } - triggerArgs = []; - if (!args[1] && lastChanged) { - // Set the changed component if one isn't provided. - args[1] = lastChanged; - } - if (_.isEmpty(args[0]) && lastChanged) { - // Set the flags if it is empty and lastChanged exists. - args[0] = lastChanged.flags; - } - lastChanged = null; - args[3] = changes; - const retVal = this.onChange(...args); - changes = []; - return retVal; - }, 100); - this.triggerChange = (...args) => { - if (args[1]) { - // Make sure that during the debounce that we always track lastChanged component, even if they - // don't provide one later. - lastChanged = args[1]; - changes.push(lastChanged); - } - if (this.root) { - this.root.changing = true; - } - if (args.length) { - triggerArgs = args; - } - return _triggerChange(...triggerArgs); - }; - - /** - * Used to trigger a redraw event within this component. - * - * @type {Function} + * @param index + * @returns {*} */ - this.triggerRedraw = _.debounce(this.redraw.bind(this), 100); + getValueAt(index) { + const input = this.performInputMapping(this.refs.input[index]); + return input ? input.value : undefined; + } /** - * list of attached tooltips - * @type {Array} + * Set the value of this component. + * + * @param value + * @param flags + * + * @return {boolean} - If the value changed. */ - this.tooltips = []; + setValue(value, flags = {}) { + const changed = this.updateValue(value, flags); + value = this.dataValue; + if (!this.hasInput) { + return changed; + } + const isArray = Array.isArray(value); + const valueInput = this.refs.fileLink || this.refs.input; + if ( + isArray && + Array.isArray(this.defaultValue) && + Object.prototype.hasOwnProperty.call(this.refs, 'input') && + valueInput && + valueInput.length !== value.length && + this.visible + ) { + this.redraw(); + } + if ( + this.isHtmlRenderMode() && + flags && + flags.fromSubmission && + changed + ) { + this.redraw(); + return changed; + } + for (const i in this.refs.input) { + if (Object.prototype.hasOwnProperty.call(this.refs.input, i)) { + this.setValueAt(i, isArray ? value[i] : value, flags); + } + } + return changed; + } /** - * List of attached addons - * @type {Array} + * Set the value at a specific index. + * + * @param index + * @param value */ - this.addons = []; + setValueAt(index, value, flags = {}) { + if ( + !flags.noDefault && + (value === null || value === undefined) && + !this.component.multiple + ) { + value = this.defaultValue; + } - // To force this component to be invalid. - this.invalid = false; + const input = this.performInputMapping(this.refs.input[index]); + const valueMaskInput = this.refs.valueMaskInput; - if (this.component) { - this.type = this.component.type; - if (this.allowData && this.key) { - this.options.name += `[${this.key}]`; - // If component is visible or not set to clear on hide, set the default value. - if (this.visible || !this.component.clearOnHide) { - if (!this.hasValue()) { - if (this.shouldAddDefaultValue) { - this.dataValue = this.defaultValue; - } - } - else { - // Ensure the dataValue is set. - /* eslint-disable no-self-assign */ - this.dataValue = this.dataValue; - /* eslint-enable no-self-assign */ - } + if (valueMaskInput?.mask && valueMaskInput.mask.textMaskInputElement) { + valueMaskInput.mask.textMaskInputElement.update(value); } - } - /** - * The element information for creating the input element. - * @type {*} - */ - this.info = this.elementInfo(); + if (input.mask && input.mask.textMaskInputElement) { + input.mask.textMaskInputElement.update(value); + } else if (input.widget && input.widget.setValue) { + input.widget.setValue(value); + } else { + input.value = value; + } } - // Allow anyone to hook into the component creation. - this.hook('component'); - - if (!this.options.skipInit) { - this.init(); + get hasSetValue() { + return this.hasValue() && !this.isEmpty(this.dataValue); } - } - /* eslint-enable max-statements */ - - get data() { - return this._data; - } - set data(value) { - this._data = value; - } - - mergeSchema(component = {}) { - return _.defaultsDeep(component, this.defaultSchema); - } - - // Allow componets to notify when ready. - get ready() { - return Promise.resolve(this); - } + setDefaultValue() { + if (this.defaultValue && this.shouldAddDefaultValue) { + const defaultValue = + this.component.multiple && !this.dataValue.length + ? [] + : this.defaultValue; + this.setValue(defaultValue, { + noUpdateEvent: true, + }); + } + } - get isPDFReadOnlyMode() { - return this.parent && - this.parent.form && - (this.parent.form.display === 'pdf') && - this.options.readOnly; - } + /** + * Restore the value of a control. + */ + restoreValue() { + if (this.hasSetValue) { + this.setValue(this.dataValue, { + noUpdateEvent: true, + }); + } else { + this.setDefaultValue(); + } + } - get labelInfo() { - const label = {}; - label.hidden = this.labelIsHidden(); + /** + * Normalize values coming into updateValue. + * + * @param value + * @return {*} + */ + normalizeValue(value) { + if (this.component.multiple && !Array.isArray(value)) { + value = value ? [value] : []; + } + return value; + } - label.className = ''; - label.labelPosition = this.component.labelPosition; - label.tooltipClass = `${this.iconClass('question-sign')} text-muted`; + /** + * Update a value of this component. + * + * @param flags + */ + updateComponentValue(value, flags = {}) { + let newValue = + !flags.resetValue && (value === undefined || value === null) + ? this.getValue() + : value; + newValue = this.normalizeValue(newValue, flags); + const oldValue = this.dataValue; + let changed = + newValue !== undefined + ? this.hasChanged(newValue, oldValue) + : false; + if (changed) { + this.dataValue = newValue; + changed = this.dataValue !== oldValue; + this.updateOnChange(flags, changed); + } + if (this.componentModal && flags && flags.fromSubmission) { + this.componentModal.setValue(value); + } + return changed; + } - const isPDFReadOnlyMode = this.isPDFReadOnlyMode; + /** + * Updates the value of this component plus all sub-components. + * + * @param args + * @return {boolean} + */ + updateValue(...args) { + return this.updateComponentValue(...args); + } - if (this.hasInput && this.component.validate && boolValue(this.component.validate.required) && !isPDFReadOnlyMode) { - label.className += ' field-required'; + getIcon(name, content, styles, ref = 'icon') { + return this.renderTemplate('icon', { + className: this.iconClass(name), + ref, + styles, + content, + }); } - if (label.hidden) { - label.className += ' control-label--hidden'; + + /** + * Resets the value of this component. + */ + resetValue() { + this.unset(); + this.setValue(this.emptyValue, { + noUpdateEvent: true, + noValidate: true, + resetValue: true, + }); } - if (this.info.attr.id) { - label.for = this.info.attr.id; + + /** + * Determine if the value of this component has changed. + * + * @param newValue + * @param oldValue + * @return {boolean} + */ + hasChanged(newValue, oldValue) { + if ( + (newValue === undefined || newValue === null) && + (oldValue === undefined || + oldValue === null || + this.isEmpty(oldValue)) + ) { + return false; + } + // If we do not have a value and are getting set to anything other than undefined or null, then we changed. + if ( + newValue !== undefined && + newValue !== null && + this.allowData && + !this.hasValue() + ) { + return true; + } + return !_.isEqual(newValue, oldValue); } - return label; - } - init() { - this.disabled = this.shouldDisabled; - this._visible = this.conditionallyVisible(null, null); - if (this.component.addons?.length) { - this.component.addons.forEach((addon) => this.createAddon(addon)); + /** + * Update the value on change. + * + * @param flags + */ + updateOnChange(flags = {}, changed = false) { + if (!flags.noUpdateEvent && changed) { + this.triggerChange(flags); + return true; + } + return false; } - } - afterComponentAssign() { - //implement in extended classes - } - - createAddon(addonConfiguration) { - const name = addonConfiguration.name; - if (!name) { - return; - } - - const settings = addonConfiguration.settings?.data || {}; - const Addon = Addons[name.value]; - - let addon = null; + /** + * Perform a calculated value operation. + * + * @param data - The global data object. + * + * @return {boolean} - If the value changed during calculation. + */ - if (Addon) { - const supportedComponents = Addon.info.supportedComponents; - const supportsThisComponentType = !supportedComponents?.length || - supportedComponents.indexOf(this.component.type) !== -1; - if (supportsThisComponentType) { - addon = new Addon(settings, this); - this.addons.push(addon); - } - else { - console.warn(`Addon ${name.label} does not support component of type ${this.component.type}.`); - } + convertNumberOrBoolToString(value) { + if (typeof value === 'number' || typeof value === 'boolean') { + return value.toString(); + } + return value; } - return addon; - } - - teardown() { - if (this.element) { - delete this.element.component; - delete this.element; + doValueCalculation(dataValue, data, row) { + return this.evaluate( + this.component.calculateValue, + { + value: dataValue, + data, + row: row || this.data, + submission: this.root?._submission || { + data: this.rootValue, + }, + }, + 'value', + ); } - delete this._currentForm; - delete this.parent; - delete this.root; - delete this.triggerChange; - delete this.triggerRedraw; - if (this.options) { - delete this.options.root; - delete this.options.parent; - delete this.options.i18next; - } - super.teardown(); - } - - destroy(all = false) { - super.destroy(all); - this.detach(); - this.addons.forEach((addon) => addon.destroy()); - if (all) { - this.teardown(); - } - } - - get shouldDisabled() { - return this.options.readOnly || this.component.disabled || (Object.prototype.hasOwnProperty.call(this.options, 'disabled') && this.options.disabled[this.key]); - } - - get isInputComponent() { - return !Object.prototype.hasOwnProperty.call(this.component, 'input') || this.component.input; - } - - get allowData() { - return this.hasInput; - } - - get hasInput() { - return this.isInputComponent || (this.refs.input && this.refs.input.length); - } - - get defaultSchema() { - return Component.schema(); - } - get key() { - return _.get(this.component, 'key', ''); - } - - set parentVisible(value) { - this._parentVisible = value; - } - - get parentVisible() { - return this._parentVisible; - } - - set parentDisabled(value) { - this._parentDisabled = value; - } - - get parentDisabled() { - return this._parentDisabled; - } - - shouldForceVisibility(component, visibility) { - if (!this.options[visibility]) { - return false; - } - if (!component) { - component = this.component; - } - if (_.isArray(this.options[visibility])) { - return this.options[visibility].includes(component.key); - } - return this.options[visibility][component.key]; - } - - shouldForceHide(component) { - return this.shouldForceVisibility(component, 'hide'); - } - - shouldForceShow(component) { - return this.shouldForceVisibility(component, 'show'); - } - - /** - * - * @param value {boolean} - */ - set visible(value) { - if (this._visible !== value) { - // Skip if this component is set to visible and is supposed to be hidden. - if (value && this.shouldForceHide()) { - return; - } - // Skip if this component is set to hidden and is supposed to be shown. - if (!value && this.shouldForceShow()) { - return; - } - this._visible = value; - this.clearOnHide(); - this.redraw(); - } - } - - /** - * - * @returns {boolean} - */ - get visible() { - // Show only if visibility changes or if we are in builder mode or if hidden fields should be shown. - if (this.builderMode || this.previewMode || this.options.showHiddenFields) { - return true; - } - if (this.shouldForceHide()) { - return false; - } - if (this.shouldForceShow()) { - return true; - } - return this._visible && this._parentVisible; - } - - get currentForm() { - return this._currentForm; - } - - set currentForm(instance) { - this._currentForm = instance; - } - - get fullMode() { - return this.options.attachMode === 'full'; - } - - get builderMode() { - return this.options.attachMode === 'builder'; - } - - get calculatedPath() { - console.error('component.calculatedPath was deprecated, use component.path instead.'); - return this.path; - } - - get labelPosition() { - return this.component.labelPosition; - } - - get labelWidth() { - const width = this.component.labelWidth; - return width >= 0 ? width : 30; - } - - get labelMargin() { - const margin = this.component.labelMargin; - return margin >= 0 ? margin : 3; - } - - get isAdvancedLabel() { - return [ - 'left-left', - 'left-right', - 'right-left', - 'right-right' - ].includes(this.labelPosition); - } - - get labelPositions() { - return this.labelPosition.split('-'); - } - - get skipInEmail() { - return false; - } - - rightDirection(direction) { - if (this.options.condensedMode) { - return false; - } - return direction === 'right'; - } - - getLabelInfo(isCondensed = false) { - const isRightPosition = this.rightDirection(this.labelPositions[0]); - const isLeftPosition = this.labelPositions[0] === 'left' || isCondensed; - const isRightAlign = this.rightDirection(this.labelPositions[1]); - - let contentMargin = ''; - if (this.component.hideLabel) { - const margin = isCondensed ? 0 : this.labelWidth + this.labelMargin; - contentMargin = isRightPosition ? `margin-right: ${margin}%` : ''; - contentMargin = isLeftPosition ? `margin-left: ${margin}%` : ''; - } - - const labelStyles = ` - flex: ${this.labelWidth}; - ${isRightPosition ? 'margin-left' : 'margin-right'}: ${this.labelMargin}%; - `; - const contentStyles = ` - flex: ${100 - this.labelWidth - this.labelMargin}; - ${contentMargin}; - ${this.component.hideLabel ? `max-width: ${100 - this.labelWidth - this.labelMargin}` : ''}; - `; + /* eslint-disable max-statements */ + calculateComponentValue(data, flags, row) { + // Skip value calculation for the component if we don't have entire form data set or in builder mode + if (this.builderMode || _.isUndefined(_.get(this, 'root.data'))) { + return false; + } + // If no calculated value or + // hidden and set to clearOnHide (Don't calculate a value for a hidden field set to clear when hidden) + const { clearOnHide } = this.component; + const shouldBeCleared = !this.visible && clearOnHide; + const allowOverride = _.get( + this.component, + 'allowCalculateOverride', + false, + ); + + if (shouldBeCleared) { + // remove calculated value so that the value is recalculated once component becomes visible + if ( + Object.prototype.hasOwnProperty.call(this, 'calculatedValue') && + allowOverride + ) { + _.unset(this, 'calculatedValue'); + } + return false; + } - return { - isRightPosition, - isRightAlign, - labelStyles, - contentStyles - }; - } - - /** - * Returns only the schema that is different from the default. - * - * @param schema - * @param defaultSchema - */ - getModifiedSchema(schema, defaultSchema, recursion) { - const modified = {}; - if (!defaultSchema) { - return schema; - } - _.each(schema, (val, key) => { - if (!_.isArray(val) && _.isObject(val) && Object.prototype.hasOwnProperty.call(defaultSchema, key)) { - const subModified = this.getModifiedSchema(val, defaultSchema[key], true); - if (!_.isEmpty(subModified)) { - modified[key] = subModified; - } - } - else if (_.isArray(val)) { - if (val.length !== 0 && !_.isEqual(val, defaultSchema[key])) { - modified[key] = val; - } - } - else if ( - (!recursion && (key === 'type')) || - (!recursion && (key === 'key')) || - (!recursion && (key === 'label')) || - (!recursion && (key === 'input')) || - (!recursion && (key === 'tableView')) || - (val !== '' && !Object.prototype.hasOwnProperty.call(defaultSchema, key)) || - (val !== '' && val !== defaultSchema[key]) || - (defaultSchema[key] && val !== defaultSchema[key]) - ) { - modified[key] = val; - } - }); - return modified; - } - - /** - * Returns the JSON schema for this component. - */ - get schema() { - return fastCloneDeep(this.getModifiedSchema(_.omit(this.component, 'id'), this.defaultSchema)); - } - - /** - * Returns true if component is inside DataGrid - */ - get isInDataGrid() { - return this.inDataGrid; - } - - /** - * Translate a text using the i18n system. - * - * @param {string} text - The i18n identifier. - * @param {Object} params - The i18n parameters to use for translation. - */ - t(text, params = {}, ...args) { - if (!text) { - return ''; - } - // Use _userInput: true to ignore translations from defaults - if (text in enTranslation && params._userInput) { - return text; - } - params.data = this.rootValue; - params.row = this.data; - params.component = this.component; - return super.t(text, params, ...args); - } - - labelIsHidden() { - return !this.component.label || - ((!this.isInDataGrid && this.component.hideLabel) || - (this.isInDataGrid && !this.component.dataGridLabel) || - this.options.floatingLabels || - this.options.inputsOnly) && !this.builderMode; - } - - transform(type, value) { - const frameworkTemplates = this.options.template ? Templates.templates[this.options.template] : Templates.current; - return Object.prototype.hasOwnProperty.call(frameworkTemplates, 'transform') - ? frameworkTemplates.transform(type, value, this) - : (type, value) => value; - } - - getTemplate(names, modes) { - modes = Array.isArray(modes) ? modes : [modes]; - names = Array.isArray(names) ? names : [names]; - if (!modes.includes('form')) { - modes.push('form'); - } - - let result = null; - - if (this.options.templates) { - result = this.checkTemplate(this.options.templates, names, modes); - if (result) { - return result; - } - } - - const frameworkTemplates = this.options.template ? Templates.templates[this.options.template] : Templates.current; - result = this.checkTemplate(frameworkTemplates, names, modes); - if (result) { - return result; - } - - // Default back to bootstrap if not defined. - const name = names[names.length - 1]; - const templatesByName = Templates.defaultTemplates[name]; - - if (!templatesByName) { - return `Unknown template: ${name}`; - } - - const templateByMode = this.checkTemplateMode(templatesByName, modes); - if (templateByMode) { - return templateByMode; - } - - return templatesByName.form; - } - - checkTemplate(templates, names, modes) { - for (const name of names) { - const templatesByName = templates[name]; - - if (templatesByName) { - const templateByMode = this.checkTemplateMode(templatesByName, modes); - if (templateByMode) { - return templateByMode; - } - } - } - - return null; - } - - checkTemplateMode(templatesByName, modes) { - for (const mode of modes) { - const templateByMode = templatesByName[mode]; - - if (templateByMode) { - return templateByMode; - } - } - - return null; - } - - getFormattedAttribute(attr) { - return attr ? this.t(attr, { _userInput: true }).replace(/"/g, '"') : ''; - } - - getFormattedTooltip(tooltipValue) { - const tooltip = this.interpolate(tooltipValue || '').replace(/(?:\r\n|\r|\n)/g, '
'); - return this.getFormattedAttribute(tooltip); - } - - isHtmlRenderMode() { - return this.options.renderMode === 'html'; - } - - renderTemplate(name, data = {}, modeOption) { - // Need to make this fall back to form if renderMode is not found similar to how we search templates. - const mode = modeOption || this.options.renderMode || 'form'; - data.component = this.component; - data.self = this; - data.options = this.options; - data.readOnly = this.options.readOnly; - data.iconClass = this.iconClass.bind(this); - data.size = this.size.bind(this); - data.t = this.t.bind(this); - data.transform = this.transform.bind(this); - data.id = data.id || this.id; - data.key = data.key || this.key; - data.value = data.value || this.dataValue; - data.disabled = this.disabled; - data.builder = this.builderMode; - data.render = (...args) => { - console.warn(`Form.io 'render' template function is deprecated. - If you need to render template (template A) inside of another template (template B), - pass pre-compiled template A (use this.renderTemplate('template_A_name') as template context variable for template B`); - return this.renderTemplate(...args); - }; - data.label = data.labelInfo || this.labelInfo; - data.tooltip = this.getFormattedTooltip(this.component.tooltip); - - // Allow more specific template names - const names = [ - `${name}-${this.component.type}-${this.key}`, - `${name}-${this.component.type}`, - `${name}-${this.key}`, - `${name}`, - ]; - - // Allow template alters. - return this.hook( - `render${name.charAt(0).toUpperCase() + name.substring(1, name.length)}`, - this.interpolate(this.getTemplate(names, mode), data), - data, - mode - ); - } - - /** - * Sanitize an html string. - * - * @param string - * @returns {*} - */ - sanitize(dirty, forceSanitize, options) { - if (!this.shouldSanitizeValue && !forceSanitize) { - return dirty; - } - return FormioUtils.sanitize( - dirty, - { - sanitizeConfig: _.merge(this.options?.sanitizeConfig || {}, options || {}), - }); - } - - /** - * Render a template string into html. - * - * @param template - * @param data - * @param actions - * - * @return {HTMLElement|String} - The created element or an empty string if template is not specified. - */ - renderString(template, data) { - if (!template) { - return ''; - } - // Interpolate the template and populate - return this.interpolate(template, data); - } - - performInputMapping(input) { - return input; - } - - get widget() { - const settings = this.component.widget; - - if (settings && this.root?.shadowRoot) { - settings.shadowRoot = this.root.shadowRoot; - } - - const widget = settings && Widgets[settings.type] ? new Widgets[settings.type](settings, this.component, this): null; - return widget; - } - - getBrowserLanguage() { - const nav = window.navigator; - const browserLanguagePropertyKeys = ['language', 'browserLanguage', 'systemLanguage', 'userLanguage']; - let language; - - // support for HTML 5.1 "navigator.languages" - if (Array.isArray(nav.languages)) { - for (let i = 0; i < nav.languages.length; i++) { - language = nav.languages[i]; - if (language && language.length) { - return language.split(';')[0]; - } - } - } - - // support for other well known properties in browsers - for (let i = 0; i < browserLanguagePropertyKeys.length; i++) { - language = nav[browserLanguagePropertyKeys[i]]; - if (language && language.length) { - return language.split(';')[0]; - } - } - - return null; - } - - /** - * Called before a next and previous page is triggered allowing the components - * to perform special functions. - * - * @return {*} - */ - beforePage() { - return Promise.resolve(true); - } - - beforeNext() { - return this.beforePage(true); - } - - /** - * Called before a submission is triggered allowing the components - * to perform special async functions. - * - * @return {*} - */ - beforeSubmit() { - return Promise.resolve(true); - } - - /** - * Return the submission timezone. - * - * @return {*} - */ - get submissionTimezone() { - this.options.submissionTimezone = this.options.submissionTimezone || _.get(this.root, 'options.submissionTimezone'); - return this.options.submissionTimezone; - } - - get timezone() { - return this.getTimezone(this.component); - } - - getTimezone(settings) { - if (settings.timezone) { - return settings.timezone; - } - if (settings.displayInTimezone === 'utc') { - return 'UTC'; - } - const submissionTimezone = this.submissionTimezone; - if ( - submissionTimezone && - ( - (settings.displayInTimezone === 'submission') || - ((this.options.pdf || this.options.server) && (settings.displayInTimezone === 'viewer')) - ) - ) { - return submissionTimezone; - } - - // Return current timezone if none are provided. - return currentTimezone(); - } - - loadRefs(element, refs) { - for (const ref in refs) { - const refType = refs[ref]; - const isString = typeof refType === 'string'; - - const selector = isString && refType.includes('scope') ? `:scope > [ref="${ref}"]` : `[ref="${ref}"]`; - - if (isString && refType.startsWith('single')) { - this.refs[ref] = element.querySelector(selector); - } - else { - this.refs[ref] = element.querySelectorAll(selector); - } - } - } - - setOpenModalElement(template) { - this.componentModal.setOpenModalElement(template || this.getModalPreviewTemplate()); - } - - getModalPreviewTemplate() { - const dataValue = this.component.type === 'password' ? this.dataValue.replace(/./g, '•') : this.dataValue; - const message = this.error ? { - level: 'error', - message: this.error.message, - } : ''; - - let modalLabel; - - if (this.hasInput && this.component.validate?.required && !this.isPDFReadOnlyMode) { - modalLabel = { className: 'field-required' }; - } - - return this.renderTemplate('modalPreview', { - previewText: this.getValueAsString(dataValue, { modalPreview: true }) || this.t('Click to set value'), - messages: message && this.renderTemplate('message', message), - labelInfo: modalLabel, - }); - } - - build(element) { - element = element || this.element; - this.empty(element); - this.setContent(element, this.render()); - return this.attach(element); - } - - get hasModalSaveButton() { - return true; - } - - render(children = `Unknown component: ${this.component.type}`, topLevel = false) { - const isVisible = this.visible; - this.rendered = true; - - if (!this.builderMode && !this.previewMode && this.component.modalEdit) { - return ComponentModal.render(this, { - visible: isVisible, - showSaveButton: this.hasModalSaveButton, - id: this.id, - classes: this.className, - styles: this.customStyle, - children - }, topLevel); - } - else { - return this.renderTemplate('component', { - visible: isVisible, - id: this.id, - classes: this.className, - styles: this.customStyle, - children - }, topLevel); - } - } - - attachTooltips(toolTipsRefs) { - toolTipsRefs?.forEach((tooltip, index) => { - if (tooltip) { - const tooltipAttribute = tooltip.getAttribute('data-tooltip'); - const tooltipDataTitle = tooltip.getAttribute('data-title'); - const tooltipText = this.interpolate(tooltipDataTitle || tooltipAttribute) - .replace(/(?:\r\n|\r|\n)/g, '
'); - - this.tooltips[index] = tippy(tooltip, { - allowHTML: true, - trigger: 'mouseenter click focus', - placement: 'right', - zIndex: 10000, - interactive: true, - content: this.t(this.sanitize(tooltipText), { _userInput: true }), - }); - } - }); - } + // Handle all cases when calculated values should not fire. + if ( + (this.options.readOnly && + !this.options.pdf && + !this.component.calculateValue) || + !( + this.component.calculateValue || + this.component.calculateValueVariable + ) || + (this.options.server && !this.component.calculateServer) || + (flags.dataSourceInitialLoading && allowOverride) + ) { + return false; + } - createComponentModal(element, modalShouldBeOpened, currentValue) { - return new ComponentModal(this, element, modalShouldBeOpened, currentValue); - } + const dataValue = this.dataValue; + // Calculate the new value. + let calculatedValue = this.doValueCalculation( + dataValue, + data, + row, + flags, + ); - attach(element) { - if (!this.builderMode && !this.previewMode && this.component.modalEdit) { - const modalShouldBeOpened = this.componentModal ? this.componentModal.isOpened : false; - const currentValue = modalShouldBeOpened ? this.componentModal.currentValue : this.dataValue; - const openModalTemplate = this.componentModal && modalShouldBeOpened - ? this.componentModal.openModalTemplate - : null; - this.componentModal = this.createComponentModal(element, modalShouldBeOpened, currentValue); - this.setOpenModalElement(openModalTemplate); - } + if (this.options.readOnly && dataValue && !calculatedValue) { + return false; + } - this.attached = true; - this.setElement(element); - element.component = this; + if (_.isNil(calculatedValue)) { + calculatedValue = this.emptyValue; + } - // If this already has an id, get it from the dom. If SSR, it could be different from the initiated id. - if (this.element.id) { - this.id = this.element.id; - this.component.id = this.id; - } + const changed = !_.isEqual(dataValue, calculatedValue); + + // Do not override calculations on server if they have calculateServer set. + if (allowOverride) { + // The value is considered locked if it is not empty and comes from a submission value. + const fromSubmission = + flags.fromSubmission && this.component.persistent === true; + if (this.isEmpty(dataValue)) { + // Reset the calculation lock if ever the data is cleared. + this.calculationLocked = false; + } else if (this.calculationLocked || fromSubmission) { + this.calculationLocked = true; + return false; + } - this.loadRefs(element, { - messageContainer: 'single', - tooltip: 'multiple' - }); + const firstPass = + this.calculatedValue === undefined || flags.resetValue; + if (firstPass) { + this.calculatedValue = null; + } + const newCalculatedValue = this.normalizeValue( + this.convertNumberOrBoolToString(calculatedValue), + ); + const previousCalculatedValue = this.normalizeValue( + this.convertNumberOrBoolToString(this.calculatedValue), + ); + const normalizedDataValue = this.normalizeValue( + this.convertNumberOrBoolToString(dataValue), + ); + const calculationChanged = !_.isEqual( + previousCalculatedValue, + newCalculatedValue, + ); + const previousChanged = !_.isEqual( + normalizedDataValue, + previousCalculatedValue, + ); + + if (calculationChanged && previousChanged && !firstPass) { + return false; + } - this.attachTooltips(this.refs.tooltip); + // Check to ensure that the calculated value is different than the previously calculated value. + if ( + previousCalculatedValue && + previousChanged && + !calculationChanged + ) { + this.calculatedValue = null; + return false; + } - // Attach logic. - this.attachLogic(); - this.autofocus(); + if (flags.isReordered || !calculationChanged) { + return false; + } - // Allow global attach. - this.hook('attachComponent', element, this); - // Allow attach per component type. - const type = this.component.type; - if (type) { - this.hook(`attach${type.charAt(0).toUpperCase() + type.substring(1, type.length)}`, element, this); - } + if (fromSubmission) { + // If we set value from submission and it differs from calculated one, set the calculated value to prevent overriding dataValue in the next pass + this.calculatedValue = fastCloneDeep(calculatedValue); + return false; + } - this.restoreFocus(); + // If this is the firstPass, and the dataValue is different than to the calculatedValue. + if ( + firstPass && + !this.isEmpty(dataValue) && + changed && + calculationChanged + ) { + // Return that we have a change so it will perform another pass. + return true; + } + } - this.addons.forEach((addon) => addon.attach(element)); + this.calculatedValue = fastCloneDeep(calculatedValue); - return Promise.resolve(); - } + if (changed) { + if (!flags.noPristineChangeOnModified) { + this.pristine = false; + } - restoreFocus() { - const isFocused = this.root?.focusedComponent?.path === this.path; - if (isFocused) { - this.loadRefs(this.element, { input: 'multiple' }); - this.focus(this.root.currentSelection?.index); - this.restoreCaretPosition(); + flags.triggeredComponentId = this.id; + return this.setValue(calculatedValue, flags); + } + return false; } - } + /* eslint-enable max-statements */ - addShortcut(element, shortcut) { - // Avoid infinite recursion. - if (!element || !this.root || (this.root === this)) { - return; + /** + * Performs calculations in this component plus any child components. + * + * @param args + * @return {boolean} + */ + calculateValue(data, flags, row) { + data = data || this.rootValue; + flags = flags || {}; + row = row || this.data; + return this.calculateComponentValue(data, flags, row); } - if (!shortcut) { - shortcut = this.component.shortcut; + /** + * Get this component's label text. + * + */ + get label() { + return this.component.label; } - this.root.addShortcut(element, shortcut); - } - - removeShortcut(element, shortcut) { - // Avoid infinite recursion. - if (!element || (this.root === this)) { - return; + /** + * Set this component's label text and render it. + * + * @param value - The new label text. + */ + set label(value) { + this.component.label = value; + if (this.labelElement) { + this.labelElement.innerText = value; + } } - if (!shortcut) { - shortcut = this.component.shortcut; + /** + * Get FormioForm element at the root of this component tree. + * + */ + getRoot() { + return this.root; } - this.root.removeShortcut(element, shortcut); - } - - /** - * Remove all event handlers. - */ - detach() { - // First iterate through each ref and delete the component so there are no dangling component references. - _.each(this.refs, (ref) => { - if (typeof ref === NodeList) { - ref.forEach((elem) => { - delete elem.component; - }); - } - else if (ref) { - delete ref.component; - } - }); - this.refs = {}; - this.removeEventListeners(); - this.detachLogic(); - if (this.tooltip) { - this.tooltip.destroy(); - } - } - - checkRefresh(refreshData, changed, flags) { - const changePath = _.get(changed, 'instance.path', false); - // Don't let components change themselves. - if (changePath && this.path === changePath) { - return; - } - if (refreshData === 'data') { - this.refresh(this.data, changed, flags); - } - else if ( - (changePath && getComponentPath(changed.instance) === refreshData) && changed && changed.instance && - // Make sure the changed component is not in a different "context". Solves issues where refreshOn being set - // in fields inside EditGrids could alter their state from other rows (which is bad). - this.inContext(changed.instance) - ) { - this.refresh(changed.value, changed, flags); - } - } - - checkRefreshOn(changes, flags = {}) { - changes = changes || []; - if (flags.noRefresh) { - return; - } - if (!changes.length && flags.changed) { - changes = [flags.changed]; - } - const refreshOn = flags.fromBlur ? this.component.refreshOnBlur : this.component.refreshOn || this.component.redrawOn; - // If they wish to refresh on a value, then add that here. - if (refreshOn) { - if (Array.isArray(refreshOn)) { - refreshOn.forEach(refreshData => changes.forEach(changed => this.checkRefresh(refreshData, changed, flags))); - } - else { - changes.forEach(changed => this.checkRefresh(refreshOn, changed, flags)); - } - } - } - - /** - * Refreshes the component with a new value. - * - * @param value - */ - refresh(value) { - if (Object.prototype.hasOwnProperty.call(this, 'refreshOnValue')) { - this.refreshOnChanged = !_.isEqual(value, this.refreshOnValue); - } - else { - this.refreshOnChanged = true; - } - this.refreshOnValue = fastCloneDeep(value); - if (this.refreshOnChanged) { - if (this.component.clearOnRefresh) { - this.setValue(null); - } - this.triggerRedraw(); - } - } - - /** - * Checks to see if a separate component is in the "context" of this component. This is determined by first checking - * if they share the same "data" object. It will then walk up the parent tree and compare its parents data objects - * with the components data and returns true if they are in the same context. - * - * Different rows of the same EditGrid, for example, are in different contexts. - * - * @param component - */ - inContext(component) { - if (component.data === this.data) { - return true; - } - let parent = this.parent; - while (parent) { - if (parent.data === component.data) { - return true; - } - parent = parent.parent; - } + /** + * Returns the invalid message, or empty string if the component is valid. + * + * @param data + * @param dirty + * @return {*} + */ + invalidMessage(data, dirty, ignoreCondition, row) { + if (!ignoreCondition && !this.checkCondition(row, data)) { + return ''; + } - return false; - } + // See if this is forced invalid. + if (this.invalid) { + return this.invalid; + } - get viewOnly() { - return this.options.readOnly && this.options.viewAsHtml; - } + // No need to check for errors if there is no input or if it is pristine. + if (!this.hasInput || (!dirty && this.pristine)) { + return ''; + } - setElement(element) { - if (this.element) { - delete this.element.component; - delete this.element; + return _.map(Validator.checkComponent(this, data), 'message').join( + '\n\n', + ); } - this.element = element; - } - createViewOnlyElement() { - this.setElement(this.ce('dl', { - id: this.id - })); + /** + * Returns if the component is valid or not. + * + * @param data + * @param dirty + * @return {boolean} + */ + isValid(data, dirty) { + return !this.invalidMessage(data, dirty); + } + + setComponentValidity(messages, dirty, silentCheck) { + const hasErrors = !!messages.filter( + (message) => message.level === 'error' && !message.fromServer, + ).length; + if ( + messages.length && + (!silentCheck || this.error) && + (!this.isEmpty(this.defaultValue) || dirty || !this.pristine) + ) { + this.setCustomValidity(messages, dirty); + } else if (!silentCheck) { + this.setCustomValidity(''); + } - if (this.element) { - // Ensure you can get the component info from the element. - this.element.component = this; + return !hasErrors; } - return this.element; - } + /** + * Checks the validity of this component and sets the error message if it is invalid. + * + * @param data + * @param dirty + * @param row + * @return {boolean} + */ + checkComponentValidity(data, dirty, row, options = {}) { + data = data || this.rootValue; + row = row || this.data; + const { async = false, silentCheck = false } = options; + + if (this.shouldSkipValidation(data, dirty, row)) { + this.setCustomValidity(''); + return async ? Promise.resolve(true) : true; + } - get defaultViewOnlyValue() { - return '-'; - } + const check = Validator.checkComponent(this, data, row, true, async); + let validations = check; - /** - * Uses the widget to determine the output string. - * - * @param value - * @return {*} - */ - getWidgetValueAsString(value, options) { - const noInputWidget = !this.refs.input || !this.refs.input[0] || !this.refs.input[0].widget; - if (!value || noInputWidget) { - if (!this.widget || !value) { - return value; - } - else { - return this.widget.getValueAsString(value); - } - } - if (Array.isArray(value)) { - const values = []; - value.forEach((val, index) => { - const widget = this.refs.input[index] && this.refs.input[index].widget; - if (widget) { - values.push(widget.getValueAsString(val, options)); - } - }); - return values; - } - - const widget = this.refs.input[0].widget; - return widget.getValueAsString(value, options); - } - - getValueAsString(value, options) { - if (!value) { - return ''; - } - value = this.getWidgetValueAsString(value, options); - if (Array.isArray(value)) { - return value.join(', '); - } - if (_.isPlainObject(value)) { - return JSON.stringify(value); - } - if (value === null || value === undefined) { - return ''; - } - const stringValue = value.toString(); - return this.sanitize(stringValue); - } - - getView(value, options) { - if (this.component.protected) { - return '--- PROTECTED ---'; - } - return this.getValueAsString(value, options); - } - - updateItems(...args) { - this.restoreValue(); - this.onChange(...args); - } - - /** - * @param {*} data - * @param {boolean} [forceUseValue=false] - if true, return 'value' property of the data - * @return {*} - */ - itemValue(data, forceUseValue = false) { - if (_.isObject(data) && !_.isArray(data)) { - if (this.valueProperty) { - return _.get(data, this.valueProperty); - } - - if (forceUseValue) { - return data.value; - } - } - - return data; - } - - itemValueForHTMLMode(value) { - if (Array.isArray(value)) { - const values = value.map(item => Array.isArray(item) ? this.itemValueForHTMLMode(item) : this.itemValue(item)); - - return values.join(', '); - } - - return this.itemValue(value); - } - - createModal(element, attr, confirm) { - const dialog = this.ce('div', attr || {}); - this.setContent(dialog, this.renderTemplate('dialog')); - - // Add refs to dialog, not "this". - dialog.refs = {}; - this.loadRefs.call(dialog, dialog, { - dialogOverlay: 'single', - dialogContents: 'single', - dialogClose: 'single', - }); - - dialog.refs.dialogContents.appendChild(element); - document.body.appendChild(dialog); - document.body.classList.add('modal-open'); - - dialog.close = () => { - document.body.classList.remove('modal-open'); - dialog.dispatchEvent(new CustomEvent('close')); - }; - this.addEventListener(dialog, 'close', () => this.removeChildFrom(dialog, document.body)); - - const close = (event) => { - event.preventDefault(); - dialog.close(); - }; - - const handleCloseClick = (e) => { - if (confirm) { - confirm().then(() => close(e)) - .catch(() => {}); - } - else { - close(e); - } - }; - - this.addEventListener(dialog.refs.dialogOverlay, 'click', handleCloseClick); - this.addEventListener(dialog.refs.dialogClose, 'click', handleCloseClick); - - return dialog; - } - - get optimizeRedraw() { - if (this.options.optimizeRedraw && this.element && !this.visible) { - this.addClass(this.element, 'formio-removed'); - return true; - } - return false; - } - - /** - * Retrieves the CSS class name of this component. - * @returns {string} - The class name of this component. - */ - get className() { - let className = this.hasInput ? `${this.transform('class', 'form-group')} has-feedback `: ''; - className += `formio-component formio-component-${this.component.type} `; - // TODO: find proper way to avoid overriding of default type-based component styles - if (this.key && this.key !== 'form') { - className += `formio-component-${this.key} `; - } - if (this.component.multiple) { - className += 'formio-component-multiple '; - } - if (this.component.customClass) { - className += this.component.customClass; - } - if (this.hasInput && this.component.validate && boolValue(this.component.validate.required)) { - className += ' required'; - } - if (this.labelIsHidden()) { - className += ' formio-component-label-hidden'; - } - if (!this.visible) { - className += ' formio-hidden'; - } - return className; - } - - /** - * Build the custom style from the layout values - * @return {string} - The custom style - */ - get customStyle() { - let customCSS = ''; - _.each(this.component.style, (value, key) => { - if (value !== '') { - customCSS += `${key}:${value};`; - } - }); - return customCSS; - } - - static get serverConditionSettings() { - return Component.conditionOperatorsSettings; - } - - get isMobile() { - return isMobile(); - } - - /** - * Returns the outside wrapping element of this component. - * @returns {HTMLElement} - */ - getElement() { - return this.element; - } - - /** - * Create an evaluation context for all script executions and interpolations. - * - * @param additional - * @return {*} - */ - evalContext(additional) { - return super.evalContext(Object.assign({ - component: this.component, - row: this.data, - rowIndex: this.rowIndex, - data: this.rootValue, - iconClass: this.iconClass.bind(this), - // Bind the translate function to the data context of any interpolated string. - // It is useful to translate strings in different scenarions (eg: custom edit grid templates, custom error messages etc.) - // and desirable to be publicly available rather than calling the internal {instance.t} function in the template string. - t: this.t.bind(this), - submission: (this.root ? this.root._submission : { - data: this.rootValue - }), - form: this.root ? this.root._form : {}, - options: this.options, - }, additional)); - } - - /** - * Sets the pristine flag for this component. - * - * @param pristine {boolean} - TRUE to make pristine, FALSE not pristine. - */ - setPristine(pristine) { - this.pristine = pristine; - } - - get isPristine() { - return this.pristine; - } - - setDirty(dirty) { - this.dirty = dirty; - } - - get isDirty() { - return this.dirty; - } - - /** - * Removes a value out of the data array and rebuild the rows. - * @param {number} index - The index of the data element to remove. - */ - removeValue(index) { - this.splice(index); - this.redraw(); - this.restoreValue(); - this.triggerRootChange(); - } - - iconClass(name, spinning) { - const iconset = this.options.iconset || Templates.current.defaultIconset || 'fa'; - return Object.prototype.hasOwnProperty.call(Templates.current, 'iconClass') - ? Templates.current.iconClass(iconset, name, spinning) - : this.options.iconset === 'fa' ? Templates.defaultTemplates.iconClass(iconset, name, spinning) : name; - } - - size(size) { - return Object.prototype.hasOwnProperty.call(Templates.current, 'size') - ? Templates.current.size(size) - : size; - } - - /** - * The readible name for this component. - * @returns {string} - The name of the component. - */ - get name() { - return this.t(this.component.label || this.component.placeholder || this.key, { _userInput: true }); - } - - /** - * Returns the error label for this component. - * @return {*} - */ - get errorLabel() { - return this.t(this.component.errorLabel - || this.component.label - || this.component.placeholder - || this.key); - } - - /** - * Get the error message provided a certain type of error. - * @param type - * @return {*} - */ - errorMessage(type) { - return (this.component.errors && this.component.errors[type]) ? this.component.errors[type] : type; - } - - setContent(element, content, forceSanitize, sanitizeOptions) { - if (element instanceof HTMLElement) { - element.innerHTML = this.sanitize(content, forceSanitize, sanitizeOptions); - return true; - } - return false; - } - - restoreCaretPosition() { - if (this.root?.currentSelection) { - if (this.refs.input?.length) { - const { selection, index } = this.root.currentSelection; - let input = this.refs.input[index]; - const isInputRangeSelectable = (i) => /text|search|password|tel|url/i.test(i?.type || ''); - if (input) { - if (isInputRangeSelectable(input)) { - input.setSelectionRange(...selection); - } - } - else { - input = this.refs.input[this.refs.input.length]; - const lastCharacter = input.value?.length || 0; - if (isInputRangeSelectable(input)) { - input.setSelectionRange(lastCharacter, lastCharacter); - } - } - } - } - } - - redraw() { - // Don't bother if we have not built yet. - if (!this.element || !this.element.parentNode || this.optimizeRedraw) { - // Return a non-resolving promise. - return Promise.resolve(); - } - this.detach(); - this.emit('redraw'); - // Since we are going to replace the element, we need to know it's position so we can find it in the parent's children. - const parent = this.element.parentNode; - const index = Array.prototype.indexOf.call(parent.children, this.element); - this.element.outerHTML = this.sanitize(this.render()); - this.setElement(parent.children[index]); - return this.attach(this.element); - } - - rebuild() { - this.destroy(); - this.init(); - this.visible = this.conditionallyVisible(null, null); - return this.redraw(); - } - - removeEventListeners() { - super.removeEventListeners(); - this.tooltips.forEach(tooltip => tooltip.destroy()); - this.tooltips = []; - } - - hasClass(element, className) { - if (!element) { - return; - } - - return super.hasClass(element, this.transform('class', className)); - } - - addClass(element, className) { - if (!element) { - return; - } - - return super.addClass(element, this.transform('class', className)); - } - - removeClass(element, className) { - if (!element) { - return; - } - - return super.removeClass(element, this.transform('class', className)); - } - - /** - * Determines if this component has a condition defined. - * - * @return {null} - */ - hasCondition() { - if (this._hasCondition !== null) { - return this._hasCondition; - } - - this._hasCondition = FormioUtils.hasCondition(this.component); - return this._hasCondition; - } - - /** - * Check if this component is conditionally visible. - * - * @param data - * @return {boolean} - */ - conditionallyVisible(data, row) { - data = data || this.rootValue; - row = row || this.data; - if (this.builderMode || this.previewMode || !this.hasCondition()) { - return !this.component.hidden; - } - data = data || (this.root ? this.root.data : {}); - return this.checkCondition(row, data); - } - - /** - * Checks the condition of this component. - * - * TODO: Switch row and data parameters to be consistent with other methods. - * - * @param row - The row contextual data. - * @param data - The global data object. - * @return {boolean} - True if the condition applies to this component. - */ - checkCondition(row, data) { - return FormioUtils.checkCondition( - this.component, - row || this.data, - data || this.rootValue, - this.root ? this.root._form : {}, - this - ); - } - - /** - * Check for conditionals and hide/show the element based on those conditions. - */ - checkComponentConditions(data, flags, row) { - data = data || this.rootValue; - flags = flags || {}; - row = row || this.data; - - if (!this.builderMode & !this.previewMode && this.fieldLogic(data, row)) { - this.redraw(); - } - - // Check advanced conditions - const visible = this.conditionallyVisible(data, row); - - if (this.visible !== visible) { - this.visible = visible; - } - - return visible; - } - - /** - * Checks conditions for this component and any sub components. - * @param args - * @return {boolean} - */ - checkConditions(data, flags, row) { - data = data || this.rootValue; - flags = flags || {}; - row = row || this.data; - return this.checkComponentConditions(data, flags, row); - } - - get logic() { - return this.component.logic || []; - } - - /** - * Check all triggers and apply necessary actions. - * - * @param data - */ - fieldLogic(data, row) { - data = data || this.rootValue; - row = row || this.data; - const logics = this.logic; - - // If there aren't logic, don't go further. - if (logics.length === 0) { - return; - } - - const newComponent = fastCloneDeep(this.originalComponent); - - let changed = logics.reduce((changed, logic) => { - const result = FormioUtils.checkTrigger( - newComponent, - logic.trigger, - row, - data, - this.root ? this.root._form : {}, - this, - ); - - return (result ? this.applyActions(newComponent, logic.actions, result, row, data) : false) || changed; - }, false); - - // If component definition changed, replace and mark as changed. - if (!_.isEqual(this.component, newComponent)) { - this.component = newComponent; - changed = true; - const disabled = this.shouldDisabled; - // Change disabled state if it has changed - if (this.disabled !== disabled) { - this.disabled = disabled; - } - } - - return changed; - } - - isIE() { - if (typeof window === 'undefined') { - return false; - } - - const userAgent = window.navigator.userAgent; - - const msie = userAgent.indexOf('MSIE '); - if (msie > 0) { - // IE 10 or older => return version number - return parseInt(userAgent.substring(msie + 5, userAgent.indexOf('.', msie)), 10); - } - - const trident = userAgent.indexOf('Trident/'); - if (trident > 0) { - // IE 11 => return version number - const rv = userAgent.indexOf('rv:'); - return parseInt(userAgent.substring(rv + 3, userAgent.indexOf('.', rv)), 10); - } - - const edge = userAgent.indexOf('Edge/'); - if (edge > 0) { - // IE 12 (aka Edge) => return version number - return parseInt(userAgent.substring(edge + 5, userAgent.indexOf('.', edge)), 10); + if (this.serverErrors?.length) { + validations = check.concat(this.serverErrors); + } + return async + ? validations.then((messages) => + this.setComponentValidity(messages, dirty, silentCheck), + ) + : this.setComponentValidity(validations, dirty, silentCheck); + } + + checkValidity(data, dirty, row, silentCheck) { + data = data || this.rootValue; + row = row || this.data; + const isValid = this.checkComponentValidity(data, dirty, row, { + silentCheck, + }); + this.checkModal(); + return isValid; } - // other browser - return false; - } - - defineActionValue(action, argsObject) { - return this.evaluate( - action.value, - argsObject, - 'value', - ); - } - - applyActions(newComponent, actions, result, row, data) { - data = data || this.rootValue; - row = row || this.data; - - return actions.reduce((changed, action) => { - switch (action.type) { - case 'property': { - FormioUtils.setActionProperty(newComponent, action, result, row, data, this); - - const property = action.property.value; - if (!_.isEqual(_.get(this.component, property), _.get(newComponent, property))) { - changed = true; - } + checkAsyncValidity(data, dirty, row, silentCheck) { + return Promise.resolve( + this.checkComponentValidity(data, dirty, row, { + async: true, + silentCheck, + }), + ); + } - break; - } - case 'value': { - const oldValue = this.getValue(); - const newValue = this.defineActionValue( - action, - { - value: _.clone(oldValue), - data, - row, - component: newComponent, - result, - } - ); + /** + * Check the conditions, calculations, and validity of a single component and triggers an update if + * something changed. + * + * @param data - The root data of the change event. + * @param flags - The flags from this change event. + * + * @return boolean - If component is valid or not. + */ + checkData(data, flags, row) { + data = data || this.rootValue; + flags = flags || {}; + row = row || this.data; - if (!_.isEqual(oldValue, newValue) && !(this.component.clearOnHide && !this.visible)) { - this.setValue(newValue); + // Needs for Nextgen Rules Engine + this.resetCaches(); - if (this.viewOnly) { - this.dataValue = newValue; - } + // Do not trigger refresh if change was triggered on blur event since components with Refresh on Blur have their own listeners + if (!flags.fromBlur) { + this.checkRefreshOn(flags.changes, flags); + } - changed = true; - } + if (flags.noCheck) { + return true; + } + + this.checkComponentConditions(data, flags, row); - break; + if (this.id !== flags.triggeredComponentId) { + this.calculateComponentValue(data, flags, row); } - case 'mergeComponentSchema': { - const schema = this.evaluate( - action.schemaDefinition, - { - value: _.clone(this.getValue()), - data, - row, - component: newComponent, - result, - }, - 'schema', - ); - _.assign(newComponent, schema); + if (flags.noValidate && !flags.validateOnInit && !flags.fromIframe) { + if ( + flags.fromSubmission && + this.rootPristine && + this.pristine && + this.error && + flags.changed + ) { + this.checkComponentValidity( + data, + !!this.options.alwaysDirty, + row, + true, + ); + } + return true; + } - if (!_.isEqual(this.component, newComponent)) { - changed = true; - } + let isDirty = false; - break; + // We need to set dirty if they explicitly set noValidate to false. + if (this.options.alwaysDirty || flags.dirty) { + isDirty = true; } - case 'customAction': { - const oldValue = this.getValue(); - const newValue = this.evaluate(action.customAction, { - value: _.clone(oldValue), - data, - row, - input: oldValue, - component: newComponent, - result, - }, - 'value'); - if (!_.isEqual(oldValue, newValue) && !(this.component.clearOnHide && !this.visible)) { - this.setValue(newValue); + // See if they explicitely set the values with setSubmission. + if (flags.fromSubmission && this.hasValue(data)) { + isDirty = true; + } - if (this.viewOnly) { - this.dataValue = newValue; - } + this.setDirty(isDirty); - changed = true; - } - - break; - } - } - - return changed; - }, false); - } - - // Deprecated - addInputError(message, dirty, elements) { - this.addMessages(message); - this.setErrorClasses(elements, dirty, !!message); - } - - // Deprecated - removeInputError(elements) { - this.setErrorClasses(elements, true, false); - } - - /** - * Add a new input error to this element. - * - * @param message - * @param dirty - */ - addMessages(messages) { - if (!messages) { - return; - } - - // Standardize on array of objects for message. - if (typeof messages === 'string') { - messages = { - messages, - level: 'error', - }; + if (this.component.validateOn === 'blur' && flags.fromSubmission) { + return true; + } + const isValid = this.checkComponentValidity(data, isDirty, row, flags); + this.checkModal(); + return isValid; } - if (!Array.isArray(messages)) { - messages = [messages]; + checkModal(isValid = true, dirty = false) { + if (!this.component.modalEdit || !this.componentModal) { + return; + } + if (dirty && !isValid) { + this.setErrorClasses( + [this.refs.openModal], + dirty, + !isValid, + !!this.errors.length, + this.refs.openModalWrapper, + ); + } else { + this.clearErrorClasses(this.refs.openModalWrapper); + } } - messages = _.uniqBy(messages, message => message.message); + get validationValue() { + return this.dataValue; + } - if (this.refs.messageContainer) { - this.setContent(this.refs.messageContainer, messages.map((message) => { - if (message.message && typeof message.message === 'string') { - message.message = message.message.replaceAll('<', '<').replaceAll('>', '>'); - } - return this.renderTemplate('message', message); - } - ).join('')); + isEmpty(value = this.dataValue) { + const isEmptyArray = + _.isArray(value) && value.length === 1 + ? _.isEqual(value[0], this.emptyValue) + : false; + return ( + value == null || + value.length === 0 || + _.isEqual(value, this.emptyValue) || + isEmptyArray + ); } - } - setErrorClasses(elements, dirty, hasErrors, hasMessages, element = this.element) { - this.clearErrorClasses(); - elements.forEach((element) => { - this.setElementInvalid(this.performInputMapping(element), false); - }); - this.setInputWidgetErrorClasses(elements, hasErrors); - // do not set error classes for hidden components - if (!this.visible) { - return; + isEqual(valueA, valueB = this.dataValue) { + return ( + (this.isEmpty(valueA) && this.isEmpty(valueB)) || + _.isEqual(valueA, valueB) + ); } - if (hasErrors) { - // Add error classes - elements.forEach((input) => { - this.setElementInvalid(this.performInputMapping(input), true); - }); + /** + * Check if a component is eligible for multiple validation + * + * @return {boolean} + */ + validateMultiple() { + return true; + } - if (dirty && this.options.highlightErrors) { - this.addClass(element, this.options.componentErrorClass); - } - else { - this.addClass(element, 'has-error'); - } + get errors() { + return this.error ? [this.error] : []; } - if (hasMessages) { - this.addClass(element, 'has-message'); + + clearErrorClasses(element = this.element) { + this.removeClass(element, this.options.componentErrorClass); + this.removeClass(element, 'alert alert-danger'); + this.removeClass(element, 'has-error'); + this.removeClass(element, 'has-message'); } - } - setElementInvalid(element, invalid) { - if (!element) return; + setInputWidgetErrorClasses(inputRefs, hasErrors) { + if ( + !this.isInputComponent || + !this.component.widget || + !inputRefs?.length + ) { + return; + } - if (invalid) { - this.addClass(element, 'is-invalid'); - } - else { - this.removeClass(element, 'is-invalid'); + inputRefs.forEach((input) => { + if (input?.widget && input.widget.setErrorClasses) { + input.widget.setErrorClasses(hasErrors); + } + }); } - element.setAttribute('aria-invalid', invalid ? 'true' : 'false'); - } - clearOnHide() { - // clearOnHide defaults to true for old forms (without the value set) so only trigger if the value is false. - if ( - // if change happens inside EditGrid's row, it doesn't trigger change on the root level, so rootPristine will be true - (!this.rootPristine || this.options.server || isInsideScopingComponent(this)) && - this.component.clearOnHide !== false && - !this.options.readOnly && - !this.options.showHiddenFields - ) { - if (!this.visible) { - this.deleteValue(); - } - else if (!this.hasValue() && this.shouldAddDefaultValue) { - // If shown, ensure the default is set. - this.setValue(this.defaultValue, { - noUpdateEvent: true - }); - } - } - } - - triggerRootChange(...args) { - if (this.options.onChange) { - this.options.onChange(...args); - } - else if (this.root) { - this.root.triggerChange(...args); - } - } - - onChange(flags, fromRoot) { - flags = flags || {}; - if (flags.modified) { - if (!flags.noPristineChangeOnModified) { - this.pristine = false; - } - this.addClass(this.getElement(), 'formio-modified'); - } - - // If we are supposed to validate on blur, then don't trigger validation yet. - if (this.component.validateOn === 'blur' && !this.errors.length) { - flags.noValidate = true; - } - - if (this.component.onChange) { - this.evaluate(this.component.onChange, { - flags - }); - } - - // Set the changed variable. - const changed = { - instance: this, - component: this.component, - value: this.dataValue, - flags: flags - }; - - // Emit the change. - this.emit('componentChange', changed); - - // Do not propogate the modified flag. - let modified = false; - if (flags.modified) { - modified = true; - delete flags.modified; - } - - // Bubble this change up to the top. - if (!fromRoot) { - this.triggerRootChange(flags, changed, modified); - } - return changed; - } - - get wysiwygDefault() { - return { - quill: { - theme: 'snow', - placeholder: this.t(this.component.placeholder, { _userInput: true }), - modules: { - toolbar: [ - [{ 'size': ['small', false, 'large', 'huge'] }], // custom dropdown - [{ 'header': [1, 2, 3, 4, 5, 6, false] }], - [{ 'font': [] }], - ['bold', 'italic', 'underline', 'strike', { 'script': 'sub' }, { 'script': 'super' }, 'clean'], - [{ 'color': [] }, { 'background': [] }], - [{ 'list': 'ordered' }, { 'list': 'bullet' }, { 'indent': '-1' }, { 'indent': '+1' }, { 'align': [] }], - ['blockquote', 'code-block'], - ['link', 'image', 'video', 'formula', 'source'] - ] - } - }, - ace: { - theme: 'ace/theme/xcode', - maxLines: 12, - minLines: 12, - tabSize: 2, - mode: 'ace/mode/javascript', - placeholder: this.t(this.component.placeholder, { _userInput: true }) - }, - ckeditor: { - image: { - toolbar: [ - 'imageTextAlternative', - '|', - 'imageStyle:full', - 'imageStyle:alignLeft', - 'imageStyle:alignCenter', - 'imageStyle:alignRight' - ], - styles: [ - 'full', - 'alignLeft', - 'alignCenter', - 'alignRight' - ] - }, - extraPlugins: [] - }, - default: {} - }; - } - - addCKE(element, settings, onChange) { - settings = _.isEmpty(settings) ? {} : settings; - settings.base64Upload = this.component.isUploadEnabled ? false : true; - settings.mediaEmbed = { previewsInData: true }; - settings = _.merge(this.wysiwygDefault.ckeditor, _.get(this.options, 'editors.ckeditor.settings', {}), settings); - - if (this.component.isUploadEnabled) { - settings.extraPlugins.push(getFormioUploadAdapterPlugin(this.fileService, this)); - } - - return Formio.requireLibrary( - 'ckeditor', - isIEBrowser ? 'CKEDITOR' : 'ClassicEditor', - _.get(this.options, 'editors.ckeditor.src', - `${Formio.cdn.ckeditor}/ckeditor.js` - ), true) - .then(() => { - if (!element.parentNode) { - return Promise.reject(); - } - if (isIEBrowser) { - const editor = CKEDITOR.replace(element); - editor.on('change', () => onChange(editor.getData())); - return Promise.resolve(editor); - } - else { - return ClassicEditor.create(element, settings).then(editor => { - editor.model.document.on('change', () => onChange(editor.data.get())); - return editor; - }); - } - }); - } - - addQuill(element, settings, onChange) { - settings = _.isEmpty(settings) ? this.wysiwygDefault.quill : settings; - settings = _.merge(this.wysiwygDefault.quill, _.get(this.options, 'editors.quill.settings', {}), settings); - settings = { - ...settings, - modules: { - table: true, - ...settings.modules - } - }; - // Lazy load the quill css. - Formio.requireLibrary(`quill-css-${settings.theme}`, 'Quill', [ - { type: 'styles', src: `${Formio.cdn.quill}/quill.${settings.theme}.css` } - ], true); - - // Lazy load the quill library. - return Formio.requireLibrary('quill', 'Quill', _.get(this.options, 'editors.quill.src', `${Formio.cdn.quill}/quill.min.js`), true) - .then(() => { - return Formio.requireLibrary('quill-table', 'Quill', `${Formio.cdn.baseUrl}/quill/quill-table.js`, true) - .then(() => { - if (!element.parentNode) { - return Promise.reject(); - } - this.quill = new Quill(element, isIEBrowser ? { ...settings, modules: {} } : settings); - - /** This block of code adds the [source] capabilities. See https://codepen.io/anon/pen/ZyEjrQ **/ - const txtArea = document.createElement('textarea'); - txtArea.setAttribute('class', 'quill-source-code'); - this.quill.addContainer('ql-custom').appendChild(txtArea); - const qlSource = element.parentNode.querySelector('.ql-source'); - if (qlSource) { - this.addEventListener(qlSource, 'click', (event) => { - event.preventDefault(); - if (txtArea.style.display === 'inherit') { - this.quill.setContents(this.quill.clipboard.convert({ html: txtArea.value })); + addFocusBlurEvents(element) { + this.addEventListener(element, 'focus', () => { + if (this.root.focusedComponent !== this) { + if (this.root.pendingBlur) { + this.root.pendingBlur(); } - txtArea.style.display = (txtArea.style.display === 'none') ? 'inherit' : 'none'; - }); - } - /** END CODEBLOCK **/ - // Make sure to select cursor when they click on the element. - this.addEventListener(element, 'click', () => this.quill.focus()); + this.root.focusedComponent = this; - // Allows users to skip toolbar items when tabbing though form - const elm = document.querySelectorAll('.ql-formats > button'); - for (let i = 0; i < elm.length; i++) { - elm[i].setAttribute('tabindex', '-1'); + this.emit('focus', this); + } else if ( + this.root.focusedComponent === this && + this.root.pendingBlur + ) { + this.root.pendingBlur.cancel(); + this.root.pendingBlur = null; } - - this.quill.on('text-change', () => { - txtArea.value = this.quill.root.innerHTML; - onChange(txtArea); + }); + this.addEventListener(element, 'blur', () => { + this.root.pendingBlur = FormioUtils.delay(() => { + this.emit('blur', this); + if (this.component.validateOn === 'blur') { + this.root.triggerChange( + { fromBlur: true }, + { + instance: this, + component: this.component, + value: this.dataValue, + flags: { fromBlur: true }, + }, + ); + } + this.root.focusedComponent = null; + this.root.pendingBlur = null; }); - return this.quill; - }); - }); - } - - get shouldSanitizeValue() { - // Sanitize value if sanitizing for thw whole content is turned off - return (this.options?.sanitize !== false); - } - - addAce(element, settings, onChange) { - if (!settings || (settings.theme === 'snow')) { - const mode = settings ? settings.mode : ''; - settings = {}; - if (mode) { - settings.mode = mode; - } - } - settings = _.merge(this.wysiwygDefault.ace, _.get(this.options, 'editors.ace.settings', {}), settings || {}); - return Formio.requireLibrary('ace', 'ace', _.get(this.options, 'editors.ace.src', `${Formio.cdn.ace}/ace.js`), true) - .then((editor) => { - editor = editor.edit(element); - editor.removeAllListeners('change'); - editor.setOptions(settings); - editor.getSession().setMode(settings.mode); - editor.on('change', () => onChange(editor.getValue())); - if (settings.isUseWorkerDisabled) { - editor.session.setUseWorker(false); - } - return editor; - }); - } - - get tree() { - return this.component.tree || false; - } - - /** - * The empty value for this component. - * - * @return {null} - */ - get emptyValue() { - return null; - } - - /** - * Returns if this component has a value set. - * - */ - hasValue(data) { - return !_.isUndefined(_.get(data || this.data, this.key)); - } - - /** - * Get the data value at the root level. - * - * @return {*} - */ - get rootValue() { - return this.root ? this.root.data : this.data; - } - - get rootPristine() { - return _.get(this, 'root.pristine', false); - } - - /** - * Get the static value of this component. - * @return {*} - */ - get dataValue() { - if ( - !this.key || - (!this.visible && this.component.clearOnHide && !this.rootPristine) - ) { - return this.emptyValue; - } - if (!this.hasValue() && this.shouldAddDefaultValue) { - const empty = this.component.multiple ? [] : this.emptyValue; - if (!this.rootPristine) { - this.dataValue = empty; - } - return empty; - } - return _.get(this._data, this.key); - } - - /** - * Sets the static value of this component. - * - * @param value - */ - set dataValue(value) { - if ( - !this.allowData || - !this.key || - (!this.visible && this.component.clearOnHide && !this.rootPristine) - ) { - return; - } - if ((value !== null) && (value !== undefined)) { - value = this.hook('setDataValue', value, this.key, this._data); - } - if ((value === null) || (value === undefined)) { - this.unset(); - return; - } - _.set(this._data, this.key, value); - return; - } - - /** - * Splice a value from the dataValue. - * - * @param index - */ - splice(index, flags = {}) { - if (this.hasValue()) { - const dataValue = this.dataValue || []; - if (_.isArray(dataValue) && Object.prototype.hasOwnProperty.call(dataValue, index)) { - dataValue.splice(index, 1); - this.dataValue = dataValue; - this.triggerChange(flags); - } - } - } - - unset() { - _.unset(this._data, this.key); - } - - /** - * Deletes the value of the component. - */ - deleteValue() { - this.setValue(null, { - noUpdateEvent: true, - noDefault: true - }); - this.unset(); - } - - getCustomDefaultValue(defaultValue) { - if (this.component.customDefaultValue && !this.options.preview) { - defaultValue = this.evaluate( - this.component.customDefaultValue, - { value: '' }, - 'value' - ); - } - return defaultValue; - } - - get shouldAddDefaultValue() { - return !this.options.noDefaults || (this.component.defaultValue && !this.isEmpty(this.component.defaultValue)) || this.component.customDefaultValue; - } - - get defaultValue() { - let defaultValue = this.emptyValue; - if (this.component.defaultValue) { - defaultValue = this.component.defaultValue; - } - - defaultValue = this.getCustomDefaultValue(defaultValue); - - const checkMask = (value) => { - if (typeof value === 'string') { - if (this.component.type !== 'textfield') { - const placeholderChar = this.placeholderChar; - - value = conformToMask(value, this.defaultMask, { placeholderChar }).conformedValue; - if (!FormioUtils.matchInputMask(value, this.defaultMask)) { - value = ''; - } - } - } - else { - value = ''; - } - return value; - }; - - if (this.defaultMask) { - if (Array.isArray(defaultValue)) { - defaultValue = defaultValue.map(checkMask); - } - else { - defaultValue = checkMask(defaultValue); - } - } - - // Clone so that it creates a new instance. - return _.cloneDeep(defaultValue); - } - - /** - * Get the input value of this component. - * - * @return {*} - */ - getValue() { - if (!this.hasInput || this.viewOnly || !this.refs.input || !this.refs.input.length) { - return this.dataValue; - } - const values = []; - for (const i in this.refs.input) { - if (Object.prototype.hasOwnProperty.call(this.refs.input, i)) { - if (!this.component.multiple) { - return this.getValueAt(i); - } - values.push(this.getValueAt(i)); - } - } - if (values.length === 0 && !this.component.multiple) { - return ''; - } - - return values; - } - - /** - * Get the value at a specific index. - * - * @param index - * @returns {*} - */ - getValueAt(index) { - const input = this.performInputMapping(this.refs.input[index]); - return input ? input.value : undefined; - } - - /** - * Set the value of this component. - * - * @param value - * @param flags - * - * @return {boolean} - If the value changed. - */ - setValue(value, flags = {}) { - const changed = this.updateValue(value, flags); - value = this.dataValue; - if (!this.hasInput) { - return changed; - } - const isArray = Array.isArray(value); - const valueInput = this.refs.fileLink || this.refs.input; - if ( - isArray && - Array.isArray(this.defaultValue) && - Object.prototype.hasOwnProperty.call(this.refs, 'input') && - valueInput && - (valueInput.length !== value.length) && - this.visible - ) { - this.redraw(); - } - if (this.isHtmlRenderMode() && flags && flags.fromSubmission && changed) { - this.redraw(); - return changed; - } - for (const i in this.refs.input) { - if (Object.prototype.hasOwnProperty.call(this.refs.input, i)) { - this.setValueAt(i, isArray ? value[i] : value, flags); - } - } - return changed; - } - - /** - * Set the value at a specific index. - * - * @param index - * @param value - */ - setValueAt(index, value, flags = {}) { - if (!flags.noDefault && (value === null || value === undefined) && !this.component.multiple) { - value = this.defaultValue; - } - - const input = this.performInputMapping(this.refs.input[index]); - const valueMaskInput = this.refs.valueMaskInput; - - if (valueMaskInput?.mask && valueMaskInput.mask.textMaskInputElement) { - valueMaskInput.mask.textMaskInputElement.update(value); - } - - if (input.mask && input.mask.textMaskInputElement) { - input.mask.textMaskInputElement.update(value); - } - else if (input.widget && input.widget.setValue) { - input.widget.setValue(value); - } - else { - input.value = value; - } - } - - get hasSetValue() { - return this.hasValue() && !this.isEmpty(this.dataValue); - } - - setDefaultValue() { - if (this.defaultValue && this.shouldAddDefaultValue) { - const defaultValue = (this.component.multiple && !this.dataValue.length) ? [] : this.defaultValue; - this.setValue(defaultValue, { - noUpdateEvent: true - }); - } - } - - /** - * Restore the value of a control. - */ - restoreValue() { - if (this.hasSetValue) { - this.setValue(this.dataValue, { - noUpdateEvent: true - }); - } - else { - this.setDefaultValue(); - } - } - - /** - * Normalize values coming into updateValue. - * - * @param value - * @return {*} - */ - normalizeValue(value) { - if (this.component.multiple && !Array.isArray(value)) { - value = value ? [value] : []; - } - return value; - } - - /** - * Update a value of this component. - * - * @param flags - */ - updateComponentValue(value, flags = {}) { - let newValue = (!flags.resetValue && (value === undefined || value === null)) ? this.getValue() : value; - newValue = this.normalizeValue(newValue, flags); - const oldValue = this.dataValue; - let changed = ((newValue !== undefined) ? this.hasChanged(newValue, oldValue) : false); - if (changed) { - this.dataValue = newValue; - changed = this.dataValue !== oldValue; - this.updateOnChange(flags, changed); - } - if (this.componentModal && flags && flags.fromSubmission) { - this.componentModal.setValue(value); - } - return changed; - } - - /** - * Updates the value of this component plus all sub-components. - * - * @param args - * @return {boolean} - */ - updateValue(...args) { - return this.updateComponentValue(...args); - } - - getIcon(name, content, styles, ref = 'icon') { - return this.renderTemplate('icon', { - className: this.iconClass(name), - ref, - styles, - content - }); - } - - /** - * Resets the value of this component. - */ - resetValue() { - this.unset(); - this.setValue(this.emptyValue, { - noUpdateEvent: true, - noValidate: true, - resetValue: true - }); - } - - /** - * Determine if the value of this component has changed. - * - * @param newValue - * @param oldValue - * @return {boolean} - */ - hasChanged(newValue, oldValue) { - if ( - ((newValue === undefined) || (newValue === null)) && - ((oldValue === undefined) || (oldValue === null) || this.isEmpty(oldValue)) - ) { - return false; - } - // If we do not have a value and are getting set to anything other than undefined or null, then we changed. - if ( - newValue !== undefined && - newValue !== null && - this.allowData && - !this.hasValue() - ) { - return true; - } - return !_.isEqual(newValue, oldValue); - } - - /** - * Update the value on change. - * - * @param flags - */ - updateOnChange(flags = {}, changed = false) { - if (!flags.noUpdateEvent && changed) { - this.triggerChange(flags); - return true; - } - return false; - } - - /** - * Perform a calculated value operation. - * - * @param data - The global data object. - * - * @return {boolean} - If the value changed during calculation. - */ - - convertNumberOrBoolToString(value) { - if (typeof value === 'number' || typeof value === 'boolean' ) { - return value.toString(); - } - return value; - } - - doValueCalculation(dataValue, data, row) { - return this.evaluate(this.component.calculateValue, { - value: dataValue, - data, - row: row || this.data, - submission: this.root?._submission || { - data: this.rootValue - } - }, 'value'); - } - - /* eslint-disable max-statements */ - calculateComponentValue(data, flags, row) { - // Skip value calculation for the component if we don't have entire form data set or in builder mode - if (this.builderMode || _.isUndefined(_.get(this, 'root.data'))) { - return false; - } - // If no calculated value or - // hidden and set to clearOnHide (Don't calculate a value for a hidden field set to clear when hidden) - const { clearOnHide } = this.component; - const shouldBeCleared = !this.visible && clearOnHide; - const allowOverride = _.get(this.component, 'allowCalculateOverride', false); - - if (shouldBeCleared) { - // remove calculated value so that the value is recalculated once component becomes visible - if (Object.prototype.hasOwnProperty.call(this, 'calculatedValue') && allowOverride) { - _.unset(this, 'calculatedValue'); - } - return false; - } - - // Handle all cases when calculated values should not fire. - if ( - (this.options.readOnly && !this.options.pdf && !this.component.calculateValue) || - !(this.component.calculateValue || this.component.calculateValueVariable) || - (this.options.server && !this.component.calculateServer) || - (flags.dataSourceInitialLoading && allowOverride) - ) { - return false; - } - - const dataValue = this.dataValue; - // Calculate the new value. - let calculatedValue = this.doValueCalculation(dataValue, data, row, flags); - - if (this.options.readOnly && dataValue && !calculatedValue) { - return false; + }); } - if (_.isNil(calculatedValue)) { - calculatedValue = this.emptyValue; - } + setCustomValidity(messages, dirty, external) { + const inputRefs = this.isInputComponent ? this.refs.input || [] : null; - const changed = !_.isEqual(dataValue, calculatedValue); + if (typeof messages === 'string' && messages) { + messages = { + level: 'error', + message: messages, + }; + } - // Do not override calculations on server if they have calculateServer set. - if (allowOverride) { - // The value is considered locked if it is not empty and comes from a submission value. - const fromSubmission = (flags.fromSubmission && this.component.persistent === true); - if (this.isEmpty(dataValue)) { - // Reset the calculation lock if ever the data is cleared. - this.calculationLocked = false; - } - else if (this.calculationLocked || fromSubmission) { - this.calculationLocked = true; - return false; - } - - const firstPass = (this.calculatedValue === undefined) || flags.resetValue; - if (firstPass) { - this.calculatedValue = null; - } - const newCalculatedValue = this.normalizeValue(this.convertNumberOrBoolToString(calculatedValue)); - const previousCalculatedValue = this.normalizeValue(this.convertNumberOrBoolToString(this.calculatedValue)); - const normalizedDataValue = this.normalizeValue(this.convertNumberOrBoolToString(dataValue)); - const calculationChanged = !_.isEqual(previousCalculatedValue, newCalculatedValue); - const previousChanged = !_.isEqual(normalizedDataValue, previousCalculatedValue); - - if (calculationChanged && previousChanged && !firstPass) { - return false; - } + if (!Array.isArray(messages)) { + if (messages) { + messages = [messages]; + } else { + messages = []; + } + } - // Check to ensure that the calculated value is different than the previously calculated value. - if (previousCalculatedValue && previousChanged && !calculationChanged) { - this.calculatedValue = null; - return false; - } + const hasErrors = !!messages.filter( + (message) => message.level === 'error', + ).length; - if (flags.isReordered || !calculationChanged) { - return false; - } + let invalidInputRefs = inputRefs; + if (this.component.multiple) { + const inputRefsArray = Array.from(inputRefs); + inputRefsArray.forEach((input) => { + this.setElementInvalid(this.performInputMapping(input), false); + }); + this.setInputWidgetErrorClasses(inputRefsArray, false); - if (fromSubmission) { - // If we set value from submission and it differs from calculated one, set the calculated value to prevent overriding dataValue in the next pass - this.calculatedValue = fastCloneDeep(calculatedValue); - return false; - } + invalidInputRefs = inputRefsArray.filter((ref) => { + return messages.some?.((msg) => { + return msg?.context?.input === ref; + }); + }); + } + if (messages.length) { + if (this.refs.messageContainer) { + this.empty(this.refs.messageContainer); + } + this.error = { + component: this.component, + message: messages[0].message, + messages, + external: !!external, + }; + this.emit('componentError', this.error); + this.addMessages(messages, dirty, invalidInputRefs); + if (invalidInputRefs) { + this.setErrorClasses( + invalidInputRefs, + dirty, + hasErrors, + !!messages.length, + ); + } + } else if ( + !this.error || + (this.error && this.error.external === !!external) + ) { + if (this.refs.messageContainer) { + this.empty(this.refs.messageContainer); + } + if (this.refs.modalMessageContainer) { + this.empty(this.refs.modalMessageContainer); + } + this.error = null; + if (invalidInputRefs) { + this.setErrorClasses( + invalidInputRefs, + dirty, + hasErrors, + !!messages.length, + ); + } + this.clearErrorClasses(); + } - // If this is the firstPass, and the dataValue is different than to the calculatedValue. - if (firstPass && !this.isEmpty(dataValue) && changed && calculationChanged) { - // Return that we have a change so it will perform another pass. - return true; - } - } - - this.calculatedValue = fastCloneDeep(calculatedValue); - - if (changed) { - if (!flags.noPristineChangeOnModified) { - this.pristine = false; - } - - flags.triggeredComponentId = this.id; - return this.setValue(calculatedValue, flags); - } - return false; - } - /* eslint-enable max-statements */ - - /** - * Performs calculations in this component plus any child components. - * - * @param args - * @return {boolean} - */ - calculateValue(data, flags, row) { - data = data || this.rootValue; - flags = flags || {}; - row = row || this.data; - return this.calculateComponentValue(data, flags, row); - } - - /** - * Get this component's label text. - * - */ - get label() { - return this.component.label; - } - - /** - * Set this component's label text and render it. - * - * @param value - The new label text. - */ - set label(value) { - this.component.label = value; - if (this.labelElement) { - this.labelElement.innerText = value; - } - } - - /** - * Get FormioForm element at the root of this component tree. - * - */ - getRoot() { - return this.root; - } - - /** - * Returns the invalid message, or empty string if the component is valid. - * - * @param data - * @param dirty - * @return {*} - */ - invalidMessage(data, dirty, ignoreCondition, row) { - if (!ignoreCondition && !this.checkCondition(row, data)) { - return ''; - } - - // See if this is forced invalid. - if (this.invalid) { - return this.invalid; - } - - // No need to check for errors if there is no input or if it is pristine. - if (!this.hasInput || (!dirty && this.pristine)) { - return ''; - } - - return _.map(Validator.checkComponent(this, data), 'message').join('\n\n'); - } - - /** - * Returns if the component is valid or not. - * - * @param data - * @param dirty - * @return {boolean} - */ - isValid(data, dirty) { - return !this.invalidMessage(data, dirty); - } - - setComponentValidity(messages, dirty, silentCheck) { - const hasErrors = !!messages.filter(message => message.level === 'error' && !message.fromServer).length; - if (messages.length && (!silentCheck || this.error) && (!this.isEmpty(this.defaultValue) || dirty || !this.pristine)) { - this.setCustomValidity(messages, dirty); - } - else if (!silentCheck) { - this.setCustomValidity(''); - } - - return !hasErrors; - } - - /** - * Checks the validity of this component and sets the error message if it is invalid. - * - * @param data - * @param dirty - * @param row - * @return {boolean} - */ - checkComponentValidity(data, dirty, row, options = {}) { - data = data || this.rootValue; - row = row || this.data; - const { async = false, silentCheck = false } = options; - - if (this.shouldSkipValidation(data, dirty, row)) { - this.setCustomValidity(''); - return async ? Promise.resolve(true) : true; - } - - const check = Validator.checkComponent(this, data, row, true, async); - let validations = check; - - if (this.serverErrors?.length) { - validations = check.concat(this.serverErrors); + // if (!this.refs.input) { + // return; + // } + // this.refs.input.forEach(input => { + // input = this.performInputMapping(input); + // if (typeof input.setCustomValidity === 'function') { + // input.setCustomValidity(message, dirty); + // } + // }); } - return async ? - validations.then((messages) => this.setComponentValidity(messages, dirty, silentCheck)) : - this.setComponentValidity(validations, dirty, silentCheck); - } - - checkValidity(data, dirty, row, silentCheck) { - data = data || this.rootValue; - row = row || this.data; - const isValid = this.checkComponentValidity(data, dirty, row, { silentCheck }); - this.checkModal(); - return isValid; - } - - checkAsyncValidity(data, dirty, row, silentCheck) { - return Promise.resolve(this.checkComponentValidity(data, dirty, row, { async: true, silentCheck })); - } - /** - * Check the conditions, calculations, and validity of a single component and triggers an update if - * something changed. - * - * @param data - The root data of the change event. - * @param flags - The flags from this change event. - * - * @return boolean - If component is valid or not. - */ - checkData(data, flags, row) { - data = data || this.rootValue; - flags = flags || {}; - row = row || this.data; + /** + * Determines if the value of this component is hidden from the user as if it is coming from the server, but is + * protected. + * + * @return {boolean|*} + */ + isValueHidden() { + if (this.component.protected && this.root.editing) { + return false; + } + if ( + !this.root || + !Object.prototype.hasOwnProperty.call(this.root, 'editing') + ) { + return false; + } + if (!this.root || !this.root.editing) { + return false; + } + return ( + this.component.protected || + !this.component.persistent || + this.component.persistent === 'client-only' + ); + } - // Needs for Nextgen Rules Engine - this.resetCaches(); + shouldSkipValidation(data, dirty, row) { + const { validateWhenHidden = false } = this.component || {}; + const rules = [ + // Force valid if component is read-only + () => this.options.readOnly, + // Do not check validations if component is not an input component. + () => !this.hasInput, + // Check to see if we are editing and if so, check component persistence. + () => this.isValueHidden(), + // Force valid if component is hidden. + () => !this.visible && !validateWhenHidden, + // Force valid if component is conditionally hidden. + () => !this.checkCondition(row, data) && !validateWhenHidden, + ]; - // Do not trigger refresh if change was triggered on blur event since components with Refresh on Blur have their own listeners - if (!flags.fromBlur) { - this.checkRefreshOn(flags.changes, flags); + return rules.some((pred) => pred()); } - if (flags.noCheck) { - return true; + // Maintain reverse compatibility. + whenReady() { + console.warn( + 'The whenReady() method has been deprecated. Please use the dataReady property instead.', + ); + return this.dataReady; } - this.checkComponentConditions(data, flags, row); - - if (this.id !== flags.triggeredComponentId) { - this.calculateComponentValue(data, flags, row); + get dataReady() { + return Promise.resolve(); } - if (flags.noValidate && !flags.validateOnInit && !flags.fromIframe) { - if (flags.fromSubmission && this.rootPristine && this.pristine && this.error && flags.changed) { - this.checkComponentValidity(data, !!this.options.alwaysDirty, row, true); - } - return true; - } - - let isDirty = false; - - // We need to set dirty if they explicitly set noValidate to false. - if (this.options.alwaysDirty || flags.dirty) { - isDirty = true; + /** + * Prints out the value of this component as a string value. + */ + asString(value) { + value = value || this.getValue(); + return (Array.isArray(value) ? value : [value]) + .map(_.toString) + .join(', '); } - // See if they explicitely set the values with setSubmission. - if (flags.fromSubmission && this.hasValue(data)) { - isDirty = true; + /** + * Return if the component is disabled. + * @return {boolean} + */ + get disabled() { + return this._disabled || this.parentDisabled; } - this.setDirty(isDirty); - - if (this.component.validateOn === 'blur' && flags.fromSubmission) { - return true; - } - const isValid = this.checkComponentValidity(data, isDirty, row, flags); - this.checkModal(); - return isValid; - } - - checkModal(isValid = true, dirty = false) { - if (!this.component.modalEdit || !this.componentModal) { - return; - } - if (dirty && !isValid) { - this.setErrorClasses([this.refs.openModal], dirty, !isValid, !!this.errors.length, this.refs.openModalWrapper); - } - else { - this.clearErrorClasses(this.refs.openModalWrapper); + /** + * Disable this component. + * + * @param {boolean} disabled + */ + set disabled(disabled) { + this._disabled = disabled; } - } - - get validationValue() { - return this.dataValue; - } - - isEmpty(value = this.dataValue) { - const isEmptyArray = (_.isArray(value) && value.length === 1) ? _.isEqual(value[0], this.emptyValue) : false; - return value == null || value.length === 0 || _.isEqual(value, this.emptyValue) || isEmptyArray; - } - isEqual(valueA, valueB = this.dataValue) { - return (this.isEmpty(valueA) && this.isEmpty(valueB)) || _.isEqual(valueA, valueB); - } - - /** - * Check if a component is eligible for multiple validation - * - * @return {boolean} - */ - validateMultiple() { - return true; - } - - get errors() { - return this.error ? [this.error] : []; - } + setDisabled(element, disabled) { + if (!element) { + return; + } + element.disabled = disabled; + if (disabled) { + element.setAttribute('disabled', 'disabled'); + } else { + element.removeAttribute('disabled'); + } + } - clearErrorClasses(element = this.element) { - this.removeClass(element, this.options.componentErrorClass); - this.removeClass(element, 'alert alert-danger'); - this.removeClass(element, 'has-error'); - this.removeClass(element, 'has-message'); - } + setLoading(element, loading) { + if (!element || element.loading === loading) { + return; + } - setInputWidgetErrorClasses(inputRefs, hasErrors) { - if (!this.isInputComponent || !this.component.widget || !inputRefs?.length) { - return; + element.loading = loading; + if (!element.loader && loading) { + element.loader = this.ce('i', { + class: `${this.iconClass('refresh', true)} button-icon-right`, + }); + } + if (element.loader) { + if (loading) { + this.appendTo(element.loader, element); + } else { + this.removeChildFrom(element.loader, element); + } + } } - inputRefs.forEach((input) => { - if (input?.widget && input.widget.setErrorClasses) { - input.widget.setErrorClasses(hasErrors); - } - }); - } + selectOptions(select, tag, options, defaultValue) { + _.each(options, (option) => { + const attrs = { + value: option.value, + }; + if (defaultValue !== undefined && option.value === defaultValue) { + attrs.selected = 'selected'; + } + const optionElement = this.ce('option', attrs); + optionElement.appendChild(this.text(option.label)); + select.appendChild(optionElement); + }); + } - addFocusBlurEvents(element) { - this.addEventListener(element, 'focus', () => { - if (this.root.focusedComponent !== this) { - if (this.root.pendingBlur) { - this.root.pendingBlur(); + setSelectValue(select, value) { + const options = select.querySelectorAll('option'); + _.each(options, (option) => { + if (option.value === value) { + option.setAttribute('selected', 'selected'); + } else { + option.removeAttribute('selected'); + } + }); + if (select.onchange) { + select.onchange(); } + if (select.onselect) { + select.onselect(); + } + } - this.root.focusedComponent = this; + getRelativePath(path) { + const keyPart = `.${this.key}`; + const thisPath = this.isInputComponent + ? this.path + : this.path.slice(0).replace(keyPart, ''); + return path.replace(thisPath, ''); + } - this.emit('focus', this); - } - else if (this.root.focusedComponent === this && this.root.pendingBlur) { - this.root.pendingBlur.cancel(); - this.root.pendingBlur = null; - } - }); - this.addEventListener(element, 'blur', () => { - this.root.pendingBlur = FormioUtils.delay(() => { - this.emit('blur', this); - if (this.component.validateOn === 'blur') { - this.root.triggerChange({ fromBlur: true }, { - instance: this, - component: this.component, - value: this.dataValue, - flags: { fromBlur: true } - }); - } - this.root.focusedComponent = null; - this.root.pendingBlur = null; - }); - }); - } - - setCustomValidity(messages, dirty, external) { - const inputRefs = this.isInputComponent ? this.refs.input || [] : null; - - if (typeof messages === 'string' && messages) { - messages = { - level: 'error', - message: messages, - }; + clear() { + this.detach(); + this.empty(this.getElement()); } - if (!Array.isArray(messages)) { - if (messages) { - messages = [messages]; - } - else { - messages = []; - } + append(element) { + this.appendTo(element, this.element); } - const hasErrors = !!messages.filter(message => message.level === 'error').length; + prepend(element) { + this.prependTo(element, this.element); + } - let invalidInputRefs = inputRefs; - if (this.component.multiple) { - const inputRefsArray = Array.from(inputRefs); - inputRefsArray.forEach((input) => { - this.setElementInvalid(this.performInputMapping(input), false); - }); - this.setInputWidgetErrorClasses(inputRefsArray, false); + removeChild(element) { + this.removeChildFrom(element, this.element); + } - invalidInputRefs = inputRefsArray.filter((ref) => { - return messages.some?.((msg) => { - return msg?.context?.input === ref; + detachLogic() { + this.logic.forEach((logic) => { + if (logic.trigger.type === 'event') { + const event = this.interpolate(logic.trigger.event); + this.off(event); // only applies to callbacks on this component + } }); - }); - } - if (messages.length) { - if (this.refs.messageContainer) { - this.empty(this.refs.messageContainer); - } - this.error = { - component: this.component, - message: messages[0].message, - messages, - external: !!external, - }; - this.emit('componentError', this.error); - this.addMessages(messages, dirty, invalidInputRefs); - if (invalidInputRefs) { - this.setErrorClasses(invalidInputRefs, dirty, hasErrors, !!messages.length); - } - } - else if (!this.error || (this.error && this.error.external === !!external)) { - if (this.refs.messageContainer) { - this.empty(this.refs.messageContainer); - } - if (this.refs.modalMessageContainer) { - this.empty(this.refs.modalMessageContainer); - } - this.error = null; - if (invalidInputRefs) { - this.setErrorClasses(invalidInputRefs, dirty, hasErrors, !!messages.length); - } - this.clearErrorClasses(); - } - - // if (!this.refs.input) { - // return; - // } - // this.refs.input.forEach(input => { - // input = this.performInputMapping(input); - // if (typeof input.setCustomValidity === 'function') { - // input.setCustomValidity(message, dirty); - // } - // }); - } - - /** - * Determines if the value of this component is hidden from the user as if it is coming from the server, but is - * protected. - * - * @return {boolean|*} - */ - isValueHidden() { - if (this.component.protected && this.root.editing) { - return false; - } - if (!this.root || !Object.prototype.hasOwnProperty.call(this.root, 'editing')) { - return false; - } - if (!this.root || !this.root.editing) { - return false; - } - return (this.component.protected || !this.component.persistent || (this.component.persistent === 'client-only')); - } - - shouldSkipValidation(data, dirty, row) { - const { validateWhenHidden = false } = this.component || {}; - const rules = [ - // Force valid if component is read-only - () => this.options.readOnly, - // Do not check validations if component is not an input component. - () => !this.hasInput, - // Check to see if we are editing and if so, check component persistence. - () => this.isValueHidden(), - // Force valid if component is hidden. - () => !this.visible && !validateWhenHidden, - // Force valid if component is conditionally hidden. - () => !this.checkCondition(row, data) && !validateWhenHidden - ]; - - return rules.some(pred => pred()); - } - - // Maintain reverse compatibility. - whenReady() { - console.warn('The whenReady() method has been deprecated. Please use the dataReady property instead.'); - return this.dataReady; - } - - get dataReady() { - return Promise.resolve(); - } - - /** - * Prints out the value of this component as a string value. - */ - asString(value) { - value = value || this.getValue(); - return (Array.isArray(value) ? value : [value]).map(_.toString).join(', '); - } - - /** - * Return if the component is disabled. - * @return {boolean} - */ - get disabled() { - return this._disabled || this.parentDisabled; - } - - /** - * Disable this component. - * - * @param {boolean} disabled - */ - set disabled(disabled) { - this._disabled = disabled; - } - - setDisabled(element, disabled) { - if (!element) { - return; - } - element.disabled = disabled; - if (disabled) { - element.setAttribute('disabled', 'disabled'); - } - else { - element.removeAttribute('disabled'); - } - } - - setLoading(element, loading) { - if (!element || (element.loading === loading)) { - return; - } - - element.loading = loading; - if (!element.loader && loading) { - element.loader = this.ce('i', { - class: `${this.iconClass('refresh', true)} button-icon-right` - }); - } - if (element.loader) { - if (loading) { - this.appendTo(element.loader, element); - } - else { - this.removeChildFrom(element.loader, element); - } - } - } - - selectOptions(select, tag, options, defaultValue) { - _.each(options, (option) => { - const attrs = { - value: option.value - }; - if (defaultValue !== undefined && (option.value === defaultValue)) { - attrs.selected = 'selected'; - } - const optionElement = this.ce('option', attrs); - optionElement.appendChild(this.text(option.label)); - select.appendChild(optionElement); - }); - } - - setSelectValue(select, value) { - const options = select.querySelectorAll('option'); - _.each(options, (option) => { - if (option.value === value) { - option.setAttribute('selected', 'selected'); - } - else { - option.removeAttribute('selected'); - } - }); - if (select.onchange) { - select.onchange(); - } - if (select.onselect) { - select.onselect(); - } - } - - getRelativePath(path) { - const keyPart = `.${this.key}`; - const thisPath = this.isInputComponent ? this.path - : this.path.slice(0).replace(keyPart, ''); - return path.replace(thisPath, ''); - } - - clear() { - this.detach(); - this.empty(this.getElement()); - } - - append(element) { - this.appendTo(element, this.element); - } - - prepend(element) { - this.prependTo(element, this.element); - } - - removeChild(element) { - this.removeChildFrom(element, this.element); - } - - detachLogic() { - this.logic.forEach(logic => { - if (logic.trigger.type === 'event') { - const event = this.interpolate(logic.trigger.event); - this.off(event); // only applies to callbacks on this component - } - }); - } - - attachLogic() { - // Do not attach logic during builder mode. - if (this.builderMode) { - return; - } - this.logic.forEach((logic) => { - if (logic.trigger.type === 'event') { - const event = this.interpolate(logic.trigger.event); - this.on(event, (...args) => { - const newComponent = fastCloneDeep(this.originalComponent); - if (this.applyActions(newComponent, logic.actions, args)) { - // If component definition changed, replace it. - if (!_.isEqual(this.component, newComponent)) { - this.component = newComponent; - const visible = this.conditionallyVisible(null, null); - const disabled = this.shouldDisabled; - - // Change states which won't be recalculated during redrawing - if (this.visible !== visible) { - this.visible = visible; - } - if (this.disabled !== disabled) { - this.disabled = disabled; - } + } - this.redraw(); + attachLogic() { + // Do not attach logic during builder mode. + if (this.builderMode) { + return; + } + this.logic.forEach((logic) => { + if (logic.trigger.type === 'event') { + const event = this.interpolate(logic.trigger.event); + this.on( + event, + (...args) => { + const newComponent = fastCloneDeep( + this.originalComponent, + ); + if ( + this.applyActions(newComponent, logic.actions, args) + ) { + // If component definition changed, replace it. + if (!_.isEqual(this.component, newComponent)) { + this.component = newComponent; + const visible = this.conditionallyVisible( + null, + null, + ); + const disabled = this.shouldDisabled; + + // Change states which won't be recalculated during redrawing + if (this.visible !== visible) { + this.visible = visible; + } + if (this.disabled !== disabled) { + this.disabled = disabled; + } + + this.redraw(); + } + } + }, + true, + ); } - } - }, true); - } - }); - } - - /** - * Get the element information. - */ - elementInfo() { - const attributes = { - name: this.options.name, - type: this.component.inputType || 'text', - class: 'form-control', - lang: this.options.language - }; - - if (this.component.placeholder) { - attributes.placeholder = this.t(this.component.placeholder, { _userInput: true }); + }); } - if (this.component.tabindex) { - attributes.tabindex = this.component.tabindex; - } + /** + * Get the element information. + */ + elementInfo() { + const attributes = { + name: this.options.name, + type: this.component.inputType || 'text', + class: 'form-control', + lang: this.options.language, + }; - if (this.disabled) { - attributes.disabled = 'disabled'; - } + if (this.component.placeholder) { + attributes.placeholder = this.t(this.component.placeholder, { + _userInput: true, + }); + } - _.defaults(attributes, this.component.attributes); + if (this.component.tabindex) { + attributes.tabindex = this.component.tabindex; + } - return { - type: 'input', - component: this.component, - changeEvent: 'change', - attr: attributes - }; - } + if (this.disabled) { + attributes.disabled = 'disabled'; + } - autofocus() { - const hasAutofocus = this.component.autofocus && !this.builderMode && !this.options.preview; + _.defaults(attributes, this.component.attributes); - if (hasAutofocus) { - this.on('render', () => this.focus(), true); + return { + type: 'input', + component: this.component, + changeEvent: 'change', + attr: attributes, + }; } - } - scrollIntoView(element = this.element) { - if (!element) { - return; + autofocus() { + const hasAutofocus = + this.component.autofocus && + !this.builderMode && + !this.options.preview; + + if (hasAutofocus) { + this.on('render', () => this.focus(), true); + } } - const { left, top } = element.getBoundingClientRect(); - window.scrollTo(left + window.scrollX, top + window.scrollY); - } - focus(index) { - if ('beforeFocus' in this.parent) { - this.parent.beforeFocus(this); + scrollIntoView(element = this.element) { + if (!element) { + return; + } + const { left, top } = element.getBoundingClientRect(); + window.scrollTo(left + window.scrollX, top + window.scrollY); } - if (this.refs.input?.length) { - const focusingInput = typeof index === 'number' && this.refs.input[index] - ? this.refs.input[index] - : this.refs.input[this.refs.input.length - 1]; + focus(index) { + if ('beforeFocus' in this.parent) { + this.parent.beforeFocus(this); + } + + if (this.refs.input?.length) { + const focusingInput = + typeof index === 'number' && this.refs.input[index] + ? this.refs.input[index] + : this.refs.input[this.refs.input.length - 1]; - if (this.component.widget?.type === 'calendar') { - const sibling = focusingInput.nextSibling; + if (this.component.widget?.type === 'calendar') { + const sibling = focusingInput.nextSibling; - if (sibling) { - sibling.focus(); + if (sibling) { + sibling.focus(); + } + } else { + focusingInput.focus(); + } } - } - else { - focusingInput.focus(); - } - } - if (this.refs.openModal) { - this.refs.openModal.focus(); - } + if (this.refs.openModal) { + this.refs.openModal.focus(); + } - if (this.parent.refs.openModal) { - this.parent.refs.openModal.focus(); + if (this.parent.refs.openModal) { + this.parent.refs.openModal.focus(); + } } - } - /** - * Get `Formio` instance for working with files - */ - get fileService() { - if (this.options.fileService) { - return this.options.fileService; - } - if (this.options.formio) { - return this.options.formio; - } - if (this.root && this.root.formio) { - return this.root.formio; - } - const formio = new Formio(); - // If a form is loaded, then make sure to set the correct formUrl. - if (this.root && this.root._form && this.root._form._id) { - formio.formUrl = `${formio.projectUrl}/form/${this.root._form._id}`; + /** + * Get `Formio` instance for working with files + */ + get fileService() { + if (this.options.fileService) { + return this.options.fileService; + } + if (this.options.formio) { + return this.options.formio; + } + if (this.root && this.root.formio) { + return this.root.formio; + } + const formio = new Formio(); + // If a form is loaded, then make sure to set the correct formUrl. + if (this.root && this.root._form && this.root._form._id) { + formio.formUrl = `${formio.projectUrl}/form/${this.root._form._id}`; + } + return formio; } - return formio; - } - resetCaches() {} + resetCaches() {} - get previewMode() { - return false; - } + get previewMode() { + return false; + } } Component.externalLibraries = {}; -Component.requireLibrary = function(name, property, src, polling) { - if (!Object.prototype.hasOwnProperty.call(Component.externalLibraries, name)) { - Component.externalLibraries[name] = {}; - Component.externalLibraries[name].ready = new Promise((resolve, reject) => { - Component.externalLibraries[name].resolve = resolve; - Component.externalLibraries[name].reject = reject; - }); - - const callbackName = `${name}Callback`; - - if (!polling && !window[callbackName]) { - window[callbackName] = function() { - this.resolve(); - }.bind(Component.externalLibraries[name]); - } - // See if the plugin already exists. - const plugin = _.get(window, property); - if (plugin) { - Component.externalLibraries[name].resolve(plugin); - } - else { - src = Array.isArray(src) ? src : [src]; - src.forEach((lib) => { - let attrs = {}; - let elementType = ''; - if (typeof lib === 'string') { - lib = { - type: 'script', - src: lib - }; - } - switch (lib.type) { - case 'script': - elementType = 'script'; - attrs = { - src: lib.src, - type: 'text/javascript', - defer: true, - async: true - }; - break; - case 'styles': - elementType = 'link'; - attrs = { - href: lib.src, - rel: 'stylesheet' - }; - break; - } +Component.requireLibrary = function (name, property, src, polling) { + if ( + !Object.prototype.hasOwnProperty.call(Component.externalLibraries, name) + ) { + Component.externalLibraries[name] = {}; + Component.externalLibraries[name].ready = new Promise( + (resolve, reject) => { + Component.externalLibraries[name].resolve = resolve; + Component.externalLibraries[name].reject = reject; + }, + ); - // Add the script to the top page. - const script = document.createElement(elementType); - for (const attr in attrs) { - script.setAttribute(attr, attrs[attr]); - } - document.getElementsByTagName('head')[0].appendChild(script); - }); + const callbackName = `${name}Callback`; - // if no callback is provided, then check periodically for the script. - if (polling) { - setTimeout(function checkLibrary() { - const plugin = _.get(window, property); - if (plugin) { + if (!polling && !window[callbackName]) { + window[callbackName] = function () { + this.resolve(); + }.bind(Component.externalLibraries[name]); + } + // See if the plugin already exists. + const plugin = _.get(window, property); + if (plugin) { Component.externalLibraries[name].resolve(plugin); - } - else { - // check again after 200 ms. - setTimeout(checkLibrary, 200); - } - }, 200); - } - } - } - return Component.externalLibraries[name].ready; -}; + } else { + src = Array.isArray(src) ? src : [src]; + src.forEach((lib) => { + let attrs = {}; + let elementType = ''; + if (typeof lib === 'string') { + lib = { + type: 'script', + src: lib, + }; + } + switch (lib.type) { + case 'script': + elementType = 'script'; + attrs = { + src: lib.src, + type: 'text/javascript', + defer: true, + async: true, + }; + break; + case 'styles': + elementType = 'link'; + attrs = { + href: lib.src, + rel: 'stylesheet', + }; + break; + } + + // Add the script to the top page. + const script = document.createElement(elementType); + for (const attr in attrs) { + script.setAttribute(attr, attrs[attr]); + } + document.getElementsByTagName('head')[0].appendChild(script); + }); -Component.libraryReady = function(name) { - if ( - Object.prototype.hasOwnProperty.call(Component.externalLibraries, name) && - Component.externalLibraries[name].ready - ) { + // if no callback is provided, then check periodically for the script. + if (polling) { + setTimeout(function checkLibrary() { + const plugin = _.get(window, property); + if (plugin) { + Component.externalLibraries[name].resolve(plugin); + } else { + // check again after 200 ms. + setTimeout(checkLibrary, 200); + } + }, 200); + } + } + } return Component.externalLibraries[name].ready; - } +}; + +Component.libraryReady = function (name) { + if ( + Object.prototype.hasOwnProperty.call( + Component.externalLibraries, + name, + ) && + Component.externalLibraries[name].ready + ) { + return Component.externalLibraries[name].ready; + } - return Promise.reject(`${name} library was not required.`); + return Promise.reject(`${name} library was not required.`); }; diff --git a/src/components/_classes/component/Component.unit.js b/src/components/_classes/component/Component.unit.js index 6a629f9458..4200af6f8c 100644 --- a/src/components/_classes/component/Component.unit.js +++ b/src/components/_classes/component/Component.unit.js @@ -11,363 +11,482 @@ import comp3 from './fixtures/comp3'; import comp4 from './fixtures/comp4'; import comp5 from './fixtures/comp5'; -describe('Component', function() { - it('Should create a Component', function(done) { - const component = new Component(); +describe('Component', function () { + it('Should create a Component', function (done) { + const component = new Component(); - // Test that we have a proper constructed component. - assert.equal(component.options.renderMode, 'form'); - assert.equal(component.options.attachMode, 'full'); - assert.equal(component.attached, false); - assert.equal(component.rendered, false); - done(); - }); - - it('Should build a base component', function() { - return Harness.testCreate(Component, { type: 'base' }).then((component) => { - const element = component.element.querySelector('[ref="component"]'); - assert.equal(element.textContent.trim(), 'Unknown component: base'); + // Test that we have a proper constructed component. + assert.equal(component.options.renderMode, 'form'); + assert.equal(component.options.attachMode, 'full'); + assert.equal(component.attached, false); + assert.equal(component.rendered, false); + done(); }); - }); - it('Should provide required validation', function(done) { - Harness.testCreate(Component, _merge({}, comp1, { - validate: { required: true } - })).then((component) => Harness.testComponent(component, { - bad: { - value: '', - field: 'firstName', - error: 'First Name is required' - }, - good: { - value: 'te' - } - }, done)).catch(done); - }); + it('Should build a base component', function () { + return Harness.testCreate(Component, { type: 'base' }).then( + (component) => { + const element = + component.element.querySelector('[ref="component"]'); + assert.equal( + element.textContent.trim(), + 'Unknown component: base', + ); + }, + ); + }); - it('Should provide minLength validation', function(done) { - Harness.testCreate(Component, _merge({}, comp1, { - validate: { minLength: 2 } - })).then((component) => Harness.testComponent(component, { - bad: { - value: 't', - field: 'firstName', - error: 'First Name must have at least 2 characters.' - }, - good: { - value: 'te' - } - }, done)); - }); + it('Should provide required validation', function (done) { + Harness.testCreate( + Component, + _merge({}, comp1, { + validate: { required: true }, + }), + ) + .then((component) => + Harness.testComponent( + component, + { + bad: { + value: '', + field: 'firstName', + error: 'First Name is required', + }, + good: { + value: 'te', + }, + }, + done, + ), + ) + .catch(done); + }); - it('Should provide maxLength validation', function(done) { - Harness.testCreate(Component, _merge({}, comp1, { - validate: { maxLength: 5 } - })).then((component) => Harness.testComponent(component, { - bad: { - value: 'testte', - field: 'firstName', - error: 'First Name must have no more than 5 characters.' - }, - good: { - value: 'te' - } - }, done)); - }); + it('Should provide minLength validation', function (done) { + Harness.testCreate( + Component, + _merge({}, comp1, { + validate: { minLength: 2 }, + }), + ).then((component) => + Harness.testComponent( + component, + { + bad: { + value: 't', + field: 'firstName', + error: 'First Name must have at least 2 characters.', + }, + good: { + value: 'te', + }, + }, + done, + ), + ); + }); - it('Should provide maxWords validation', function(done) { - Harness.testCreate(Component, _merge({}, comp1, { - validate: { maxWords: 2 } - })).then((component) => Harness.testComponent(component, { - bad: { - value: 'test test test', - field: 'firstName', - error: 'First Name must have no more than 2 words.' - }, - good: { - value: 'te st' - } - }, done)); - }); + it('Should provide maxLength validation', function (done) { + Harness.testCreate( + Component, + _merge({}, comp1, { + validate: { maxLength: 5 }, + }), + ).then((component) => + Harness.testComponent( + component, + { + bad: { + value: 'testte', + field: 'firstName', + error: 'First Name must have no more than 5 characters.', + }, + good: { + value: 'te', + }, + }, + done, + ), + ); + }); - it('Should provide minWords validation', function(done) { - Harness.testCreate(Component, _merge({}, comp1, { - validate: { minWords: 2 } - })).then((component) => Harness.testComponent(component, { - bad: { - value: 'test', - field: 'firstName', - error: 'First Name must have at least 2 words.' - }, - good: { - value: 'te st' - } - }, done)); - }); + it('Should provide maxWords validation', function (done) { + Harness.testCreate( + Component, + _merge({}, comp1, { + validate: { maxWords: 2 }, + }), + ).then((component) => + Harness.testComponent( + component, + { + bad: { + value: 'test test test', + field: 'firstName', + error: 'First Name must have no more than 2 words.', + }, + good: { + value: 'te st', + }, + }, + done, + ), + ); + }); - it('Should provide custom validation', function(done) { - Harness.testCreate(Component, _merge({}, comp1, { - validate: { - custom: 'valid = (input !== "Joe") ? true : "You cannot be Joe"' - } - })).then((component) => Harness.testComponent(component, { - bad: { - value: 'Joe', - field: 'firstName', - error: 'You cannot be Joe' - }, - good: { - value: 'Tom' - } - }, done)); - }); + it('Should provide minWords validation', function (done) { + Harness.testCreate( + Component, + _merge({}, comp1, { + validate: { minWords: 2 }, + }), + ).then((component) => + Harness.testComponent( + component, + { + bad: { + value: 'test', + field: 'firstName', + error: 'First Name must have at least 2 words.', + }, + good: { + value: 'te st', + }, + }, + done, + ), + ); + }); - it('Should provide json validation', function(done) { - Harness.testCreate(Component, _merge({}, comp1, { - validate: { - json: { - 'if': [ - { - '===': [ - { var: 'data.firstName' }, - 'Joe' - ] - }, - true, - 'You must be Joe' - ] - } - } - })).then((component) => Harness.testComponent(component, { - bad: { - value: 'Tom', - field: 'firstName', - error: 'You must be Joe' - }, - good: { - value: 'Joe' - } - }, done)); - }); + it('Should provide custom validation', function (done) { + Harness.testCreate( + Component, + _merge({}, comp1, { + validate: { + custom: 'valid = (input !== "Joe") ? true : "You cannot be Joe"', + }, + }), + ).then((component) => + Harness.testComponent( + component, + { + bad: { + value: 'Joe', + field: 'firstName', + error: 'You cannot be Joe', + }, + good: { + value: 'Tom', + }, + }, + done, + ), + ); + }); - it('Should mark as invalid calculated fields that are invalid', function(done) { - const formElement = document.createElement('div'); - const form = new Webform(formElement); - const formJson = { - components: [ - { - label: 'A', - mask: false, - tableView: false, - delimiter: false, - requireDecimal: false, - inputFormat: 'plain', - key: 'a', - type: 'number', - input: true - }, - { - label: 'B', - mask: false, - disabled: true, - tableView: false, - delimiter: false, - requireDecimal: false, - inputFormat: 'plain', - calculateValue: 'value = data.a + 1;', - validate: { - custom: 'valid = input <= 10 ? true : \'B should be less or equal to 10\';' - }, - key: 'b', - type: 'number', - input: true - } - ], - }; + it('Should provide json validation', function (done) { + Harness.testCreate( + Component, + _merge({}, comp1, { + validate: { + json: { + if: [ + { + '===': [{ var: 'data.firstName' }, 'Joe'], + }, + true, + 'You must be Joe', + ], + }, + }, + }), + ).then((component) => + Harness.testComponent( + component, + { + bad: { + value: 'Tom', + field: 'firstName', + error: 'You must be Joe', + }, + good: { + value: 'Joe', + }, + }, + done, + ), + ); + }); - form.setForm(formJson).then(() => { - return form.setSubmission({ - data: { - a: 1 - } - }); - }) - .then(() => { - setTimeout(() => { - const a = form.getComponent('a'); - a.updateComponentValue(10); - setTimeout(()=> { - const b = form.getComponent('b'); - expect(b.refs.messageContainer?.innerHTML.indexOf('B should be less or equal to 10') > -1).to.be.true; - expect(b.refs.input[0].classList.contains('is-invalid')).to.be.true; - done(); - }, 300); - }, 300); - }) - .catch(done); - }); + it('Should mark as invalid calculated fields that are invalid', function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement); + const formJson = { + components: [ + { + label: 'A', + mask: false, + tableView: false, + delimiter: false, + requireDecimal: false, + inputFormat: 'plain', + key: 'a', + type: 'number', + input: true, + }, + { + label: 'B', + mask: false, + disabled: true, + tableView: false, + delimiter: false, + requireDecimal: false, + inputFormat: 'plain', + calculateValue: 'value = data.a + 1;', + validate: { + custom: "valid = input <= 10 ? true : 'B should be less or equal to 10';", + }, + key: 'b', + type: 'number', + input: true, + }, + ], + }; - describe('shouldSkipValidation', function() { - it('should return true if component is hidden', function(done) { - Harness.testCreate(Component, comp1) - .then(cmp => { - cmp.visible = false; - cmp.checkCondition = () => true; - expect(cmp.visible).to.be.false; - expect(cmp.checkCondition()).to.be.true; - expect(cmp.shouldSkipValidation()).to.be.true; - done(); - }, done) - .catch(done); + form.setForm(formJson) + .then(() => { + return form.setSubmission({ + data: { + a: 1, + }, + }); + }) + .then(() => { + setTimeout(() => { + const a = form.getComponent('a'); + a.updateComponentValue(10); + setTimeout(() => { + const b = form.getComponent('b'); + expect( + b.refs.messageContainer?.innerHTML.indexOf( + 'B should be less or equal to 10', + ) > -1, + ).to.be.true; + expect(b.refs.input[0].classList.contains('is-invalid')) + .to.be.true; + done(); + }, 300); + }, 300); + }) + .catch(done); }); - it('should return true if component is conditionally hidden', function(done) { - Harness.testCreate(Component, comp1) - .then(cmp => { - cmp.visible = true; - cmp.checkCondition = () => false; - expect(cmp.visible).to.be.true; - expect(cmp.checkCondition()).to.be.false; - expect(cmp.shouldSkipValidation()).to.be.true; - done(); - }, done) - .catch(done); - }); + describe('shouldSkipValidation', function () { + it('should return true if component is hidden', function (done) { + Harness.testCreate(Component, comp1) + .then((cmp) => { + cmp.visible = false; + cmp.checkCondition = () => true; + expect(cmp.visible).to.be.false; + expect(cmp.checkCondition()).to.be.true; + expect(cmp.shouldSkipValidation()).to.be.true; + done(); + }, done) + .catch(done); + }); - it('should return false if not hidden', function(done) { - Harness.testCreate(Component, comp1) - .then(cmp => { - cmp.visible = true; - cmp.checkCondition = () => true; - expect(cmp.visible).to.be.true; - expect(cmp.checkCondition()).to.be.true; - expect(cmp.shouldSkipValidation()).to.be.false; - done(); - }, done) - .catch(done); - }); - }); + it('should return true if component is conditionally hidden', function (done) { + Harness.testCreate(Component, comp1) + .then((cmp) => { + cmp.visible = true; + cmp.checkCondition = () => false; + expect(cmp.visible).to.be.true; + expect(cmp.checkCondition()).to.be.false; + expect(cmp.shouldSkipValidation()).to.be.true; + done(); + }, done) + .catch(done); + }); - describe('Component Modal', function() { - it('Modal window should stay opened after redrawing component if it was opened ont hte moment of calling', function(done) { - Harness.testCreate(Component, comp3).then((component) => { - component.componentModal.openModal(); - component.redraw().then(() => { - const isVisible = !component.componentModal.refs.modalWrapper.classList.contains('component-rendering-hidden'); - assert(isVisible); - done(); - }).catch(done); - }).catch(done); + it('should return false if not hidden', function (done) { + Harness.testCreate(Component, comp1) + .then((cmp) => { + cmp.visible = true; + cmp.checkCondition = () => true; + expect(cmp.visible).to.be.true; + expect(cmp.checkCondition()).to.be.true; + expect(cmp.shouldSkipValidation()).to.be.false; + done(); + }, done) + .catch(done); + }); }); - }); - it('Should return value for HTML mode', function() { - return Harness.testCreate(Component, comp1).then((component) => { - assert.equal(component.itemValueForHTMLMode(['option 1', 'option 2', 'option 3']), 'option 1, option 2, option 3'); - assert.equal(component.itemValueForHTMLMode(['option 1', ['option 2', 'option 3']]), 'option 1, option 2, option 3'); - assert.equal(component.itemValueForHTMLMode(['2020-03-18T15:00:00.000Z', '2020-03-31T09:05:00.000Z']), '2020-03-18T15:00:00.000Z, 2020-03-31T09:05:00.000Z'); - assert.equal(component.itemValueForHTMLMode('test'), 'test'); + describe('Component Modal', function () { + it('Modal window should stay opened after redrawing component if it was opened ont hte moment of calling', function (done) { + Harness.testCreate(Component, comp3) + .then((component) => { + component.componentModal.openModal(); + component + .redraw() + .then(() => { + const isVisible = + !component.componentModal.refs.modalWrapper.classList.contains( + 'component-rendering-hidden', + ); + assert(isVisible); + done(); + }) + .catch(done); + }) + .catch(done); + }); }); - }); - it('Should protect against change loops', function(done) { - const formElement = document.createElement('div'); - const form = new Webform(formElement); - const formJson = { - components: [ - { - key: 'textField', - label: 'Text Field', - type: 'textfield', - calculateValue: "value = value + '_calculated'", - }, - ], - }; + it('Should return value for HTML mode', function () { + return Harness.testCreate(Component, comp1).then((component) => { + assert.equal( + component.itemValueForHTMLMode([ + 'option 1', + 'option 2', + 'option 3', + ]), + 'option 1, option 2, option 3', + ); + assert.equal( + component.itemValueForHTMLMode([ + 'option 1', + ['option 2', 'option 3'], + ]), + 'option 1, option 2, option 3', + ); + assert.equal( + component.itemValueForHTMLMode([ + '2020-03-18T15:00:00.000Z', + '2020-03-31T09:05:00.000Z', + ]), + '2020-03-18T15:00:00.000Z, 2020-03-31T09:05:00.000Z', + ); + assert.equal(component.itemValueForHTMLMode('test'), 'test'); + }); + }); - form.setForm(formJson).then(() => { - const textField = form.getComponent('textField'); - const spy = sinon.spy(textField, 'calculateComponentValue'); - form.onChange({ textField: 'test' }); + it('Should protect against change loops', function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement); + const formJson = { + components: [ + { + key: 'textField', + label: 'Text Field', + type: 'textfield', + calculateValue: "value = value + '_calculated'", + }, + ], + }; - setTimeout(() => { - expect(spy.calledOnce).to.be.true; + form.setForm(formJson) + .then(() => { + const textField = form.getComponent('textField'); + const spy = sinon.spy(textField, 'calculateComponentValue'); + form.onChange({ textField: 'test' }); - done(); - }, 500); - }) - .catch((err) => done(err)); - }); + setTimeout(() => { + expect(spy.calledOnce).to.be.true; - it('Should mark as invalid only invalid fields in multiple components', function(done) { - const formElement = document.createElement('div'); - const form = new Webform(formElement); - const formJson = { - components: [ - { - label: 'Email', - tableView: true, - multiple: true, - validate: { - required: true - }, - key: 'email', - type: 'email', - input: true - }, - ], - }; + done(); + }, 500); + }) + .catch((err) => done(err)); + }); - form.setForm(formJson).then(() => { - return form.setSubmission({ - data: { - email: [ - 'oleg@form.io', - 'oleg@form', - '', - ] - } - }); - }) - .then(() => { - setTimeout(() => { - const email = form.getComponent('email'); - expect(email.refs.input[0].classList.contains('is-invalid')).to.be.false; - expect(email.refs.input[1].classList.contains('is-invalid')).to.be.true; - expect(email.refs.input[2].classList.contains('is-invalid')).to.be.true; - done(); - }, 300); - }) - .catch(done); - }); + it('Should mark as invalid only invalid fields in multiple components', function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement); + const formJson = { + components: [ + { + label: 'Email', + tableView: true, + multiple: true, + validate: { + required: true, + }, + key: 'email', + type: 'email', + input: true, + }, + ], + }; - it('Should sanitize HTML even if options.pdf is set', function(done) { - const component = new Component({}, { pdf: true }); - assert.equal(component.sanitize(''), ''); - done(); - }); + form.setForm(formJson) + .then(() => { + return form.setSubmission({ + data: { + email: ['oleg@form.io', 'oleg@form', ''], + }, + }); + }) + .then(() => { + setTimeout(() => { + const email = form.getComponent('email'); + expect(email.refs.input[0].classList.contains('is-invalid')) + .to.be.false; + expect(email.refs.input[1].classList.contains('is-invalid')) + .to.be.true; + expect(email.refs.input[2].classList.contains('is-invalid')) + .to.be.true; + done(); + }, 300); + }) + .catch(done); + }); - describe('shouldDisplayRedAsterisk', function() { - it('modalPreview template should have className "field-required" if component is required', function(done) { - Harness.testCreate(Component, _merge({}, comp4, { - validate: { required: true } - })).then(cmp => { - assert.equal(!!cmp.element.querySelector('.field-required'), true); + it('Should sanitize HTML even if options.pdf is set', function (done) { + const component = new Component({}, { pdf: true }); + assert.equal( + component.sanitize( + '', + ), + '', + ); done(); - }, done) - .catch(done); }); - }); - it('Should not execute code inside Tooltips/Description', function(done) { - const formElement = document.createElement('div'); - const form = new Webform(formElement); + describe('shouldDisplayRedAsterisk', function () { + it('modalPreview template should have className "field-required" if component is required', function (done) { + Harness.testCreate( + Component, + _merge({}, comp4, { + validate: { required: true }, + }), + ) + .then((cmp) => { + assert.equal( + !!cmp.element.querySelector('.field-required'), + true, + ); + done(); + }, done) + .catch(done); + }); + }); - form.setForm(comp5).then(() => { - setTimeout(() => { - assert.equal(window._ee, undefined, 'Should not execute code inside Tooltips/Description'); - done(); - }, 200); - }) - .catch(done); - }); + it('Should not execute code inside Tooltips/Description', function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement); + + form.setForm(comp5) + .then(() => { + setTimeout(() => { + assert.equal( + window._ee, + undefined, + 'Should not execute code inside Tooltips/Description', + ); + done(); + }, 200); + }) + .catch(done); + }); }); diff --git a/src/components/_classes/component/editForm/Component.edit.addons.js b/src/components/_classes/component/editForm/Component.edit.addons.js index 7d63552572..23826a14ac 100644 --- a/src/components/_classes/component/editForm/Component.edit.addons.js +++ b/src/components/_classes/component/editForm/Component.edit.addons.js @@ -2,22 +2,22 @@ import { editForms } from '../../../../addons'; import Addons from '../../../../addons'; export default [ - { - type: 'editgrid', - addAnother: 'Add Addon', - saveRow: 'Save Addon', - weight: 28, - input: true, - key: 'addons', - label: 'Addons', - templates: { - // eslint-disable-next-line quotes - header: `
+ { + type: 'editgrid', + addAnother: 'Add Addon', + saveRow: 'Save Addon', + weight: 28, + input: true, + key: 'addons', + label: 'Addons', + templates: { + // eslint-disable-next-line quotes + header: `
{{ t(components[0].label) }}
Settings
`, - // eslint-disable-next-line quotes - row: `
+ // eslint-disable-next-line quotes + row: `
{{ row.name.label }}
@@ -36,35 +36,43 @@ export default [
{% } %}
`, - }, - components: [ - { - label: 'Name', - tableView: true, - key: 'name', - type: 'select', - dataSrc: 'custom', - data: { - custom: function({ instance }) { - const componentType = instance?.root?.data?.type; - const availableAddons = Object.keys(Addons).filter((key) => { - if (Addons[key]?.info?.supportedComponents?.includes(componentType)) { - return true; - } - return false; - }); - return availableAddons.map((addonKey) => ({ - value: addonKey, - label: Addons[addonKey].info.label || addonKey, - })); - }, }, - input: true, - validate: { - required: true, - }, - }, - ...editForms, - ] - } + components: [ + { + label: 'Name', + tableView: true, + key: 'name', + type: 'select', + dataSrc: 'custom', + data: { + custom: function ({ instance }) { + const componentType = instance?.root?.data?.type; + const availableAddons = Object.keys(Addons).filter( + (key) => { + if ( + Addons[ + key + ]?.info?.supportedComponents?.includes( + componentType, + ) + ) { + return true; + } + return false; + }, + ); + return availableAddons.map((addonKey) => ({ + value: addonKey, + label: Addons[addonKey].info.label || addonKey, + })); + }, + }, + input: true, + validate: { + required: true, + }, + }, + ...editForms, + ], + }, ]; diff --git a/src/components/_classes/component/editForm/Component.edit.api.js b/src/components/_classes/component/editForm/Component.edit.api.js index d1cb45afe7..74f5d382f4 100644 --- a/src/components/_classes/component/editForm/Component.edit.api.js +++ b/src/components/_classes/component/editForm/Component.edit.api.js @@ -1,38 +1,40 @@ export default [ - { - weight: 0, - type: 'textfield', - input: true, - key: 'key', - label: 'Property Name', - tooltip: 'The name of this field in the API endpoint.', - validate: { - pattern: '(\\w|\\w[\\w-.]*\\w)', - patternMessage: 'The property name must only contain alphanumeric characters, underscores, dots and dashes and should not be ended by dash or dot.', - required: true - } - }, - { - weight: 100, - type: 'tags', - input: true, - label: 'Field Tags', - storeas: 'array', - tooltip: 'Tag the field for use in custom logic.', - key: 'tags' - }, - { - weight: 200, - type: 'datamap', - label: 'Custom Properties', - tooltip: 'This allows you to configure any custom properties for this component.', - key: 'properties', - valueComponent: { - type: 'textfield', - key: 'value', - label: 'Value', - placeholder: 'Value', - input: true - } - }, + { + weight: 0, + type: 'textfield', + input: true, + key: 'key', + label: 'Property Name', + tooltip: 'The name of this field in the API endpoint.', + validate: { + pattern: '(\\w|\\w[\\w-.]*\\w)', + patternMessage: + 'The property name must only contain alphanumeric characters, underscores, dots and dashes and should not be ended by dash or dot.', + required: true, + }, + }, + { + weight: 100, + type: 'tags', + input: true, + label: 'Field Tags', + storeas: 'array', + tooltip: 'Tag the field for use in custom logic.', + key: 'tags', + }, + { + weight: 200, + type: 'datamap', + label: 'Custom Properties', + tooltip: + 'This allows you to configure any custom properties for this component.', + key: 'properties', + valueComponent: { + type: 'textfield', + key: 'value', + label: 'Value', + placeholder: 'Value', + input: true, + }, + }, ]; diff --git a/src/components/_classes/component/editForm/Component.edit.conditional.js b/src/components/_classes/component/editForm/Component.edit.conditional.js index 4a6f6917ef..c3c8d93396 100644 --- a/src/components/_classes/component/editForm/Component.edit.conditional.js +++ b/src/components/_classes/component/editForm/Component.edit.conditional.js @@ -2,52 +2,56 @@ import EditFormUtils from './utils'; import { getContextComponents } from '../../../../utils/utils'; /* eslint-disable quotes, max-len */ export default [ - { - type: 'panel', - title: 'Simple', - key: 'simple-conditional', - theme: 'default', - weight: 105, - components: [ - { - type: 'select', - input: true, - label: 'This component should Display:', - key: 'conditional.show', - dataSrc: 'values', - data: { - values: [ - { label: 'True', value: 'true' }, - { label: 'False', value: 'false' } - ] - } - }, - { - type: 'select', - input: true, - label: 'When the form component:', - key: 'conditional.when', - dataSrc: 'custom', - valueProperty: 'value', - data: { - custom(context) { - return getContextComponents(context); - } - } - }, - { - type: 'textfield', - input: true, - label: 'Has the value:', - key: 'conditional.eq' - } - ] - }, - EditFormUtils.javaScriptValue('Advanced Conditions', 'customConditional', 'conditional.json', 110, - '

You must assign the show variable a boolean result.

' + - '

Note: Advanced Conditional logic will override the results of the Simple Conditional logic.

' + - '
Example
show = !!data.showMe;
', - '

Click here for an example

' - ) + { + type: 'panel', + title: 'Simple', + key: 'simple-conditional', + theme: 'default', + weight: 105, + components: [ + { + type: 'select', + input: true, + label: 'This component should Display:', + key: 'conditional.show', + dataSrc: 'values', + data: { + values: [ + { label: 'True', value: 'true' }, + { label: 'False', value: 'false' }, + ], + }, + }, + { + type: 'select', + input: true, + label: 'When the form component:', + key: 'conditional.when', + dataSrc: 'custom', + valueProperty: 'value', + data: { + custom(context) { + return getContextComponents(context); + }, + }, + }, + { + type: 'textfield', + input: true, + label: 'Has the value:', + key: 'conditional.eq', + }, + ], + }, + EditFormUtils.javaScriptValue( + 'Advanced Conditions', + 'customConditional', + 'conditional.json', + 110, + '

You must assign the show variable a boolean result.

' + + '

Note: Advanced Conditional logic will override the results of the Simple Conditional logic.

' + + '
Example
show = !!data.showMe;
', + '

Click here for an example

', + ), ]; /* eslint-enable quotes, max-len */ diff --git a/src/components/_classes/component/editForm/Component.edit.data.js b/src/components/_classes/component/editForm/Component.edit.data.js index 6e8c94d791..887fb1e19e 100644 --- a/src/components/_classes/component/editForm/Component.edit.data.js +++ b/src/components/_classes/component/editForm/Component.edit.data.js @@ -1,163 +1,181 @@ import EditFormUtils from './utils'; /* eslint-disable max-len */ export default [ - { - weight: 0, - type: 'checkbox', - label: 'Multiple Values', - tooltip: 'Allows multiple values to be entered for this field.', - key: 'multiple', - input: true - }, - { - type: 'textfield', - label: 'Default Value', - key: 'defaultValue', - weight: 5, - placeholder: 'Default Value', - tooltip: 'The Default Value will be the value for this field, before user interaction. Having a default value will override the placeholder text.', - input: true - }, - { - weight: 30, - type: 'radio', - label: 'Persistent', - tooltip: 'A persistent field will be stored in database when the form is submitted.', - key: 'persistent', - input: true, - inline: true, - defaultValue: true, - values: [ - { label: 'None', value: false }, - { label: 'Server', value: true }, - { label: 'Client', value: 'client-only' }, - ] - }, - { - weight: 150, - type: 'checkbox', - label: 'Protected', - tooltip: 'A protected field will not be returned when queried via API.', - key: 'protected', - input: true - }, - { - type: 'checkbox', - input: true, - weight: 200, - key: 'dbIndex', - label: 'Database Index', - tooltip: 'Set this field as an index within the database. Increases performance for submission queries.' - }, - { - weight: 400, - type: 'checkbox', - label: 'Encrypted', - tooltip: 'Encrypt this field on the server. This is two way encryption which is not suitable for passwords.', - key: 'encrypted', - input: true, - logic: [ - { - name: 'disabled', - trigger: { - type: 'javascript', - javascript: 'result = !instance.root.options.sac;' - }, - actions: [ - { - name: 'disabled', - type: 'property', - property: { - label: 'Disabled', - value: 'disabled', - type: 'boolean' + { + weight: 0, + type: 'checkbox', + label: 'Multiple Values', + tooltip: 'Allows multiple values to be entered for this field.', + key: 'multiple', + input: true, + }, + { + type: 'textfield', + label: 'Default Value', + key: 'defaultValue', + weight: 5, + placeholder: 'Default Value', + tooltip: + 'The Default Value will be the value for this field, before user interaction. Having a default value will override the placeholder text.', + input: true, + }, + { + weight: 30, + type: 'radio', + label: 'Persistent', + tooltip: + 'A persistent field will be stored in database when the form is submitted.', + key: 'persistent', + input: true, + inline: true, + defaultValue: true, + values: [ + { label: 'None', value: false }, + { label: 'Server', value: true }, + { label: 'Client', value: 'client-only' }, + ], + }, + { + weight: 150, + type: 'checkbox', + label: 'Protected', + tooltip: 'A protected field will not be returned when queried via API.', + key: 'protected', + input: true, + }, + { + type: 'checkbox', + input: true, + weight: 200, + key: 'dbIndex', + label: 'Database Index', + tooltip: + 'Set this field as an index within the database. Increases performance for submission queries.', + }, + { + weight: 400, + type: 'checkbox', + label: 'Encrypted', + tooltip: + 'Encrypt this field on the server. This is two way encryption which is not suitable for passwords.', + key: 'encrypted', + input: true, + logic: [ + { + name: 'disabled', + trigger: { + type: 'javascript', + javascript: 'result = !instance.root.options.sac;', + }, + actions: [ + { + name: 'disabled', + type: 'property', + property: { + label: 'Disabled', + value: 'disabled', + type: 'boolean', + }, + state: true, + }, + ], }, - state: true - } - ] - }, - { - name: 'disabledToolTip', - trigger: { - type: 'javascript', - javascript: 'result = !instance.root.options.sac;' - }, - actions: [ - { - name: 'addDisabledTooltip', - type: 'property', - property: { - label: 'Tooltip', - value: 'tooltip', - type: 'string' + { + name: 'disabledToolTip', + trigger: { + type: 'javascript', + javascript: 'result = !instance.root.options.sac;', + }, + actions: [ + { + name: 'addDisabledTooltip', + type: 'property', + property: { + label: 'Tooltip', + value: 'tooltip', + type: 'string', + }, + text: 'Only available with Security Module. Contact sales@form.io for more information.', + }, + ], }, - text: 'Only available with Security Module. Contact sales@form.io for more information.' - } - ] - } - ] - }, - { - type: 'select', - input: true, - key: 'redrawOn', - label: 'Redraw On', - weight: 600, - tooltip: 'Redraw this component if another component changes. This is useful if interpolating parts of the component like the label.', - dataSrc: 'custom', - valueProperty: 'value', - data: { - custom(context) { - var values = []; - values.push({ label: 'Any Change', value: 'data' }); - context.utils.eachComponent(context.instance.options.editForm.components, function(component, path) { - if (component.key !== context.data.key) { - values.push({ - label: component.label || component.key, - value: path - }); - } - }); - return values; - } + ], + }, + { + type: 'select', + input: true, + key: 'redrawOn', + label: 'Redraw On', + weight: 600, + tooltip: + 'Redraw this component if another component changes. This is useful if interpolating parts of the component like the label.', + dataSrc: 'custom', + valueProperty: 'value', + data: { + custom(context) { + var values = []; + values.push({ label: 'Any Change', value: 'data' }); + context.utils.eachComponent( + context.instance.options.editForm.components, + function (component, path) { + if (component.key !== context.data.key) { + values.push({ + label: component.label || component.key, + value: path, + }); + } + }, + ); + return values; + }, + }, + conditional: { + json: { '!': [{ var: 'data.dataSrc' }] }, + }, + }, + { + weight: 700, + type: 'checkbox', + label: 'Clear Value When Hidden', + key: 'clearOnHide', + defaultValue: true, + tooltip: 'When a field is hidden, clear the value.', + input: true, + }, + EditFormUtils.javaScriptValue( + 'Custom Default Value', + 'customDefaultValue', + 'customDefaultValue', + 1000, + '

Example:

value = data.firstName + " " + data.lastName;

', + '

Example:

{"cat": [{"var": "data.firstName"}, " ", {"var": "data.lastName"}]}
', + ), + EditFormUtils.javaScriptValue( + 'Calculated Value', + 'calculateValue', + 'calculateValue', + 1100, + '

Example:

value = data.a + data.b + data.c;

', + '

Example:

{"+": [{"var": "data.a"}, {"var": "data.b"}, {"var": "data.c"}]}

Click here for an example

', + 'tokenThe decoded JWT token for the authenticated user.', + ), + { + type: 'checkbox', + input: true, + weight: 1100, + key: 'calculateServer', + label: 'Calculate Value on server', + tooltip: + 'Checking this will run the calculation on the server. This is useful if you wish to override the values submitted with the calculations performed on the server.', }, - conditional: { - json: { '!' : [{ var: 'data.dataSrc' }] }, + { + type: 'checkbox', + input: true, + weight: 1200, + key: 'allowCalculateOverride', + label: 'Allow Manual Override of Calculated Value', + tooltip: + 'When checked, this will allow the user to manually override the calculated value.', }, - }, - { - weight: 700, - type: 'checkbox', - label: 'Clear Value When Hidden', - key: 'clearOnHide', - defaultValue: true, - tooltip: 'When a field is hidden, clear the value.', - input: true - }, - EditFormUtils.javaScriptValue('Custom Default Value', 'customDefaultValue', 'customDefaultValue', 1000, - '

Example:

value = data.firstName + " " + data.lastName;

', - '

Example:

{"cat": [{"var": "data.firstName"}, " ", {"var": "data.lastName"}]}
' - ), - EditFormUtils.javaScriptValue('Calculated Value', 'calculateValue', 'calculateValue', 1100, - '

Example:

value = data.a + data.b + data.c;

', - '

Example:

{"+": [{"var": "data.a"}, {"var": "data.b"}, {"var": "data.c"}]}

Click here for an example

', -'tokenThe decoded JWT token for the authenticated user.' - ), - { - type: 'checkbox', - input: true, - weight: 1100, - key: 'calculateServer', - label: 'Calculate Value on server', - tooltip: 'Checking this will run the calculation on the server. This is useful if you wish to override the values submitted with the calculations performed on the server.' - }, - { - type: 'checkbox', - input: true, - weight: 1200, - key: 'allowCalculateOverride', - label: 'Allow Manual Override of Calculated Value', - tooltip: 'When checked, this will allow the user to manually override the calculated value.' - }, ]; /* eslint-enable max-len */ diff --git a/src/components/_classes/component/editForm/Component.edit.display.js b/src/components/_classes/component/editForm/Component.edit.display.js index b6159d9df6..a714b242dd 100644 --- a/src/components/_classes/component/editForm/Component.edit.display.js +++ b/src/components/_classes/component/editForm/Component.edit.display.js @@ -1,199 +1,204 @@ /* eslint-disable max-len */ export default [ - { - weight: 0, - type: 'textfield', - input: true, - key: 'label', - label: 'Label', - placeholder: 'Field Label', - tooltip: 'The label for this field that will appear next to it.', - validate: { - required: true - }, - autofocus: true, - }, - { - type: 'select', - input: true, - key: 'labelPosition', - label: 'Label Position', - tooltip: 'Position for the label for this field.', - weight: 20, - defaultValue: 'top', - dataSrc: 'values', - data: { - values: [ - { label: 'Top', value: 'top' }, - { label: 'Left (Left-aligned)', value: 'left-left' }, - { label: 'Left (Right-aligned)', value: 'left-right' }, - { label: 'Right (Left-aligned)', value: 'right-left' }, - { label: 'Right (Right-aligned)', value: 'right-right' }, - { label: 'Bottom', value: 'bottom' } - ] - } - }, - { - type: 'number', - input: true, - key: 'labelWidth', - label: 'Label Width', - tooltip: 'The width of label on line in percentages.', - clearOnHide: false, - weight: 30, - placeholder: '30', - suffix: '%', - validate: { - min: 0, - max: 100 - }, - conditional: { - json: { - and: [ - { '!==': [{ var: 'data.labelPosition' }, 'top'] }, - { '!==': [{ var: 'data.labelPosition' }, 'bottom'] }, - ] - } - } - }, - { - type: 'number', - input: true, - key: 'labelMargin', - label: 'Label Margin', - tooltip: 'The width of label margin on line in percentages.', - clearOnHide: false, - weight: 30, - placeholder: '3', - suffix: '%', - validate: { - min: 0, - max: 100 - }, - conditional: { - json: { - and: [ - { '!==': [{ var: 'data.labelPosition' }, 'top'] }, - { '!==': [{ var: 'data.labelPosition' }, 'bottom'] }, - ] - } - } - }, - { - weight: 100, - type: 'textfield', - input: true, - key: 'placeholder', - label: 'Placeholder', - placeholder: 'Placeholder', - tooltip: 'The placeholder text that will appear when this field is empty.' - }, - { - weight: 200, - type: 'textarea', - input: true, - key: 'description', - label: 'Description', - placeholder: 'Description for this field.', - tooltip: 'The description is text that will appear below the input field.', - editor: 'ace', - as: 'html', - wysiwyg: { - minLines: 3, - isUseWorkerDisabled: true, - }, - }, - { - weight: 300, - type: 'textarea', - input: true, - key: 'tooltip', - label: 'Tooltip', - placeholder: 'To add a tooltip to this field, enter text here.', - tooltip: 'Adds a tooltip to the side of this field.', - editor: 'ace', - as: 'html', - wysiwyg: { - minLines: 3, - isUseWorkerDisabled: true, - }, - }, - { - weight: 500, - type: 'textfield', - input: true, - key: 'customClass', - label: 'Custom CSS Class', - placeholder: 'Custom CSS Class', - tooltip: 'Custom CSS class to add to this component.' - }, - { - weight: 600, - type: 'textfield', - input: true, - key: 'tabindex', - label: 'Tab Index', - placeholder: '0', - tooltip: 'Sets the tabindex attribute of this component to override the tab order of the form. See the MDN documentation on tabindex for more information.' - }, - { - weight: 1100, - type: 'checkbox', - label: 'Hidden', - tooltip: 'A hidden field is still a part of the form, but is hidden from view.', - key: 'hidden', - input: true - }, - { - weight: 1200, - type: 'checkbox', - label: 'Hide Label', - tooltip: 'Hide the label (title, if no label) of this component. This allows you to show the label in the form builder, but not when it is rendered.', - key: 'hideLabel', - input: true - }, - { - weight: 1350, - type: 'checkbox', - label: 'Initial Focus', - tooltip: 'Make this field the initially focused element on this form.', - key: 'autofocus', - input: true - }, - { - weight: 1370, - type: 'checkbox', - label: 'Show Label in DataGrid', - tooltip: 'Show the label inside each row when in a Datagrid.', - key: 'dataGridLabel', - input: true, - customConditional(context) { - return context.instance.options?.flags?.inDataGrid; - } - }, - { - weight: 1400, - type: 'checkbox', - label: 'Disabled', - tooltip: 'Disable the form input.', - key: 'disabled', - input: true - }, - { - weight: 1500, - type: 'checkbox', - label: 'Table View', - tooltip: 'Shows this value within the table view of the submissions.', - key: 'tableView', - input: true - }, - { - weight: 1600, - type: 'checkbox', - label: 'Modal Edit', - tooltip: 'Opens up a modal to edit the value of this component.', - key: 'modalEdit', - input: true - }, + { + weight: 0, + type: 'textfield', + input: true, + key: 'label', + label: 'Label', + placeholder: 'Field Label', + tooltip: 'The label for this field that will appear next to it.', + validate: { + required: true, + }, + autofocus: true, + }, + { + type: 'select', + input: true, + key: 'labelPosition', + label: 'Label Position', + tooltip: 'Position for the label for this field.', + weight: 20, + defaultValue: 'top', + dataSrc: 'values', + data: { + values: [ + { label: 'Top', value: 'top' }, + { label: 'Left (Left-aligned)', value: 'left-left' }, + { label: 'Left (Right-aligned)', value: 'left-right' }, + { label: 'Right (Left-aligned)', value: 'right-left' }, + { label: 'Right (Right-aligned)', value: 'right-right' }, + { label: 'Bottom', value: 'bottom' }, + ], + }, + }, + { + type: 'number', + input: true, + key: 'labelWidth', + label: 'Label Width', + tooltip: 'The width of label on line in percentages.', + clearOnHide: false, + weight: 30, + placeholder: '30', + suffix: '%', + validate: { + min: 0, + max: 100, + }, + conditional: { + json: { + and: [ + { '!==': [{ var: 'data.labelPosition' }, 'top'] }, + { '!==': [{ var: 'data.labelPosition' }, 'bottom'] }, + ], + }, + }, + }, + { + type: 'number', + input: true, + key: 'labelMargin', + label: 'Label Margin', + tooltip: 'The width of label margin on line in percentages.', + clearOnHide: false, + weight: 30, + placeholder: '3', + suffix: '%', + validate: { + min: 0, + max: 100, + }, + conditional: { + json: { + and: [ + { '!==': [{ var: 'data.labelPosition' }, 'top'] }, + { '!==': [{ var: 'data.labelPosition' }, 'bottom'] }, + ], + }, + }, + }, + { + weight: 100, + type: 'textfield', + input: true, + key: 'placeholder', + label: 'Placeholder', + placeholder: 'Placeholder', + tooltip: + 'The placeholder text that will appear when this field is empty.', + }, + { + weight: 200, + type: 'textarea', + input: true, + key: 'description', + label: 'Description', + placeholder: 'Description for this field.', + tooltip: + 'The description is text that will appear below the input field.', + editor: 'ace', + as: 'html', + wysiwyg: { + minLines: 3, + isUseWorkerDisabled: true, + }, + }, + { + weight: 300, + type: 'textarea', + input: true, + key: 'tooltip', + label: 'Tooltip', + placeholder: 'To add a tooltip to this field, enter text here.', + tooltip: 'Adds a tooltip to the side of this field.', + editor: 'ace', + as: 'html', + wysiwyg: { + minLines: 3, + isUseWorkerDisabled: true, + }, + }, + { + weight: 500, + type: 'textfield', + input: true, + key: 'customClass', + label: 'Custom CSS Class', + placeholder: 'Custom CSS Class', + tooltip: 'Custom CSS class to add to this component.', + }, + { + weight: 600, + type: 'textfield', + input: true, + key: 'tabindex', + label: 'Tab Index', + placeholder: '0', + tooltip: + "Sets the tabindex attribute of this component to override the tab order of the form. See the MDN documentation on tabindex for more information.", + }, + { + weight: 1100, + type: 'checkbox', + label: 'Hidden', + tooltip: + 'A hidden field is still a part of the form, but is hidden from view.', + key: 'hidden', + input: true, + }, + { + weight: 1200, + type: 'checkbox', + label: 'Hide Label', + tooltip: + 'Hide the label (title, if no label) of this component. This allows you to show the label in the form builder, but not when it is rendered.', + key: 'hideLabel', + input: true, + }, + { + weight: 1350, + type: 'checkbox', + label: 'Initial Focus', + tooltip: 'Make this field the initially focused element on this form.', + key: 'autofocus', + input: true, + }, + { + weight: 1370, + type: 'checkbox', + label: 'Show Label in DataGrid', + tooltip: 'Show the label inside each row when in a Datagrid.', + key: 'dataGridLabel', + input: true, + customConditional(context) { + return context.instance.options?.flags?.inDataGrid; + }, + }, + { + weight: 1400, + type: 'checkbox', + label: 'Disabled', + tooltip: 'Disable the form input.', + key: 'disabled', + input: true, + }, + { + weight: 1500, + type: 'checkbox', + label: 'Table View', + tooltip: 'Shows this value within the table view of the submissions.', + key: 'tableView', + input: true, + }, + { + weight: 1600, + type: 'checkbox', + label: 'Modal Edit', + tooltip: 'Opens up a modal to edit the value of this component.', + key: 'modalEdit', + input: true, + }, ]; /* eslint-enable max-len */ diff --git a/src/components/_classes/component/editForm/Component.edit.layout.js b/src/components/_classes/component/editForm/Component.edit.layout.js index de2da8f0a9..f828fb7acd 100644 --- a/src/components/_classes/component/editForm/Component.edit.layout.js +++ b/src/components/_classes/component/editForm/Component.edit.layout.js @@ -1,78 +1,81 @@ export default [ - { - label: 'HTML Attributes', - type: 'datamap', - input: true, - key: 'attributes', - keyLabel: 'Attribute Name', - valueComponent: { - type: 'textfield', - key: 'value', - label: 'Attribute Value', - input: true - }, - tooltip: 'Provide a map of HTML attributes for component\'s input element (attributes provided by other component settings or other attributes generated by form.io take precedence over attributes in this grid)', - addAnother: 'Add Attribute', - }, - { - type: 'panel', - legend: 'PDF Overlay', - title: 'PDF Overlay', - key: 'overlay', - tooltip: 'The settings inside apply only to the PDF forms.', - weight: 2000, - collapsible: true, - collapsed: true, - components: [ - { - type: 'textfield', - input: true, - key: 'overlay.style', - label: 'Style', - placeholder: '', - tooltip: 'Custom styles that should be applied to this component when rendered in PDF.' - }, - { - type: 'textfield', - input: true, - key: 'overlay.page', - label: 'Page', - placeholder: '', - tooltip: 'The PDF page to place this component.' - }, - { - type: 'textfield', - input: true, - key: 'overlay.left', - label: 'Left', - placeholder: '', - tooltip: 'The left margin within a page to place this component.' - }, - { - type: 'textfield', + { + label: 'HTML Attributes', + type: 'datamap', input: true, - key: 'overlay.top', - label: 'Top', - placeholder: '', - tooltip: 'The top margin within a page to place this component.' - }, - { - type: 'textfield', - input: true, - key: 'overlay.width', - label: 'Width', - placeholder: '', - tooltip: 'The width of the component (in pixels).' - }, - { - type: 'textfield', - input: true, - key: 'overlay.height', - label: 'Height', - placeholder: '', - tooltip: 'The height of the component (in pixels).' - }, - - ] - }, + key: 'attributes', + keyLabel: 'Attribute Name', + valueComponent: { + type: 'textfield', + key: 'value', + label: 'Attribute Value', + input: true, + }, + tooltip: + "Provide a map of HTML attributes for component's input element (attributes provided by other component settings or other attributes generated by form.io take precedence over attributes in this grid)", + addAnother: 'Add Attribute', + }, + { + type: 'panel', + legend: 'PDF Overlay', + title: 'PDF Overlay', + key: 'overlay', + tooltip: 'The settings inside apply only to the PDF forms.', + weight: 2000, + collapsible: true, + collapsed: true, + components: [ + { + type: 'textfield', + input: true, + key: 'overlay.style', + label: 'Style', + placeholder: '', + tooltip: + 'Custom styles that should be applied to this component when rendered in PDF.', + }, + { + type: 'textfield', + input: true, + key: 'overlay.page', + label: 'Page', + placeholder: '', + tooltip: 'The PDF page to place this component.', + }, + { + type: 'textfield', + input: true, + key: 'overlay.left', + label: 'Left', + placeholder: '', + tooltip: + 'The left margin within a page to place this component.', + }, + { + type: 'textfield', + input: true, + key: 'overlay.top', + label: 'Top', + placeholder: '', + tooltip: + 'The top margin within a page to place this component.', + }, + { + type: 'textfield', + input: true, + key: 'overlay.width', + label: 'Width', + placeholder: '', + tooltip: 'The width of the component (in pixels).', + }, + { + type: 'textfield', + input: true, + key: 'overlay.height', + label: 'Height', + placeholder: '', + tooltip: 'The height of the component (in pixels).', + }, + ], + }, ]; diff --git a/src/components/_classes/component/editForm/Component.edit.logic.js b/src/components/_classes/component/editForm/Component.edit.logic.js index 1779caa226..def366fec7 100644 --- a/src/components/_classes/component/editForm/Component.edit.logic.js +++ b/src/components/_classes/component/editForm/Component.edit.logic.js @@ -3,411 +3,432 @@ import { getContextComponents } from '../../../../utils/utils'; /* eslint-disable quotes, max-len */ export default [ - { - weight: 0, - input: true, - label: 'Advanced Logic', - key: 'logic', - templates: { - header: '
\n
\n {{ value.length }} {{ ctx.t("Advanced Logic Configured") }}\n
\n
', - row: '
\n
\n
{{ row.name }}
\n
\n
\n
\n \n \n
\n
\n
', - footer: '', - }, - type: 'editgrid', - addAnother: 'Add Logic', - saveRow: 'Save Logic', - components: [ - { + { weight: 0, input: true, - inputType: 'text', - label: 'Logic Name', - key: 'name', - validate: { - required: true, - }, - type: 'textfield', - }, - { - weight: 10, - key: 'triggerPanel', - input: false, - title: 'Trigger', - tableView: false, - components: [ - { - weight: 0, - input: true, - tableView: false, - components: [ - { - weight: 0, - input: true, - label: 'Type', - key: 'type', - tableView: false, - data: { - values: [ - { - value: 'simple', - label: 'Simple', - }, - { - value: 'javascript', - label: 'Javascript', - }, - { - value: 'json', - label: 'JSON Logic', - }, - { - value: 'event', - label: 'Event', - }, - ], - }, - dataSrc: 'values', - template: '{{ item.label }}', - type: 'select', - }, - { - weight: 10, - label: '', - key: 'simple', - type: 'container', - tableView: false, - customConditional({ row }) { - return row.type === 'simple'; - }, - components: [ - { - input: true, - key: 'show', - label: 'Show', - type: 'hidden', - tableView: false, - calculateValue() { - return true; - }, - }, - { - type: 'select', - input: true, - label: 'When the form component:', - key: 'when', - dataSrc: 'custom', - valueProperty: 'value', - tableView: false, - data: { - custom(context) { - return getContextComponents(context); - }, - }, - }, - { - type: 'textfield', - input: true, - label: 'Has the value:', - key: 'eq', - tableView: false, - }, - ], - }, - { - weight: 10, - type: 'textarea', - key: 'javascript', - rows: 5, - editor: 'ace', - as: 'javascript', - input: true, - tableView: false, - placeholder: `result = (data['mykey'] > 1);`, - description: '"row", "data", and "component" variables are available. Return "result".', - customConditional({ row }) { - return row.type === 'javascript'; - }, - }, - { - weight: 10, - type: 'textarea', - key: 'json', - rows: 5, - editor: 'ace', - label: 'JSON Logic', - as: 'json', - input: true, - tableView: false, - placeholder: `{ ... }`, - description: '"row", "data", "component" and "_" variables are available. Return the result to be passed to the action if truthy.', - customConditional({ row }) { - return row.type === 'json'; - }, - }, - { - weight: 10, - type: 'textfield', - key: 'event', - label: 'Event Name', - placeholder: 'event', - description: 'The event that will trigger this logic. You can trigger events externally or via a button.', - tableView: false, - customConditional({ row }) { - return row.type === 'event'; - }, - }, - ], - key: 'trigger', - type: 'container', - }, - ], - type: 'panel', - }, - { - weight: 20, - input: true, - label: 'Actions', - key: 'actions', - tableView: false, + label: 'Advanced Logic', + key: 'logic', templates: { - header: '
\n
{{ value.length }} {{ ctx.t("actions") }}
\n
', - row: '
\n
\n
{{ row.name }}
\n
\n
\n
\n \n \n
\n
\n
', - footer: '', + header: '
\n
\n {{ value.length }} {{ ctx.t("Advanced Logic Configured") }}\n
\n
', + row: '
\n
\n
{{ row.name }}
\n
\n
\n
\n \n \n
\n
\n
', + footer: '', }, type: 'editgrid', - addAnother: 'Add Action', - saveRow: 'Save Action', + addAnother: 'Add Logic', + saveRow: 'Save Logic', components: [ - { - weight: 0, - title: 'Action', - input: false, - key: 'actionPanel', - type: 'panel', - components: [ - { + { weight: 0, input: true, inputType: 'text', - label: 'Action Name', + label: 'Logic Name', key: 'name', validate: { - required: true, + required: true, }, type: 'textfield', - }, - { + }, + { weight: 10, - input: true, - label: 'Type', - key: 'type', - data: { - values: [ - { - value: 'property', - label: 'Property', - }, - { - value: 'value', - label: 'Value', - }, - { - label: 'Merge Component Schema', - value: 'mergeComponentSchema', - }, - { - label: 'Custom Action', - value: 'customAction', - }, - ], - }, - dataSrc: 'values', - template: '{{ item.label }}', - type: 'select', - }, - { - weight: 20, - type: 'select', - template: '{{ item.label }}', - dataSrc: 'json', - tableView: false, - data: { - json: [ - { - label: 'Hidden', - value: 'hidden', - type: 'boolean', - }, - { - label: 'Required', - value: 'validate.required', - type: 'boolean', - }, - { - label: 'Disabled', - value: 'disabled', - type: 'boolean', - }, - { - label: 'Label', - value: 'label', - type: 'string', - }, - { - label: 'Title', - value: 'title', - type: 'string', - }, - { - label: 'Prefix', - value: 'prefix', - type: 'string', - }, - { - label: 'Suffix', - value: 'suffix', - type: 'string', - }, - { - label: 'Tooltip', - value: 'tooltip', - type: 'string', - }, - { - label: 'Description', - value: 'description', - type: 'string', - }, - { - label: 'Placeholder', - value: 'placeholder', - type: 'string', - }, - { - label: 'Input Mask', - value: 'inputMask', - type: 'string', - }, - { - label: 'CSS Class', - value: 'className', - type: 'string', - }, - { - label: 'Container Custom Class', - value: 'customClass', - type: 'string', - }, - ], - }, - key: 'property', - label: 'Component Property', - input: true, - customConditional({ row }) { - return row.type === 'property'; - }, - }, - { - weight: 30, - input: true, - label: 'Set State', - key: 'state', + key: 'triggerPanel', + input: false, + title: 'Trigger', tableView: false, - data: { - values: [ - { - label: 'True', - value: 'true', - }, + components: [ { - label: 'False', - value: 'false', + weight: 0, + input: true, + tableView: false, + components: [ + { + weight: 0, + input: true, + label: 'Type', + key: 'type', + tableView: false, + data: { + values: [ + { + value: 'simple', + label: 'Simple', + }, + { + value: 'javascript', + label: 'Javascript', + }, + { + value: 'json', + label: 'JSON Logic', + }, + { + value: 'event', + label: 'Event', + }, + ], + }, + dataSrc: 'values', + template: '{{ item.label }}', + type: 'select', + }, + { + weight: 10, + label: '', + key: 'simple', + type: 'container', + tableView: false, + customConditional({ row }) { + return row.type === 'simple'; + }, + components: [ + { + input: true, + key: 'show', + label: 'Show', + type: 'hidden', + tableView: false, + calculateValue() { + return true; + }, + }, + { + type: 'select', + input: true, + label: 'When the form component:', + key: 'when', + dataSrc: 'custom', + valueProperty: 'value', + tableView: false, + data: { + custom(context) { + return getContextComponents( + context, + ); + }, + }, + }, + { + type: 'textfield', + input: true, + label: 'Has the value:', + key: 'eq', + tableView: false, + }, + ], + }, + { + weight: 10, + type: 'textarea', + key: 'javascript', + rows: 5, + editor: 'ace', + as: 'javascript', + input: true, + tableView: false, + placeholder: `result = (data['mykey'] > 1);`, + description: + '"row", "data", and "component" variables are available. Return "result".', + customConditional({ row }) { + return row.type === 'javascript'; + }, + }, + { + weight: 10, + type: 'textarea', + key: 'json', + rows: 5, + editor: 'ace', + label: 'JSON Logic', + as: 'json', + input: true, + tableView: false, + placeholder: `{ ... }`, + description: + '"row", "data", "component" and "_" variables are available. Return the result to be passed to the action if truthy.', + customConditional({ row }) { + return row.type === 'json'; + }, + }, + { + weight: 10, + type: 'textfield', + key: 'event', + label: 'Event Name', + placeholder: 'event', + description: + 'The event that will trigger this logic. You can trigger events externally or via a button.', + tableView: false, + customConditional({ row }) { + return row.type === 'event'; + }, + }, + ], + key: 'trigger', + type: 'container', }, - ], - }, - dataSrc: 'values', - template: '{{ item.label }}', - type: 'select', - customConditional({ row }) { - return row.type === 'property' && - Object.prototype.hasOwnProperty.call(row, 'property') && - row.property.type === 'boolean'; - }, - }, - { - weight: 30, - type: 'textfield', - key: 'text', - label: 'Text', - inputType: 'text', - input: true, - tableView: false, - description: 'Can use templating with {{ data.myfield }}. "data", "row", "component" and "result" variables are available.', - customConditional({ row }) { - return row.type === 'property' && - Object.prototype.hasOwnProperty.call(row, 'property') && - row.property.type === 'string' && - !row.property.component; - }, - }, - { - weight: 20, - input: true, - label: 'Value (Javascript)', - key: 'value', - editor: 'ace', - as: 'javascript', - rows: 5, - placeholder: `value = data.myfield;`, - type: 'textarea', - tableView: false, - description: '"row", "data", "component", and "result" variables are available. Return the value.', - customConditional({ row }) { - return row.type === 'value'; - }, - }, - { - weight: 20, - input: true, - label: 'Schema Definition', - key: 'schemaDefinition', - editor: 'ace', - as: 'javascript', - rows: 5, - placeholder: `schema = { label: 'Updated' };`, - type: 'textarea', - tableView: false, - description: '"row", "data", "component", and "result" variables are available. Return the schema.', - customConditional({ row }) { - return row.type === 'mergeComponentSchema'; - }, - }, - Object.assign(EditFormUtils.logicVariablesTable('inputThe value that was input into this component'), - { - customConditional({ row }) { - return row.type === 'customAction'; - } - } - ), - { + ], + type: 'panel', + }, + { weight: 20, input: true, - label: 'Custom Action (Javascript)', - key: 'customAction', - editor: 'ace', - rows: 5, - placeholder: `value = data.myfield;`, - type: 'textarea', + label: 'Actions', + key: 'actions', tableView: false, - customConditional({ row }) { - return row.type === 'customAction'; + templates: { + header: '
\n
{{ value.length }} {{ ctx.t("actions") }}
\n
', + row: '
\n
\n
{{ row.name }}
\n
\n
\n
\n \n \n
\n
\n
', + footer: '', }, - }, - ], - }, + type: 'editgrid', + addAnother: 'Add Action', + saveRow: 'Save Action', + components: [ + { + weight: 0, + title: 'Action', + input: false, + key: 'actionPanel', + type: 'panel', + components: [ + { + weight: 0, + input: true, + inputType: 'text', + label: 'Action Name', + key: 'name', + validate: { + required: true, + }, + type: 'textfield', + }, + { + weight: 10, + input: true, + label: 'Type', + key: 'type', + data: { + values: [ + { + value: 'property', + label: 'Property', + }, + { + value: 'value', + label: 'Value', + }, + { + label: 'Merge Component Schema', + value: 'mergeComponentSchema', + }, + { + label: 'Custom Action', + value: 'customAction', + }, + ], + }, + dataSrc: 'values', + template: '{{ item.label }}', + type: 'select', + }, + { + weight: 20, + type: 'select', + template: '{{ item.label }}', + dataSrc: 'json', + tableView: false, + data: { + json: [ + { + label: 'Hidden', + value: 'hidden', + type: 'boolean', + }, + { + label: 'Required', + value: 'validate.required', + type: 'boolean', + }, + { + label: 'Disabled', + value: 'disabled', + type: 'boolean', + }, + { + label: 'Label', + value: 'label', + type: 'string', + }, + { + label: 'Title', + value: 'title', + type: 'string', + }, + { + label: 'Prefix', + value: 'prefix', + type: 'string', + }, + { + label: 'Suffix', + value: 'suffix', + type: 'string', + }, + { + label: 'Tooltip', + value: 'tooltip', + type: 'string', + }, + { + label: 'Description', + value: 'description', + type: 'string', + }, + { + label: 'Placeholder', + value: 'placeholder', + type: 'string', + }, + { + label: 'Input Mask', + value: 'inputMask', + type: 'string', + }, + { + label: 'CSS Class', + value: 'className', + type: 'string', + }, + { + label: 'Container Custom Class', + value: 'customClass', + type: 'string', + }, + ], + }, + key: 'property', + label: 'Component Property', + input: true, + customConditional({ row }) { + return row.type === 'property'; + }, + }, + { + weight: 30, + input: true, + label: 'Set State', + key: 'state', + tableView: false, + data: { + values: [ + { + label: 'True', + value: 'true', + }, + { + label: 'False', + value: 'false', + }, + ], + }, + dataSrc: 'values', + template: '{{ item.label }}', + type: 'select', + customConditional({ row }) { + return ( + row.type === 'property' && + Object.prototype.hasOwnProperty.call( + row, + 'property', + ) && + row.property.type === 'boolean' + ); + }, + }, + { + weight: 30, + type: 'textfield', + key: 'text', + label: 'Text', + inputType: 'text', + input: true, + tableView: false, + description: + 'Can use templating with {{ data.myfield }}. "data", "row", "component" and "result" variables are available.', + customConditional({ row }) { + return ( + row.type === 'property' && + Object.prototype.hasOwnProperty.call( + row, + 'property', + ) && + row.property.type === 'string' && + !row.property.component + ); + }, + }, + { + weight: 20, + input: true, + label: 'Value (Javascript)', + key: 'value', + editor: 'ace', + as: 'javascript', + rows: 5, + placeholder: `value = data.myfield;`, + type: 'textarea', + tableView: false, + description: + '"row", "data", "component", and "result" variables are available. Return the value.', + customConditional({ row }) { + return row.type === 'value'; + }, + }, + { + weight: 20, + input: true, + label: 'Schema Definition', + key: 'schemaDefinition', + editor: 'ace', + as: 'javascript', + rows: 5, + placeholder: `schema = { label: 'Updated' };`, + type: 'textarea', + tableView: false, + description: + '"row", "data", "component", and "result" variables are available. Return the schema.', + customConditional({ row }) { + return row.type === 'mergeComponentSchema'; + }, + }, + Object.assign( + EditFormUtils.logicVariablesTable( + 'inputThe value that was input into this component', + ), + { + customConditional({ row }) { + return row.type === 'customAction'; + }, + }, + ), + { + weight: 20, + input: true, + label: 'Custom Action (Javascript)', + key: 'customAction', + editor: 'ace', + rows: 5, + placeholder: `value = data.myfield;`, + type: 'textarea', + tableView: false, + customConditional({ row }) { + return row.type === 'customAction'; + }, + }, + ], + }, + ], + }, ], - }, - ], - }, + }, ]; /* eslint-enable quotes, max-len */ diff --git a/src/components/_classes/component/editForm/Component.edit.validation.js b/src/components/_classes/component/editForm/Component.edit.validation.js index 1e56a37e2d..6eef8e38e9 100644 --- a/src/components/_classes/component/editForm/Component.edit.validation.js +++ b/src/components/_classes/component/editForm/Component.edit.validation.js @@ -3,168 +3,183 @@ import Evaluator from '../../../../utils/Evaluator'; /* eslint-disable quotes, max-len */ export default [ - { - weight: 10, - type: 'checkbox', - label: 'Required', - tooltip: 'A required field must be filled in before the form can be submitted.', - key: 'validate.required', - input: true - }, - { - weight: 100, - type: 'checkbox', - label: 'Unique', - tooltip: 'Makes sure the data submitted for this field is unique, and has not been submitted before.', - key: 'unique', - input: true - }, - { - weight: 100, - type: 'checkbox', - label: 'Validate When Hidden', - tooltip: 'Validates the component when it is hidden/conditionally hidden. Vaildation errors are displayed in the error alert on the form submission.', - key: 'validateWhenHidden', - input: true - }, - { - weight: 0, - type: 'select', - key: 'validateOn', - defaultValue: 'change', - input: true, - label: 'Validate On', - tooltip: 'Determines when this component should trigger front-end validation.', - dataSrc: 'values', - data: { - values: [ - { label: 'Change', value: 'change' }, - { label: 'Blur', value: 'blur' } - ] - } - }, - { - weight: 190, - type: 'textfield', - input: true, - key: 'errorLabel', - label: 'Error Label', - placeholder: 'Error Label', - tooltip: 'The label for this field when an error occurs.' - }, - { - weight: 200, - key: 'validate.customMessage', - label: 'Custom Error Message', - placeholder: 'Custom Error Message', - type: 'textfield', - tooltip: 'Error message displayed if any error occurred.', - input: true - }, - { - type: 'panel', - title: 'Custom Validation', - collapsible: true, - collapsed: true, - style: { 'margin-bottom': '10px' }, - key: 'custom-validation-js', - weight: 300, - customConditional() { - return !Evaluator.noeval || Evaluator.protectedEval; + { + weight: 10, + type: 'checkbox', + label: 'Required', + tooltip: + 'A required field must be filled in before the form can be submitted.', + key: 'validate.required', + input: true, }, - components: [ - EditFormUtils.logicVariablesTable('inputThe value that was input into this component'), - { - type: 'textarea', - key: 'validate.custom', - rows: 5, - editor: 'ace', - hideLabel: true, - as: 'javascript', - input: true - }, - { - type: 'htmlelement', - tag: 'div', - content: ` + { + weight: 100, + type: 'checkbox', + label: 'Unique', + tooltip: + 'Makes sure the data submitted for this field is unique, and has not been submitted before.', + key: 'unique', + input: true, + }, + { + weight: 100, + type: 'checkbox', + label: 'Validate When Hidden', + tooltip: + 'Validates the component when it is hidden/conditionally hidden. Vaildation errors are displayed in the error alert on the form submission.', + key: 'validateWhenHidden', + input: true, + }, + { + weight: 0, + type: 'select', + key: 'validateOn', + defaultValue: 'change', + input: true, + label: 'Validate On', + tooltip: + 'Determines when this component should trigger front-end validation.', + dataSrc: 'values', + data: { + values: [ + { label: 'Change', value: 'change' }, + { label: 'Blur', value: 'blur' }, + ], + }, + }, + { + weight: 190, + type: 'textfield', + input: true, + key: 'errorLabel', + label: 'Error Label', + placeholder: 'Error Label', + tooltip: 'The label for this field when an error occurs.', + }, + { + weight: 200, + key: 'validate.customMessage', + label: 'Custom Error Message', + placeholder: 'Custom Error Message', + type: 'textfield', + tooltip: 'Error message displayed if any error occurred.', + input: true, + }, + { + type: 'panel', + title: 'Custom Validation', + collapsible: true, + collapsed: true, + style: { 'margin-bottom': '10px' }, + key: 'custom-validation-js', + weight: 300, + customConditional() { + return !Evaluator.noeval || Evaluator.protectedEval; + }, + components: [ + EditFormUtils.logicVariablesTable( + 'inputThe value that was input into this component', + ), + { + type: 'textarea', + key: 'validate.custom', + rows: 5, + editor: 'ace', + hideLabel: true, + as: 'javascript', + input: true, + }, + { + type: 'htmlelement', + tag: 'div', + content: `

Enter custom validation code.

You must assign the valid variable as either true or an error message if validation fails.

Example:
valid = (input === 'Joe') ? true : 'Your name must be "Joe"';
-
` - }, - { - type: 'well', + `, + }, + { + type: 'well', + components: [ + { + weight: 100, + type: 'checkbox', + label: 'Secret Validation', + tooltip: + 'Check this if you wish to perform the validation ONLY on the server side. This keeps your validation logic private and secret.', + description: + 'Check this if you wish to perform the validation ONLY on the server side. This keeps your validation logic private and secret.', + key: 'validate.customPrivate', + input: true, + }, + ], + }, + ], + }, + { + type: 'panel', + title: 'JSONLogic Validation', + collapsible: true, + collapsed: true, + key: 'json-validation-json', + weight: 400, components: [ - { - weight: 100, - type: 'checkbox', - label: 'Secret Validation', - tooltip: 'Check this if you wish to perform the validation ONLY on the server side. This keeps your validation logic private and secret.', - description: 'Check this if you wish to perform the validation ONLY on the server side. This keeps your validation logic private and secret.', - key: 'validate.customPrivate', - input: true - } - ] - } - ] - }, - { - type: 'panel', - title: 'JSONLogic Validation', - collapsible: true, - collapsed: true, - key: 'json-validation-json', - weight: 400, - components: [ - { - type: 'htmlelement', - tag: 'div', - /* eslint-disable prefer-template */ - content: '

Execute custom logic using JSONLogic.

' + - '
Example:
' + - '
' + JSON.stringify({
-            "if": [
-              { "===": [{ "var": "input" }, "Bob"] },
-              true,
-              "Your name must be 'Bob'!"
-            ]
-          }, null, 2) + '
' - /* eslint-enable prefer-template */ - }, - { - type: 'textarea', - key: 'validate.json', - hideLabel: true, - rows: 5, - editor: 'ace', - as: 'json', - input: true - } - ] - }, - { - type: 'panel', - title: 'Custom Errors', - collapsible: true, - collapsed: true, - key: 'errors', - weight: 400, - components: [ - { - type: 'textarea', + { + type: 'htmlelement', + tag: 'div', + /* eslint-disable prefer-template */ + content: + '

Execute custom logic using JSONLogic.

' + + '
Example:
' + + '
' +
+                    JSON.stringify(
+                        {
+                            if: [
+                                { '===': [{ var: 'input' }, 'Bob'] },
+                                true,
+                                "Your name must be 'Bob'!",
+                            ],
+                        },
+                        null,
+                        2,
+                    ) +
+                    '
', + /* eslint-enable prefer-template */ + }, + { + type: 'textarea', + key: 'validate.json', + hideLabel: true, + rows: 5, + editor: 'ace', + as: 'json', + input: true, + }, + ], + }, + { + type: 'panel', + title: 'Custom Errors', + collapsible: true, + collapsed: true, key: 'errors', - hideLabel: true, - rows: 5, - editor: 'ace', - as: 'json', - input: true - }, - { - type: 'htmlelement', - tag: 'div', - content: ` + weight: 400, + components: [ + { + type: 'textarea', + key: 'errors', + hideLabel: true, + rows: 5, + editor: 'ace', + as: 'json', + input: true, + }, + { + type: 'htmlelement', + tag: 'div', + content: `

This allows you to set different custom error messages for different errors (in contrast to “Custom Error Message”, which only allows you to set one error message for all errors). E.g.

@@ -205,9 +220,9 @@ export default [
  • {{ maxYear }}
  • {{ regex }}
  • - ` - } - ] - } + `, + }, + ], + }, ]; /* eslint-enable quotes, max-len */ diff --git a/src/components/_classes/component/editForm/utils.js b/src/components/_classes/component/editForm/utils.js index 5a90768d38..ce53a7b14c 100644 --- a/src/components/_classes/component/editForm/utils.js +++ b/src/components/_classes/component/editForm/utils.js @@ -1,138 +1,158 @@ import _ from 'lodash'; import Evaluator from '../../../../utils/Evaluator'; const EditFormUtils = { - sortAndFilterComponents(components) { - return _.filter(_.sortBy(components, 'weight'), (item) => !item.ignore); - }, - unifyComponents(objValue, srcValue) { - if (objValue.key && srcValue.key) { - if (objValue.skipMerge || srcValue.skipMerge) { - return false; - } - if (objValue.key === srcValue.key) { - // Create complete objects by including missing keys. - _.each(objValue, (value, prop) => { - if (objValue.overrideEditForm || !Object.prototype.hasOwnProperty.call(srcValue, prop)) { - srcValue[prop] = value; - } - }); - _.each(srcValue, (value, prop) => { - if (srcValue.overrideEditForm || !Object.prototype.hasOwnProperty.call(objValue, prop)) { - objValue[prop] = value; - } - }); + sortAndFilterComponents(components) { + return _.filter(_.sortBy(components, 'weight'), (item) => !item.ignore); + }, + unifyComponents(objValue, srcValue) { + if (objValue.key && srcValue.key) { + if (objValue.skipMerge || srcValue.skipMerge) { + return false; + } + if (objValue.key === srcValue.key) { + // Create complete objects by including missing keys. + _.each(objValue, (value, prop) => { + if ( + objValue.overrideEditForm || + !Object.prototype.hasOwnProperty.call(srcValue, prop) + ) { + srcValue[prop] = value; + } + }); + _.each(srcValue, (value, prop) => { + if ( + srcValue.overrideEditForm || + !Object.prototype.hasOwnProperty.call(objValue, prop) + ) { + objValue[prop] = value; + } + }); - if (objValue.components) { - srcValue.components = EditFormUtils.sortAndFilterComponents( - _.unionWith(objValue.components, srcValue.components, EditFormUtils.unifyComponents) - ); + if (objValue.components) { + srcValue.components = EditFormUtils.sortAndFilterComponents( + _.unionWith( + objValue.components, + srcValue.components, + EditFormUtils.unifyComponents, + ), + ); + } + return true; + } else { + return false; + } } - return true; - } - else { - return false; - } - } - return _.isEqual(objValue, srcValue); - }, - logicVariablesTable(additional) { - additional = additional || ''; - return { - type: 'htmlelement', - tag: 'div', - /* eslint-disable prefer-template */ - content: '

    The following variables are available in all scripts.

    ' + - '' + - additional + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '
    formThe complete form JSON object
    submissionThe complete submission object.
    dataThe complete submission data object.
    rowContextual "row" data, used within DataGrid, EditGrid, and Container components
    componentThe current component JSON
    instanceThe current component instance.
    valueThe current value of the component.
    momentThe moment.js library for date manipulation.
    _An instance of Lodash.
    utilsAn instance of the FormioUtils object.
    utilAn alias for "utils".

    ' - /* eslint-enable prefer-template */ - }; - }, - javaScriptValue(title, property, propertyJSON, weight, exampleHTML, exampleJSON, additionalParams = '', excludeJSONLogic) { - const components = [ - this.logicVariablesTable(additionalParams), - { - type: 'panel', - title: 'JavaScript', - collapsible: true, - collapsed: false, - style: { 'margin-bottom': '10px' }, - key: `${property}-js`, - customConditional() { - return !Evaluator.noeval || Evaluator.protectedEval; - }, - components: [ - { - type: 'textarea', - key: property, - rows: 5, - editor: 'ace', - hideLabel: true, - as: 'javascript', - input: true - }, - { - type: 'htmlelement', - tag: 'div', - content: `

    Enter custom javascript code.

    ${exampleHTML}` - } - ] - }, - { - type: 'panel', - title: 'JSONLogic', - collapsible: true, - collapsed: true, - key: `${property}-json`, - components: [ - { + return _.isEqual(objValue, srcValue); + }, + logicVariablesTable(additional) { + additional = additional || ''; + return { type: 'htmlelement', tag: 'div', /* eslint-disable prefer-template */ - content: '

    Execute custom logic using JSONLogic.

    ' + - '

    Full Lodash support is provided using an "_" before each operation, such as {"_sum": {var: "data.a"}}

    ' + - exampleJSON + content: + '

    The following variables are available in all scripts.

    ' + + '' + + additional + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '
    formThe complete form JSON object
    submissionThe complete submission object.
    dataThe complete submission data object.
    rowContextual "row" data, used within DataGrid, EditGrid, and Container components
    componentThe current component JSON
    instanceThe current component instance.
    valueThe current value of the component.
    momentThe moment.js library for date manipulation.
    _An instance of Lodash.
    utilsAn instance of the FormioUtils object.
    utilAn alias for "utils".

    ', /* eslint-enable prefer-template */ - }, - { - type: 'textarea', - key: propertyJSON, - rows: 5, - editor: 'ace', - hideLabel: true, - as: 'json', - input: true - } - ] - } - ]; + }; + }, + javaScriptValue( + title, + property, + propertyJSON, + weight, + exampleHTML, + exampleJSON, + additionalParams = '', + excludeJSONLogic, + ) { + const components = [ + this.logicVariablesTable(additionalParams), + { + type: 'panel', + title: 'JavaScript', + collapsible: true, + collapsed: false, + style: { 'margin-bottom': '10px' }, + key: `${property}-js`, + customConditional() { + return !Evaluator.noeval || Evaluator.protectedEval; + }, + components: [ + { + type: 'textarea', + key: property, + rows: 5, + editor: 'ace', + hideLabel: true, + as: 'javascript', + input: true, + }, + { + type: 'htmlelement', + tag: 'div', + content: `

    Enter custom javascript code.

    ${exampleHTML}`, + }, + ], + }, + { + type: 'panel', + title: 'JSONLogic', + collapsible: true, + collapsed: true, + key: `${property}-json`, + components: [ + { + type: 'htmlelement', + tag: 'div', + /* eslint-disable prefer-template */ + content: + '

    Execute custom logic using JSONLogic.

    ' + + '

    Full Lodash support is provided using an "_" before each operation, such as {"_sum": {var: "data.a"}}

    ' + + exampleJSON, + /* eslint-enable prefer-template */ + }, + { + type: 'textarea', + key: propertyJSON, + rows: 5, + editor: 'ace', + hideLabel: true, + as: 'json', + input: true, + }, + ], + }, + ]; - if (excludeJSONLogic) { - components.splice(2, 1); - } + if (excludeJSONLogic) { + components.splice(2, 1); + } - return { - type: 'panel', - title: title, - theme: 'default', - collapsible: true, - collapsed: true, - key: `${property}Panel`, - weight: weight, - components - }; - } + return { + type: 'panel', + title: title, + theme: 'default', + collapsible: true, + collapsed: true, + key: `${property}Panel`, + weight: weight, + components, + }; + }, }; export default EditFormUtils; diff --git a/src/components/_classes/component/editForm/utils.spec.js b/src/components/_classes/component/editForm/utils.spec.js index a21cc288ae..8115ecd4c1 100644 --- a/src/components/_classes/component/editForm/utils.spec.js +++ b/src/components/_classes/component/editForm/utils.spec.js @@ -2,46 +2,52 @@ import { expect } from 'chai'; import _ from 'lodash'; import utils from './utils'; -describe('Edit Form Utils', function() { - describe('unifyComponents', function() { - it('should merge all objects with the same key', function() { - const components = [ - { key: 'a', label: 1, input: true }, - { key: 'a', one: 1, two: 2 }, - { key: 'b', one: 1, two: 2 } - ]; +describe('Edit Form Utils', function () { + describe('unifyComponents', function () { + it('should merge all objects with the same key', function () { + const components = [ + { key: 'a', label: 1, input: true }, + { key: 'a', one: 1, two: 2 }, + { key: 'b', one: 1, two: 2 }, + ]; - expect(_.unionWith(components, utils.unifyComponents)).to.deep.equal([ - { key: 'a', label: 1, input: true, one: 1, two: 2 }, - { key: 'b', one: 1, two: 2 } - ]); - }); + expect( + _.unionWith(components, utils.unifyComponents), + ).to.deep.equal([ + { key: 'a', label: 1, input: true, one: 1, two: 2 }, + { key: 'b', one: 1, two: 2 }, + ]); + }); - it('should not merge objects with "skipMerge" flag', function() { - const components = [ - { key: 'a', label: 1 }, - { key: 'a', label: 2, skipMerge: true }, - { key: 'b', one: 1, two: 2 }, - { key: 'b', one: 1 }, - { key: 'b', one: 1, ok: true } - ]; + it('should not merge objects with "skipMerge" flag', function () { + const components = [ + { key: 'a', label: 1 }, + { key: 'a', label: 2, skipMerge: true }, + { key: 'b', one: 1, two: 2 }, + { key: 'b', one: 1 }, + { key: 'b', one: 1, ok: true }, + ]; - expect(_.unionWith(components, utils.unifyComponents)).to.deep.equal([ - { key: 'a', label: 1 }, - { key: 'a', label: 2, skipMerge: true }, - { key: 'b', one: 1, two: 2, ok: true }, - ]); - }); + expect( + _.unionWith(components, utils.unifyComponents), + ).to.deep.equal([ + { key: 'a', label: 1 }, + { key: 'a', label: 2, skipMerge: true }, + { key: 'b', one: 1, two: 2, ok: true }, + ]); + }); - it('should override with "override" flag', function() { - const components = [ - { key: 'a', label: 1, ok: true }, - { key: 'a', label: 2, overrideEditForm: true } - ]; + it('should override with "override" flag', function () { + const components = [ + { key: 'a', label: 1, ok: true }, + { key: 'a', label: 2, overrideEditForm: true }, + ]; - expect(_.unionWith(components, utils.unifyComponents)).to.deep.equal([ - { key: 'a', label: 2, ok: true, overrideEditForm: true } - ]); + expect( + _.unionWith(components, utils.unifyComponents), + ).to.deep.equal([ + { key: 'a', label: 2, ok: true, overrideEditForm: true }, + ]); + }); }); - }); }); diff --git a/src/components/_classes/component/fixtures/comp1.js b/src/components/_classes/component/fixtures/comp1.js index d2031b1bcf..47403d802c 100644 --- a/src/components/_classes/component/fixtures/comp1.js +++ b/src/components/_classes/component/fixtures/comp1.js @@ -1,31 +1,31 @@ export default { - 'tags': [], - 'type': 'textfield', - 'conditional': { - 'eq': '', - 'when': null, - 'show': '' - }, - 'validate': { - 'customPrivate': false, - 'custom': '', - 'pattern': '', - 'maxLength': 0, - 'minLength': 0, - 'required': false - }, - 'persistent': true, - 'unique': false, - 'protected': false, - 'defaultValue': '', - 'multiple': false, - 'suffix': '', - 'prefix': '', - 'placeholder': '', - 'key': 'firstName', - 'label': 'First Name', - 'inputMask': '', - 'inputType': 'text', - 'tableView': true, - 'input': true + tags: [], + type: 'textfield', + conditional: { + eq: '', + when: null, + show: '', + }, + validate: { + customPrivate: false, + custom: '', + pattern: '', + maxLength: 0, + minLength: 0, + required: false, + }, + persistent: true, + unique: false, + protected: false, + defaultValue: '', + multiple: false, + suffix: '', + prefix: '', + placeholder: '', + key: 'firstName', + label: 'First Name', + inputMask: '', + inputType: 'text', + tableView: true, + input: true, }; diff --git a/src/components/_classes/component/fixtures/comp2.js b/src/components/_classes/component/fixtures/comp2.js index dc0c5d3f73..81d168d024 100644 --- a/src/components/_classes/component/fixtures/comp2.js +++ b/src/components/_classes/component/fixtures/comp2.js @@ -1,31 +1,31 @@ export default { - 'tags': [], - 'type': 'textfield', - 'conditional': { - 'eq': '', - 'when': null, - 'show': '' - }, - 'validate': { - 'customPrivate': false, - 'custom': '', - 'pattern': '', - 'maxLength': 0, - 'minLength': 0, - 'required': false - }, - 'persistent': true, - 'unique': false, - 'protected': false, - 'defaultValue': '', - 'multiple': true, - 'suffix': '', - 'prefix': '', - 'placeholder': '', - 'key': 'names', - 'label': 'Names', - 'inputMask': '', - 'inputType': 'text', - 'tableView': true, - 'input': true + tags: [], + type: 'textfield', + conditional: { + eq: '', + when: null, + show: '', + }, + validate: { + customPrivate: false, + custom: '', + pattern: '', + maxLength: 0, + minLength: 0, + required: false, + }, + persistent: true, + unique: false, + protected: false, + defaultValue: '', + multiple: true, + suffix: '', + prefix: '', + placeholder: '', + key: 'names', + label: 'Names', + inputMask: '', + inputType: 'text', + tableView: true, + input: true, }; diff --git a/src/components/_classes/component/fixtures/comp3.js b/src/components/_classes/component/fixtures/comp3.js index 904cd35f87..fd933e305c 100644 --- a/src/components/_classes/component/fixtures/comp3.js +++ b/src/components/_classes/component/fixtures/comp3.js @@ -1,12 +1,12 @@ export default { - label: 'Text Field', - tableView: true, - modalEdit: true, - multiple: true, - validate: { - multiple: true - }, - key: 'textField', - type: 'textfield', - input: true + label: 'Text Field', + tableView: true, + modalEdit: true, + multiple: true, + validate: { + multiple: true, + }, + key: 'textField', + type: 'textfield', + input: true, }; diff --git a/src/components/_classes/component/fixtures/comp4.js b/src/components/_classes/component/fixtures/comp4.js index b53b5bf09c..404c4168d2 100644 --- a/src/components/_classes/component/fixtures/comp4.js +++ b/src/components/_classes/component/fixtures/comp4.js @@ -1,16 +1,16 @@ export default { - _id: '611389eae1215a4dadc1099d', - label: 'Text Field', - tableView: true, - modalEdit: true, - validate: { - required: true - }, - key: 'textField', - type: 'textfield', - input: true, - title: 'FIO-3631', - display: 'form', - name: 'fio3631', - path: 'fio3631', + _id: '611389eae1215a4dadc1099d', + label: 'Text Field', + tableView: true, + modalEdit: true, + validate: { + required: true, + }, + key: 'textField', + type: 'textfield', + input: true, + title: 'FIO-3631', + display: 'form', + name: 'fio3631', + path: 'fio3631', }; diff --git a/src/components/_classes/component/fixtures/comp5.js b/src/components/_classes/component/fixtures/comp5.js index 68806c95d7..772054b4ac 100644 --- a/src/components/_classes/component/fixtures/comp5.js +++ b/src/components/_classes/component/fixtures/comp5.js @@ -1,24 +1,25 @@ export default { - type: 'form', - display: 'form', - components: [ - { - label: 'Text Field', - description: "", - tooltip: "", + tooltip: " { - if (this.isValueChanged() && !this.component.disabled) { - this.showDialog(); - } - else { - this.closeModalHandler(event); - } - }; - this.closeModalListener = this.closeModalHandler.bind(this); - this.saveModalListener = this.saveModalValueHandler.bind(this); - this.closeDialogListener = this.closeDialog.bind(this); - this.saveDialogListener = this.saveDialog.bind(this); - this.loadRefs(); - } - - setValue(value) { - if (this.dataLoaded && this.currentValue === value) { - return; - } - - this.currentValue = fastCloneDeep(value); - this.dataLoaded = true; - this.updateView(); - } - - setOpenModalElement(template) { - if (this.component?.visible) { - this.openModalTemplate = template; - this.component.setContent(this.refs.openModalWrapper, template); - this.loadRefs(); - this.setEventListeners(); - if (this.isOpened) { - this.refs.modalWrapper.classList.add('formio-dialog-disabled-animation'); + static render(component, data, topLevel) { + const children = component.renderTemplate('component', data, topLevel); + + return component.renderTemplate('componentModal', { + ...data, + children, + }); + } + + constructor(component, element, isOpened, currentValue) { + this.isOpened = isOpened; + this.component = component; + this.element = element; + this.currentValue = fastCloneDeep( + currentValue ?? this.component.getValue(), + ); + this.dataLoaded = false; + this.init(); + } + + get refs() { + return this.component.refs; + } + + init() { + this.openModalListener = this.openModalHandler.bind(this); + this.showDialogListener = (event) => { + if (this.isValueChanged() && !this.component.disabled) { + this.showDialog(); + } else { + this.closeModalHandler(event); + } + }; + this.closeModalListener = this.closeModalHandler.bind(this); + this.saveModalListener = this.saveModalValueHandler.bind(this); + this.closeDialogListener = this.closeDialog.bind(this); + this.saveDialogListener = this.saveDialog.bind(this); + this.loadRefs(); + } + + setValue(value) { + if (this.dataLoaded && this.currentValue === value) { + return; + } + + this.currentValue = fastCloneDeep(value); + this.dataLoaded = true; + this.updateView(); + } + + setOpenModalElement(template) { + if (this.component?.visible) { + this.openModalTemplate = template; + this.component.setContent(this.refs.openModalWrapper, template); + this.loadRefs(); + this.setEventListeners(); + if (this.isOpened) { + this.refs.modalWrapper.classList.add( + 'formio-dialog-disabled-animation', + ); + this.openModal(); + } + } + } + + get templateRefs() { + return { + modalOverlay: 'single', + modalContents: 'single', + modalClose: 'single', + openModalWrapper: 'single', + openModal: 'single', + modalSave: 'single', + modalWrapper: 'single', + }; + } + + loadRefs() { + this.component.loadRefs(this.element, this.templateRefs); + } + + removeEventListeners() { + this.component.removeEventListener( + this.refs.openModal, + 'click', + this.openModalListener, + ); + this.component.removeEventListener( + this.refs.modalOverlay, + 'click', + this.refs.modalSave + ? this.showDialogListener + : this.saveModalListener, + ); + this.component.removeEventListener( + this.refs.modalClose, + 'click', + this.showDialogListener, + ); + this.component.removeEventListener( + this.refs.modalSave, + 'click', + this.saveModalListener, + ); + } + + setEventListeners() { + this.removeEventListeners(); + this.component.addEventListener( + this.refs.openModal, + 'click', + this.openModalListener, + ); + this.component.addEventListener( + this.refs.modalOverlay, + 'click', + this.refs.modalSave + ? this.showDialogListener + : this.saveModalListener, + ); + this.component.addEventListener( + this.refs.modalClose, + 'click', + this.showDialogListener, + ); + this.component.addEventListener( + this.refs.modalSave, + 'click', + this.saveModalListener, + ); + } + + isValueChanged() { + let componentValue = this.component.getValue(); + let currentValue = this.currentValue; + + //excluding metadata comparison for components that have it in dataValue (for ex. nested forms) + if (componentValue && componentValue.data && componentValue.metadata) { + componentValue = this.component.getValue().data; + currentValue = this.currentValue.data; + } + + return !_.isEqual(fastCloneDeep(componentValue), currentValue); + } + + setOpenEventListener() { + this.component.removeEventListener( + this.refs.openModal, + 'click', + this.openModalListener, + ); + this.component.loadRefs(this.refs.openModalWrapper ?? this.element, { + openModal: 'single', + }); + this.component.addEventListener( + this.refs.openModal, + 'click', + this.openModalListener, + ); + } + + openModalHandler(event) { + event.preventDefault(); this.openModal(); - } - } - } - - get templateRefs() { - return { - modalOverlay: 'single', - modalContents: 'single', - modalClose: 'single', - openModalWrapper: 'single', - openModal: 'single', - modalSave: 'single', - modalWrapper: 'single', - }; - } - - loadRefs() { - this.component.loadRefs(this.element, this.templateRefs); - } - - removeEventListeners() { - this.component.removeEventListener(this.refs.openModal, 'click', this.openModalListener); - this.component.removeEventListener(this.refs.modalOverlay, 'click', this.refs.modalSave ? this.showDialogListener : this.saveModalListener); - this.component.removeEventListener(this.refs.modalClose, 'click', this.showDialogListener); - this.component.removeEventListener(this.refs.modalSave, 'click', this.saveModalListener); - } - - setEventListeners() { - this.removeEventListeners(); - this.component.addEventListener(this.refs.openModal, 'click', this.openModalListener); - this.component.addEventListener(this.refs.modalOverlay, 'click', this.refs.modalSave ? this.showDialogListener : this.saveModalListener); - this.component.addEventListener(this.refs.modalClose, 'click', this.showDialogListener); - this.component.addEventListener(this.refs.modalSave, 'click', this.saveModalListener); - } - - isValueChanged() { - let componentValue = this.component.getValue(); - let currentValue = this.currentValue; - - //excluding metadata comparison for components that have it in dataValue (for ex. nested forms) - if (componentValue && componentValue.data && componentValue.metadata) { - componentValue = this.component.getValue().data; - currentValue = this.currentValue.data; - } - - return !_.isEqual(fastCloneDeep(componentValue), currentValue); - } - - setOpenEventListener() { - this.component.removeEventListener(this.refs.openModal, 'click', this.openModalListener); - this.component.loadRefs(this.refs.openModalWrapper ?? this.element, { - 'openModal': 'single', - }); - this.component.addEventListener(this.refs.openModal, 'click', this.openModalListener); - } - - openModalHandler(event) { - event.preventDefault(); - this.openModal(); - } - - positionOverElement() { - // Position the modal just over the element on the page. - const elementOffset = this.element.getBoundingClientRect().top; - const modalHeight = this.refs.modalContents.getBoundingClientRect().height; - let modalTop = elementOffset - modalHeight - 10; - modalTop = modalTop > 0 ? modalTop : 10; - this.refs.modalWrapper.style.paddingTop = `${modalTop}px`; - } - - openModal() { - this.isOpened = true; - this.refs.modalWrapper.classList.remove('component-rendering-hidden'); - if (this.component.component.type === 'signature') { - // Position signature modals just above the signature button. - this.positionOverElement(); - } - } - - updateView() { - const template = _.isEqual(this.currentValue, this.component.defaultValue) - ? this.openModalTemplate - : this.component.getModalPreviewTemplate(); - this.component.setContent(this.refs.openModalWrapper, template); - this.setOpenEventListener(); - } - - closeModal() { - this.refs.modalWrapper.classList.remove('formio-dialog-disabled-animation'); - this.refs.modalWrapper.classList.add('component-rendering-hidden'); - this.isOpened = false; - this.updateView(); - } - - closeModalHandler(event) { - event.preventDefault(); - if (!this.component.disabled) { - this.component.setValue(_.cloneDeep(this.currentValue), { resetValue: true }); - } - this.closeModal(); - } - - showDialog() { - this.dialogElement = this.component.ce('div'); - const dialogContent = ` -

    ${this.component.t('Do you want to clear changes?')}

    + } + + positionOverElement() { + // Position the modal just over the element on the page. + const elementOffset = this.element.getBoundingClientRect().top; + const modalHeight = + this.refs.modalContents.getBoundingClientRect().height; + let modalTop = elementOffset - modalHeight - 10; + modalTop = modalTop > 0 ? modalTop : 10; + this.refs.modalWrapper.style.paddingTop = `${modalTop}px`; + } + + openModal() { + this.isOpened = true; + this.refs.modalWrapper.classList.remove('component-rendering-hidden'); + if (this.component.component.type === 'signature') { + // Position signature modals just above the signature button. + this.positionOverElement(); + } + } + + updateView() { + const template = _.isEqual( + this.currentValue, + this.component.defaultValue, + ) + ? this.openModalTemplate + : this.component.getModalPreviewTemplate(); + this.component.setContent(this.refs.openModalWrapper, template); + this.setOpenEventListener(); + } + + closeModal() { + this.refs.modalWrapper.classList.remove( + 'formio-dialog-disabled-animation', + ); + this.refs.modalWrapper.classList.add('component-rendering-hidden'); + this.isOpened = false; + this.updateView(); + } + + closeModalHandler(event) { + event.preventDefault(); + if (!this.component.disabled) { + this.component.setValue(_.cloneDeep(this.currentValue), { + resetValue: true, + }); + } + this.closeModal(); + } + + showDialog() { + this.dialogElement = this.component.ce('div'); + const dialogContent = ` +

    ${this.component.t( + 'Do you want to clear changes?', + )}

    - - + +
    `; - this.dialogElement.innerHTML = dialogContent; - this.dialogElement.refs = {}; - this.component.loadRefs.call(this.dialogElement, this.dialogElement, { - dialogHeader: 'single', - dialogCancelButton: 'single', - dialogYesButton: 'single', - }); - - this.dialog = this.component.createModal(this.dialogElement); - this.component.addEventListener(this.dialogElement.refs.dialogYesButton, 'click', this.saveDialogListener); - this.component.addEventListener(this.dialogElement.refs.dialogCancelButton, 'click', this.closeDialogListener); - } - - closeDialog(event) { - event.preventDefault(); - this.dialog.close(); - this.component.removeEventListener(this.dialogElement.refs.dialogYesButton, 'click', this.saveDialogListener); - this.component.removeEventListener(this.dialogElement.refs.dialogCancelButton, 'click', this.closeDialogListener); - } - - saveDialog(event) { - this.closeDialog(event); - this.closeModalHandler(event); - } - - saveModalValueHandler(event) { - event.preventDefault(); - this.currentValue = fastCloneDeep(this.component.dataValue ?? this.component.getValue()); - this.closeModal(); - } + this.dialogElement.innerHTML = dialogContent; + this.dialogElement.refs = {}; + this.component.loadRefs.call(this.dialogElement, this.dialogElement, { + dialogHeader: 'single', + dialogCancelButton: 'single', + dialogYesButton: 'single', + }); + + this.dialog = this.component.createModal(this.dialogElement); + this.component.addEventListener( + this.dialogElement.refs.dialogYesButton, + 'click', + this.saveDialogListener, + ); + this.component.addEventListener( + this.dialogElement.refs.dialogCancelButton, + 'click', + this.closeDialogListener, + ); + } + + closeDialog(event) { + event.preventDefault(); + this.dialog.close(); + this.component.removeEventListener( + this.dialogElement.refs.dialogYesButton, + 'click', + this.saveDialogListener, + ); + this.component.removeEventListener( + this.dialogElement.refs.dialogCancelButton, + 'click', + this.closeDialogListener, + ); + } + + saveDialog(event) { + this.closeDialog(event); + this.closeModalHandler(event); + } + + saveModalValueHandler(event) { + event.preventDefault(); + this.currentValue = fastCloneDeep( + this.component.dataValue ?? this.component.getValue(), + ); + this.closeModal(); + } } diff --git a/src/components/_classes/field/Field.js b/src/components/_classes/field/Field.js index 5bc4a52ef4..05dfbdb3a1 100644 --- a/src/components/_classes/field/Field.js +++ b/src/components/_classes/field/Field.js @@ -1,36 +1,44 @@ import Component from '../component/Component'; export default class Field extends Component { - render(element) { - if (this.noField) { - return super.render(element); - } - else if (this.isAdvancedLabel || this.options.condensedMode) { - return super.render(this.renderTemplate('field', { - ...this.getLabelInfo(this.options.condensedMode), - labelMarkup: this.renderTemplate('label'), - element: element - }, 'align')); - } - else { - return super.render(this.renderTemplate('field', { - labelMarkup: this.renderTemplate('label'), - element: element, - })); + render(element) { + if (this.noField) { + return super.render(element); + } else if (this.isAdvancedLabel || this.options.condensedMode) { + return super.render( + this.renderTemplate( + 'field', + { + ...this.getLabelInfo(this.options.condensedMode), + labelMarkup: this.renderTemplate('label'), + element: element, + }, + 'align', + ), + ); + } else { + return super.render( + this.renderTemplate('field', { + labelMarkup: this.renderTemplate('label'), + element: element, + }), + ); + } } - } - // Saves current caret position to restore it after the component is redrawn - saveCaretPosition(element, index) { - if (this.root?.focusedComponent?.path === this.path) { - try { - this.root.currentSelection = { selection: [element.selectionStart, element.selectionEnd], index }; - } - catch (e) { - if (!(e instanceof DOMException)) { - console.debug(e); + // Saves current caret position to restore it after the component is redrawn + saveCaretPosition(element, index) { + if (this.root?.focusedComponent?.path === this.path) { + try { + this.root.currentSelection = { + selection: [element.selectionStart, element.selectionEnd], + index, + }; + } catch (e) { + if (!(e instanceof DOMException)) { + console.debug(e); + } + } } - } } - } } diff --git a/src/components/_classes/input/Input.js b/src/components/_classes/input/Input.js index b1cc447785..3b851f0b5d 100644 --- a/src/components/_classes/input/Input.js +++ b/src/components/_classes/input/Input.js @@ -4,336 +4,410 @@ import Widgets from '../../../widgets'; import _ from 'lodash'; export default class Input extends Multivalue { - constructor(component, options, data) { - super(component, options, data); - this.triggerUpdateValueAt = _.debounce(this.updateValueAt.bind(this), 100); - } - - static schema(...extend) { - return Multivalue.schema({ - widget: { - type: 'input' - } - }, ...extend); - } - - get inputInfo() { - const attr = { - name: this.options.name, - type: this.component.inputType || 'text', - class: 'form-control', - lang: this.options.language - }; - - if (this.options.attachMode === 'builder' || this.options.building || _.get(this.root, 'form.settings.disableAutocomplete')) { - attr.autocomplete = this.autocompleteDisableAttrName; + constructor(component, options, data) { + super(component, options, data); + this.triggerUpdateValueAt = _.debounce( + this.updateValueAt.bind(this), + 100, + ); } - if (this.component.inputMode) { - attr.inputmode = this.component.inputMode; + static schema(...extend) { + return Multivalue.schema( + { + widget: { + type: 'input', + }, + }, + ...extend, + ); } - if (this.component.placeholder) { - attr.placeholder = this.getFormattedAttribute(this.component.placeholder); - } + get inputInfo() { + const attr = { + name: this.options.name, + type: this.component.inputType || 'text', + class: 'form-control', + lang: this.options.language, + }; + + if ( + this.options.attachMode === 'builder' || + this.options.building || + _.get(this.root, 'form.settings.disableAutocomplete') + ) { + attr.autocomplete = this.autocompleteDisableAttrName; + } + + if (this.component.inputMode) { + attr.inputmode = this.component.inputMode; + } + + if (this.component.placeholder) { + attr.placeholder = this.getFormattedAttribute( + this.component.placeholder, + ); + } + + if (this.component.tabindex) { + attr.tabindex = this.component.tabindex; + } + + if (this.disabled) { + attr.disabled = 'disabled'; + } + + if (this.component.autocomplete) { + attr.autocomplete = this.component.autocomplete; + } - if (this.component.tabindex) { - attr.tabindex = this.component.tabindex; + _.defaults(attr, this.component.attributes); + + return { + id: this.key, + type: 'input', + changeEvent: 'input', + content: '', + attr, + }; } - if (this.disabled) { - attr.disabled = 'disabled'; + get autocompleteDisableAttrName() { + return 'off'; } - if (this.component.autocomplete) { - attr.autocomplete = this.component.autocomplete; + get maskOptions() { + return _.map(this.component.inputMasks, (mask) => { + return { + label: mask.label, + value: mask.label, + }; + }); } - _.defaults(attr, this.component.attributes); - - return { - id: this.key, - type: 'input', - changeEvent: 'input', - content: '', - attr - }; - } - - get autocompleteDisableAttrName() { - return 'off'; - } - - get maskOptions() { - return _.map(this.component.inputMasks, mask => { - return { - label: mask.label, - value: mask.label - }; - }); - } - - get isMultipleMasksField() { - return this.component.allowMultipleMasks && !!this.component.inputMasks && !!this.component.inputMasks.length; - } - - getMaskByName(maskName) { - const inputMask = _.find(this.component.inputMasks, (inputMask) => { - return inputMask.label === maskName; - }); - return inputMask ? inputMask.mask : undefined; - } - - setInputMask(input, inputMask) { - const mask = inputMask || this.component.displayMask || this.component.inputMask; - return super.setInputMask(input, mask, !this.component.placeholder); - } - - getMaskOptions() { - return this.component.inputMasks - .map(mask => ({ - label: mask.label, - value: mask.label, - })); - } - - getWordCount(value) { - return !value ? 0 : value.trim().split(/\s+/).length; - } - - get remainingWords() { - const maxWords = _.parseInt(_.get(this.component, 'validate.maxWords'), 10); - const wordCount = this.getWordCount(this.dataValue); - return maxWords - wordCount; - } - - get prefix() { - return this.component.prefix; - } - - get suffix() { - if (this.component.widget && this.component.widget.type === 'calendar') { - const calendarIcon = this.renderTemplate('icon', { - ref: 'icon', - // After font-awesome would be updated to v5.x, "clock-o" should be replaced with "clock" - className: this.iconClass(this.component.enableDate || this.component.widget.enableDate ? 'calendar' : 'clock-o'), - styles: '', - content: '' - }).trim(); - if (this.component.prefix !== calendarIcon) { - // converting string to HTML markup to render correctly DateTime component in portal.form.io - return convertStringToHTMLElement(calendarIcon, '[ref="icon"]'); - } + get isMultipleMasksField() { + return ( + this.component.allowMultipleMasks && + !!this.component.inputMasks && + !!this.component.inputMasks.length + ); } - return this.component.suffix; - } - renderElement(value, index) { - // Double quotes cause the input value to close so replace them with html quote char. - if (value && typeof value === 'string') { - value = value.replace(/"/g, '"'); + getMaskByName(maskName) { + const inputMask = _.find(this.component.inputMasks, (inputMask) => { + return inputMask.label === maskName; + }); + return inputMask ? inputMask.mask : undefined; } - const info = this.inputInfo; - info.attr = info.attr || {}; - info.attr.value = this.getValueAsString(this.formatValue(this.parseValue(value))) - .replace(/"/g, '"'); - const valueMask = this.component.inputMask; - const displayMask = this.component.displayMask; - const hasDifferentDisplayAndSaveFormats = valueMask && displayMask && valueMask !== displayMask; + setInputMask(input, inputMask) { + const mask = + inputMask || this.component.displayMask || this.component.inputMask; + return super.setInputMask(input, mask, !this.component.placeholder); + } - if (this.isMultipleMasksField) { - info.attr.class += ' formio-multiple-mask-input'; + getMaskOptions() { + return this.component.inputMasks.map((mask) => ({ + label: mask.label, + value: mask.label, + })); } - return this.isMultipleMasksField - ? this.renderTemplate('multipleMasksInput', { - input: info, - value, - index, - selectOptions: this.getMaskOptions() || [], - }, this.isHtmlRenderMode() ? 'html' : null) - : this.renderTemplate('input', { - prefix: this.prefix, - suffix: this.suffix, - input: info, - value: this.formatValue(this.parseValue(value)), - hasValueMaskInput: hasDifferentDisplayAndSaveFormats, - index - }, this.isHtmlRenderMode() ? 'html' : null); - } - - setCounter(type, element, count, max) { - if (max) { - const remaining = max - count; - if (remaining > 0) { - this.removeClass(element, 'text-danger'); - } - else { - this.addClass(element, 'text-danger'); - } - this.setContent(element, this.t(`{{ remaining }} ${type} remaining.`, { - remaining: remaining - })); + getWordCount(value) { + return !value ? 0 : value.trim().split(/\s+/).length; } - else { - this.setContent(element, this.t(`{{ count }} ${type}`, { - count: count - })); + + get remainingWords() { + const maxWords = _.parseInt( + _.get(this.component, 'validate.maxWords'), + 10, + ); + const wordCount = this.getWordCount(this.dataValue); + return maxWords - wordCount; } - } - - updateValueAt(value, flags, index) { - flags = flags || {}; - if (_.get(this.component, 'showWordCount', false)) { - if (this.refs.wordcount && this.refs.wordcount[index]) { - const maxWords = _.parseInt(_.get(this.component, 'validate.maxWords', 0), 10); - this.setCounter(this.t('words'), this.refs.wordcount[index], this.getWordCount(value), maxWords); - } + + get prefix() { + return this.component.prefix; } - if (_.get(this.component, 'showCharCount', false)) { - if (this.refs.charcount && this.refs.charcount[index]) { - const maxChars = _.parseInt(_.get(this.component, 'validate.maxLength', 0), 10); - this.setCounter(this.t('characters'), this.refs.charcount[index], value.length, maxChars); - } + + get suffix() { + if ( + this.component.widget && + this.component.widget.type === 'calendar' + ) { + const calendarIcon = this.renderTemplate('icon', { + ref: 'icon', + // After font-awesome would be updated to v5.x, "clock-o" should be replaced with "clock" + className: this.iconClass( + this.component.enableDate || + this.component.widget.enableDate + ? 'calendar' + : 'clock-o', + ), + styles: '', + content: '', + }).trim(); + if (this.component.prefix !== calendarIcon) { + // converting string to HTML markup to render correctly DateTime component in portal.form.io + return convertStringToHTMLElement(calendarIcon, '[ref="icon"]'); + } + } + return this.component.suffix; } - } - getValueAt(index) { - const input = this.performInputMapping(this.refs.input[index]); - if (input && input.widget) { - return input.widget.getValue(); + renderElement(value, index) { + // Double quotes cause the input value to close so replace them with html quote char. + if (value && typeof value === 'string') { + value = value.replace(/"/g, '"'); + } + const info = this.inputInfo; + info.attr = info.attr || {}; + info.attr.value = this.getValueAsString( + this.formatValue(this.parseValue(value)), + ).replace(/"/g, '"'); + + const valueMask = this.component.inputMask; + const displayMask = this.component.displayMask; + const hasDifferentDisplayAndSaveFormats = + valueMask && displayMask && valueMask !== displayMask; + + if (this.isMultipleMasksField) { + info.attr.class += ' formio-multiple-mask-input'; + } + + return this.isMultipleMasksField + ? this.renderTemplate( + 'multipleMasksInput', + { + input: info, + value, + index, + selectOptions: this.getMaskOptions() || [], + }, + this.isHtmlRenderMode() ? 'html' : null, + ) + : this.renderTemplate( + 'input', + { + prefix: this.prefix, + suffix: this.suffix, + input: info, + value: this.formatValue(this.parseValue(value)), + hasValueMaskInput: hasDifferentDisplayAndSaveFormats, + index, + }, + this.isHtmlRenderMode() ? 'html' : null, + ); } - return input ? input.value : undefined; - } - - updateValue(value, flags, index) { - flags = flags || {}; - const changed = super.updateValue(value, flags); - this.triggerUpdateValueAt(this.dataValue, flags, index); - return changed; - } - - parseValue(value) { - return value; - } - - formatValue(value) { - return value; - } - - attach(element) { - this.loadRefs(element, { - charcount: 'multiple', - wordcount: 'multiple', - prefix: 'multiple', - suffix: 'multiple' - }); - return super.attach(element); - } - - getWidget(index) { - index = index || 0; - if (this.refs.input && this.refs.input[index]) { - return this.refs.input[index].widget; + + setCounter(type, element, count, max) { + if (max) { + const remaining = max - count; + if (remaining > 0) { + this.removeClass(element, 'text-danger'); + } else { + this.addClass(element, 'text-danger'); + } + this.setContent( + element, + this.t(`{{ remaining }} ${type} remaining.`, { + remaining: remaining, + }), + ); + } else { + this.setContent( + element, + this.t(`{{ count }} ${type}`, { + count: count, + }), + ); + } } - return null; - } - attachElement(element, index) { - super.attachElement(element, index); - if (element.widget) { - element.widget.destroy(); + updateValueAt(value, flags, index) { + flags = flags || {}; + if (_.get(this.component, 'showWordCount', false)) { + if (this.refs.wordcount && this.refs.wordcount[index]) { + const maxWords = _.parseInt( + _.get(this.component, 'validate.maxWords', 0), + 10, + ); + this.setCounter( + this.t('words'), + this.refs.wordcount[index], + this.getWordCount(value), + maxWords, + ); + } + } + if (_.get(this.component, 'showCharCount', false)) { + if (this.refs.charcount && this.refs.charcount[index]) { + const maxChars = _.parseInt( + _.get(this.component, 'validate.maxLength', 0), + 10, + ); + this.setCounter( + this.t('characters'), + this.refs.charcount[index], + value.length, + maxChars, + ); + } + } } - // Attach the widget. - let promise = Promise.resolve(); - element.widget = this.createWidget(index); - if (element.widget) { - promise = element.widget.attach(element); - if (this.refs.prefix && this.refs.prefix[index]) { - element.widget.addPrefix(this.refs.prefix[index]); - } - if (this.refs.suffix && this.refs.suffix[index]) { - element.widget.addSuffix(this.refs.suffix[index]); - } + + getValueAt(index) { + const input = this.performInputMapping(this.refs.input[index]); + if (input && input.widget) { + return input.widget.getValue(); + } + return input ? input.value : undefined; } - // Add focus and blur events. - this.addFocusBlurEvents(element); + updateValue(value, flags, index) { + flags = flags || {}; + const changed = super.updateValue(value, flags); + this.triggerUpdateValueAt(this.dataValue, flags, index); + return changed; + } - if (this.options.submitOnEnter) { - this.addEventListener(element, 'keypress', (event) => { - const key = event.keyCode || event.which; - if (key === 13) { - event.preventDefault(); - event.stopPropagation(); - this.emit('submitButton'); - } - }); + parseValue(value) { + return value; } - return promise; - } - - /** - * Creates an instance of a widget for this component. - * - * @return {null} - */ - createWidget(index) { - // Return null if no widget is found. - if (!this.component.widget) { - return null; + + formatValue(value) { + return value; } - // Get the widget settings. - const settings = (typeof this.component.widget === 'string') ? { - type: this.component.widget - } : this.component.widget; + attach(element) { + this.loadRefs(element, { + charcount: 'multiple', + wordcount: 'multiple', + prefix: 'multiple', + suffix: 'multiple', + }); + return super.attach(element); + } - if (this.root?.shadowRoot) { - settings.shadowRoot = this.root?.shadowRoot; + getWidget(index) { + index = index || 0; + if (this.refs.input && this.refs.input[index]) { + return this.refs.input[index].widget; + } + return null; } - // Make sure we have a widget. - if (!Object.prototype.hasOwnProperty.call(Widgets, settings.type)) { - return null; + attachElement(element, index) { + super.attachElement(element, index); + if (element.widget) { + element.widget.destroy(); + } + // Attach the widget. + let promise = Promise.resolve(); + element.widget = this.createWidget(index); + if (element.widget) { + promise = element.widget.attach(element); + if (this.refs.prefix && this.refs.prefix[index]) { + element.widget.addPrefix(this.refs.prefix[index]); + } + if (this.refs.suffix && this.refs.suffix[index]) { + element.widget.addSuffix(this.refs.suffix[index]); + } + } + + // Add focus and blur events. + this.addFocusBlurEvents(element); + + if (this.options.submitOnEnter) { + this.addEventListener(element, 'keypress', (event) => { + const key = event.keyCode || event.which; + if (key === 13) { + event.preventDefault(); + event.stopPropagation(); + this.emit('submitButton'); + } + }); + } + return promise; } - // Create the widget. - const widget = new Widgets[settings.type](settings, this.component, this, index); - widget.on('update', () => this.updateValue(this.getValue(), { - modified: true - }, index), true); - widget.on('redraw', () => this.redraw(), true); - return widget; - } - - teardown() { - if (this.element && this.element.widget) { - this.element.widget.destroy(); - delete this.element.widget; + /** + * Creates an instance of a widget for this component. + * + * @return {null} + */ + createWidget(index) { + // Return null if no widget is found. + if (!this.component.widget) { + return null; + } + + // Get the widget settings. + const settings = + typeof this.component.widget === 'string' + ? { + type: this.component.widget, + } + : this.component.widget; + + if (this.root?.shadowRoot) { + settings.shadowRoot = this.root?.shadowRoot; + } + + // Make sure we have a widget. + if (!Object.prototype.hasOwnProperty.call(Widgets, settings.type)) { + return null; + } + + // Create the widget. + const widget = new Widgets[settings.type]( + settings, + this.component, + this, + index, + ); + widget.on( + 'update', + () => + this.updateValue( + this.getValue(), + { + modified: true, + }, + index, + ), + true, + ); + widget.on('redraw', () => this.redraw(), true); + return widget; } - if (this.refs && this.refs.input) { - for (let i = 0; i <= this.refs.input.length; i++) { - const widget = this.getWidget(i); - if (widget) { - widget.destroy(); + + teardown() { + if (this.element && this.element.widget) { + this.element.widget.destroy(); + delete this.element.widget; } - } + if (this.refs && this.refs.input) { + for (let i = 0; i <= this.refs.input.length; i++) { + const widget = this.getWidget(i); + if (widget) { + widget.destroy(); + } + } + } + super.teardown(); } - super.teardown(); - } - - detach() { - if (this.refs && this.refs.input) { - for (let i = 0; i <= this.refs.input.length; i++) { - const widget = this.getWidget(i); - if (widget) { - widget.destroy(); + + detach() { + if (this.refs && this.refs.input) { + for (let i = 0; i <= this.refs.input.length; i++) { + const widget = this.getWidget(i); + if (widget) { + widget.destroy(); + } + } } - } + this.refs.input = []; + super.detach(); } - this.refs.input = []; - super.detach(); - } } diff --git a/src/components/_classes/list/ListComponent.form.js b/src/components/_classes/list/ListComponent.form.js index e0cca99f24..be4fa70df6 100644 --- a/src/components/_classes/list/ListComponent.form.js +++ b/src/components/_classes/list/ListComponent.form.js @@ -1,11 +1,14 @@ import Components from '../../Components'; import ListEditData from './editForm/ListComponent.edit.data'; -export default function(...extend) { - return Components.baseEditForm([ - { - key: 'data', - components: ListEditData - }, - ], ...extend); +export default function (...extend) { + return Components.baseEditForm( + [ + { + key: 'data', + components: ListEditData, + }, + ], + ...extend, + ); } diff --git a/src/components/_classes/list/ListComponent.js b/src/components/_classes/list/ListComponent.js index ead6035c11..cb31fe9980 100644 --- a/src/components/_classes/list/ListComponent.js +++ b/src/components/_classes/list/ListComponent.js @@ -4,270 +4,347 @@ import _ from 'lodash'; import { getItemTemplateKeys } from '../../../utils/utils'; export default class ListComponent extends Field { - static schema(...extend) { - return Field.schema({ - dataSrc: 'values', - authenticate: false, - ignoreCache: false, - template: '{{ item.label }}', - validate: { - onlyAvailableItems: false - }, - }, ...extend); - } - - get isSelectURL() { - return this.component.dataSrc === 'url'; - } - - get selectData() { - const selectData = _.get(this.root, 'submission.metadata.selectData', {}); - return _.get(selectData, this.path); - } - - get shouldLoad() { - if (this.loadingError) { - return false; - } - // Live forms should always load. - if (!this.options.readOnly) { - return true; + static schema(...extend) { + return Field.schema( + { + dataSrc: 'values', + authenticate: false, + ignoreCache: false, + template: '{{ item.label }}', + validate: { + onlyAvailableItems: false, + }, + }, + ...extend, + ); } - // If there are template keys, then we need to see if we have the data. - if (this.templateKeys && this.templateKeys.length) { - // See if we already have the data we need. - const dataValue = this.dataValue; - const selectData = this.selectData; - return this.templateKeys.reduce((shouldLoad, key) => { - const hasValue = _.has(dataValue, key) || - (_.isArray(selectData) ? selectData.every((data) => _.has(data, key)) : _.has(selectData, key)); - return shouldLoad || !hasValue; - }, false); + get isSelectURL() { + return this.component.dataSrc === 'url'; } - // Return that we should load. - return true; - } - - getTemplateKeys() { - const template = this.component.template; - this.templateKeys = this.options.readOnly && template - ? getItemTemplateKeys(template) - : []; - } - - get requestHeaders() { - // Create the headers object. - const headers = new Formio.Headers(); - // Add custom headers to the url. - if (this.component.data && this.component.data.headers) { - try { - _.each(this.component.data.headers, (header) => { - if (header.key) { - headers.set(header.key, this.interpolate(header.value)); - } - }); - } - catch (err) { - console.warn(err.message); - } + get selectData() { + const selectData = _.get( + this.root, + 'submission.metadata.selectData', + {}, + ); + return _.get(selectData, this.path); } - return headers; - } - - // Must be implemented in child classes. - setItems() {} - - updateCustomItems() {} - - loadItems() {} + get shouldLoad() { + if (this.loadingError) { + return false; + } + // Live forms should always load. + if (!this.options.readOnly) { + return true; + } - getOptionTemplate(data, value, index) { - if (!this.component.template) { - return data.label; - } - const options = { - noeval: true, - data: {} - }; - const template = this.sanitize( - this.component.template - ? this.interpolate(this.component.template, { item: data }, options) - : data.label, - this.shouldSanitizeValue, - ); - const templateValue = this.component.reference && value?._id ? value._id.toString() : value; - if (templateValue && !_.isObject(templateValue) && options.data.item) { - // If the value is not an object, then we need to save the template data off for when it is selected. - this.templateData[templateValue] = options.data.item; - } - if (_.isNumber(index)) { - this.templateData[index] = options.data.item; - } - return template; - } + // If there are template keys, then we need to see if we have the data. + if (this.templateKeys && this.templateKeys.length) { + // See if we already have the data we need. + const dataValue = this.dataValue; + const selectData = this.selectData; + return this.templateKeys.reduce((shouldLoad, key) => { + const hasValue = + _.has(dataValue, key) || + (_.isArray(selectData) + ? selectData.every((data) => _.has(data, key)) + : _.has(selectData, key)); + return shouldLoad || !hasValue; + }, false); + } - itemTemplate(data, value, index) { - if (_.isEmpty(data)) { - return ''; + // Return that we should load. + return true; } - const template = this.sanitize(this.getOptionTemplate(data, value, index), this.shouldSanitizeValue); - if (template) { - const label = template.replace(/<\/?[^>]+(>|$)/g, ''); - if (!label) return; - return template.replace(label, this.t(label, { _userInput: true })); - } - else { - return this.sanitize(JSON.stringify(data), this.shouldSanitizeValue); + getTemplateKeys() { + const template = this.component.template; + this.templateKeys = + this.options.readOnly && template + ? getItemTemplateKeys(template) + : []; } - } - handleLoadingError(err) { - this.loading = false; - if (err.networkError) { - this.networkError = true; - } - this.itemsLoadedResolve(); - this.emit('componentError', { - component: this.component, - message: err.toString(), - }); - console.warn(`Unable to load resources for ${this.key}`); - } + get requestHeaders() { + // Create the headers object. + const headers = new Formio.Headers(); + // Add custom headers to the url. + if (this.component.data && this.component.data.headers) { + try { + _.each(this.component.data.headers, (header) => { + if (header.key) { + headers.set(header.key, this.interpolate(header.value)); + } + }); + } catch (err) { + console.warn(err.message); + } + } - /* eslint-disable max-statements */ - updateItems(searchInput, forceUpdate) { - if (!this.component.data) { - console.warn(`Select component ${this.key} does not have data configuration.`); - this.itemsLoadedResolve(); - return; + return headers; } - // Only load the data if it is visible. - if (!this.visible) { - this.itemsLoadedResolve(); - return; - } + // Must be implemented in child classes. + setItems() {} - switch (this.component.dataSrc) { - case 'values': - this.setItems(this.component.data.values); - break; - case 'json': - this.setItems(this.component.data.json); - break; - case 'custom': - this.updateCustomItems(forceUpdate); - break; - case 'resource': { - // If there is no resource, or we are lazyLoading, wait until active. - if (!this.component.data.resource || (!forceUpdate && !this.active)) { - this.itemsLoadedResolve(); - return; - } + updateCustomItems() {} - let resourceUrl = this.options.formio ? this.options.formio.formsUrl : `${Formio.getProjectUrl()}/form`; - resourceUrl += (`/${this.component.data.resource}/submission`); + loadItems() {} - if (forceUpdate || this.additionalResourcesAvailable || !this.serverCount) { - try { - this.loadItems(resourceUrl, searchInput, this.requestHeaders); - } - catch (err) { - console.warn(`Unable to load resources for ${this.key}`); - } + getOptionTemplate(data, value, index) { + if (!this.component.template) { + return data.label; } - else { - this.setItems(this.downloadedResources); + const options = { + noeval: true, + data: {}, + }; + const template = this.sanitize( + this.component.template + ? this.interpolate( + this.component.template, + { item: data }, + options, + ) + : data.label, + this.shouldSanitizeValue, + ); + const templateValue = + this.component.reference && value?._id + ? value._id.toString() + : value; + if (templateValue && !_.isObject(templateValue) && options.data.item) { + // If the value is not an object, then we need to save the template data off for when it is selected. + this.templateData[templateValue] = options.data.item; } - break; - } - case 'url': { - if (!forceUpdate && !this.active && !this.calculatedValue && this.component.type === 'select') { - // If we are lazyLoading, wait until activated. - this.itemsLoadedResolve(); - return; + if (_.isNumber(index)) { + this.templateData[index] = options.data.item; } - let { url } = this.component.data; - let method; - let body; - if (url.startsWith('/')) { - // if URL starts with '/project', we should use base URL to avoid issues with URL formed like //project//... - const baseUrl = url.startsWith('/project') ? Formio.getBaseUrl() : Formio.getProjectUrl() || Formio.getBaseUrl(); - url = baseUrl + url; + return template; + } + + itemTemplate(data, value, index) { + if (_.isEmpty(data)) { + return ''; } - if (!this.component.data.method) { - method = 'GET'; + const template = this.sanitize( + this.getOptionTemplate(data, value, index), + this.shouldSanitizeValue, + ); + if (template) { + const label = template.replace(/<\/?[^>]+(>|$)/g, ''); + if (!label) return; + return template.replace(label, this.t(label, { _userInput: true })); + } else { + return this.sanitize( + JSON.stringify(data), + this.shouldSanitizeValue, + ); } - else { - method = this.component.data.method; - if (method.toUpperCase() === 'POST') { - body = this.component.data.body; - } - else { - body = null; - } + } + + handleLoadingError(err) { + this.loading = false; + if (err.networkError) { + this.networkError = true; } + this.itemsLoadedResolve(); + this.emit('componentError', { + component: this.component, + message: err.toString(), + }); + console.warn(`Unable to load resources for ${this.key}`); + } - const options = this.component.authenticate ? {} : { noToken: true }; - this.loadItems(url, searchInput, this.requestHeaders, options, method, body); - break; - } - case 'indexeddb': { - if (typeof window === 'undefined') { - return; + /* eslint-disable max-statements */ + updateItems(searchInput, forceUpdate) { + if (!this.component.data) { + console.warn( + `Select component ${this.key} does not have data configuration.`, + ); + this.itemsLoadedResolve(); + return; } - if (!window.indexedDB) { - window.alert("Your browser doesn't support current version of indexedDB"); + // Only load the data if it is visible. + if (!this.visible) { + this.itemsLoadedResolve(); + return; } - if (this.component.indexeddb && this.component.indexeddb.database && this.component.indexeddb.table) { - const request = window.indexedDB.open(this.component.indexeddb.database); + switch (this.component.dataSrc) { + case 'values': + this.setItems(this.component.data.values); + break; + case 'json': + this.setItems(this.component.data.json); + break; + case 'custom': + this.updateCustomItems(forceUpdate); + break; + case 'resource': { + // If there is no resource, or we are lazyLoading, wait until active. + if ( + !this.component.data.resource || + (!forceUpdate && !this.active) + ) { + this.itemsLoadedResolve(); + return; + } - request.onupgradeneeded = (event) => { - if (this.component.customOptions) { - const db = event.target.result; - const objectStore = db.createObjectStore(this.component.indexeddb.table, { keyPath: 'myKey', autoIncrement: true }); - objectStore.transaction.oncomplete = () => { - const transaction = db.transaction(this.component.indexeddb.table, 'readwrite'); - this.component.customOptions.forEach((item) => { - transaction.objectStore(this.component.indexeddb.table).put(item); - }); - }; + let resourceUrl = this.options.formio + ? this.options.formio.formsUrl + : `${Formio.getProjectUrl()}/form`; + resourceUrl += `/${this.component.data.resource}/submission`; + + if ( + forceUpdate || + this.additionalResourcesAvailable || + !this.serverCount + ) { + try { + this.loadItems( + resourceUrl, + searchInput, + this.requestHeaders, + ); + } catch (err) { + console.warn( + `Unable to load resources for ${this.key}`, + ); + } + } else { + this.setItems(this.downloadedResources); + } + break; } - }; + case 'url': { + if ( + !forceUpdate && + !this.active && + !this.calculatedValue && + this.component.type === 'select' + ) { + // If we are lazyLoading, wait until activated. + this.itemsLoadedResolve(); + return; + } + let { url } = this.component.data; + let method; + let body; + if (url.startsWith('/')) { + // if URL starts with '/project', we should use base URL to avoid issues with URL formed like //project//... + const baseUrl = url.startsWith('/project') + ? Formio.getBaseUrl() + : Formio.getProjectUrl() || Formio.getBaseUrl(); + url = baseUrl + url; + } - request.onerror = () => { - window.alert(request.errorCode); - }; + if (!this.component.data.method) { + method = 'GET'; + } else { + method = this.component.data.method; + if (method.toUpperCase() === 'POST') { + body = this.component.data.body; + } else { + body = null; + } + } - request.onsuccess = (event) => { - const db = event.target.result; - const transaction = db.transaction(this.component.indexeddb.table, 'readwrite'); - const objectStore = transaction.objectStore(this.component.indexeddb.table); - new Promise((resolve) => { - const responseItems = []; - objectStore.getAll().onsuccess = (event) => { - event.target.result.forEach((item) => { - responseItems.push(item); - }); - resolve(responseItems); - }; - }).then((items) => { - if (!_.isEmpty(this.component.indexeddb.filter)) { - items = _.filter(items, this.component.indexeddb.filter); - } - this.setItems(items); - }); - }; + const options = this.component.authenticate + ? {} + : { noToken: true }; + this.loadItems( + url, + searchInput, + this.requestHeaders, + options, + method, + body, + ); + break; + } + case 'indexeddb': { + if (typeof window === 'undefined') { + return; + } + + if (!window.indexedDB) { + window.alert( + "Your browser doesn't support current version of indexedDB", + ); + } + + if ( + this.component.indexeddb && + this.component.indexeddb.database && + this.component.indexeddb.table + ) { + const request = window.indexedDB.open( + this.component.indexeddb.database, + ); + + request.onupgradeneeded = (event) => { + if (this.component.customOptions) { + const db = event.target.result; + const objectStore = db.createObjectStore( + this.component.indexeddb.table, + { keyPath: 'myKey', autoIncrement: true }, + ); + objectStore.transaction.oncomplete = () => { + const transaction = db.transaction( + this.component.indexeddb.table, + 'readwrite', + ); + this.component.customOptions.forEach((item) => { + transaction + .objectStore( + this.component.indexeddb.table, + ) + .put(item); + }); + }; + } + }; + + request.onerror = () => { + window.alert(request.errorCode); + }; + + request.onsuccess = (event) => { + const db = event.target.result; + const transaction = db.transaction( + this.component.indexeddb.table, + 'readwrite', + ); + const objectStore = transaction.objectStore( + this.component.indexeddb.table, + ); + new Promise((resolve) => { + const responseItems = []; + objectStore.getAll().onsuccess = (event) => { + event.target.result.forEach((item) => { + responseItems.push(item); + }); + resolve(responseItems); + }; + }).then((items) => { + if (!_.isEmpty(this.component.indexeddb.filter)) { + items = _.filter( + items, + this.component.indexeddb.filter, + ); + } + this.setItems(items); + }); + }; + } + } } - } } - } - /* eslint-enable max-statements */ + /* eslint-enable max-statements */ } diff --git a/src/components/_classes/list/editForm/ListComponent.edit.data.js b/src/components/_classes/list/editForm/ListComponent.edit.data.js index d74bb7a468..1c2eaaa5b2 100644 --- a/src/components/_classes/list/editForm/ListComponent.edit.data.js +++ b/src/components/_classes/list/editForm/ListComponent.edit.data.js @@ -3,7 +3,8 @@ export default [ type: 'select', input: true, weight: 0, - tooltip: 'The source to use for the select data. Values lets you provide your own values and labels. JSON lets you provide raw JSON data. URL lets you provide a URL to retrieve the JSON data from.', + tooltip: + 'The source to use for the select data. Values lets you provide your own values and labels. JSON lets you provide raw JSON data. URL lets you provide a URL to retrieve the JSON data from.', key: 'dataSrc', defaultValue: 'values', label: 'Data Source Type', @@ -26,24 +27,25 @@ export default [ input: true, label: 'Request Headers', key: 'data.headers', - tooltip: 'Set any headers that should be sent along with the request to the url. This is useful for authentication.', + tooltip: + 'Set any headers that should be sent along with the request to the url. This is useful for authentication.', weight: 11, components: [ - { - label: 'Key', - key: 'key', - input: true, - type: 'textfield', - }, - { - label: 'Value', - key: 'value', - input: true, - type: 'textfield', - }, + { + label: 'Key', + key: 'key', + input: true, + type: 'textfield', + }, + { + label: 'Value', + key: 'value', + input: true, + type: 'textfield', + }, ], conditional: { - json: { '===': [{ var: 'data.dataSrc' }, 'url'] }, + json: { '===': [{ var: 'data.dataSrc' }, 'url'] }, }, }, { @@ -55,17 +57,11 @@ export default [ clearOnHide: false, weight: 13, description: "The selected item's property to save.", - tooltip: 'The property of each item in the data source to use as the select value. If not specified, the item itself will be used.', + tooltip: + 'The property of each item in the data source to use as the select value. If not specified, the item itself will be used.', conditional: { json: { - in: [ - { var: 'data.dataSrc' }, - [ - 'json', - 'url', - 'custom' - ], - ], + in: [{ var: 'data.dataSrc' }, ['json', 'url', 'custom']], }, }, }, @@ -80,10 +76,14 @@ export default [ weight: 18, tooltip: 'The HTML template for the result data items.', allowCalculateOverride: true, - calculateValue:(context) => { + calculateValue: (context) => { if (!context.data.template) { - if (context.instance && context.instance._currentForm.options.editComponent) { - return context.instance._currentForm.options.editComponent.template; + if ( + context.instance && + context.instance._currentForm.options.editComponent + ) { + return context.instance._currentForm.options.editComponent + .template; } } return context.data.template; @@ -95,9 +95,10 @@ export default [ weight: 26, key: 'authenticate', label: 'Formio Authenticate', - tooltip: 'Check this if you would like to use Formio Authentication with the request.', + tooltip: + 'Check this if you would like to use Formio Authentication with the request.', conditional: { - json: { '===': [{ var: 'data.dataSrc' }, 'url'] }, + json: { '===': [{ var: 'data.dataSrc' }, 'url'] }, }, }, { @@ -106,12 +107,15 @@ export default [ weight: 29, key: 'ignoreCache', label: 'Disables Storing Request Result in the Cache', - tooltip: 'Check it if you don\'t want the requests and its results to be stored in the cache. By default, it is stored and if the Select tries to make the request to the same URL with the same paremetrs, the cached data will be returned. It allows to increase performance, but if the remote source\'s data is changing quite often and you always need to keep it up-to-date, uncheck this option.', + tooltip: + "Check it if you don't want the requests and its results to be stored in the cache. By default, it is stored and if the Select tries to make the request to the same URL with the same paremetrs, the cached data will be returned. It allows to increase performance, but if the remote source's data is changing quite often and you always need to keep it up-to-date, uncheck this option.", conditional: { - json: { 'or': [ - { '===': [{ var: 'data.dataSrc' }, 'url'] }, - { '===': [{ var: 'data.dataSrc' }, 'resource'] }, - ] }, + json: { + or: [ + { '===': [{ var: 'data.dataSrc' }, 'url'] }, + { '===': [{ var: 'data.dataSrc' }, 'resource'] }, + ], + }, }, }, ]; diff --git a/src/components/_classes/multivalue/Multivalue.js b/src/components/_classes/multivalue/Multivalue.js index 7f6a82663e..ea4db38fdd 100644 --- a/src/components/_classes/multivalue/Multivalue.js +++ b/src/components/_classes/multivalue/Multivalue.js @@ -2,293 +2,331 @@ import Field from '../field/Field'; import _ from 'lodash'; export default class Multivalue extends Field { - get dataValue() { - const parent = super.dataValue; + get dataValue() { + const parent = super.dataValue; - if (!parent && this.component.multiple) { - return []; - } - return parent; - } - - set dataValue(value) { - super.dataValue = value; - } - - get defaultValue() { - let value = super.defaultValue; - - if (this.component.multiple) { - if (_.isArray(value)) { - value = !value.length ? [super.emptyValue] : value; - } - else { - value = [value]; - } + if (!parent && this.component.multiple) { + return []; + } + return parent; } - return value; - } + set dataValue(value) { + super.dataValue = value; + } - get addAnother() { - return this.t(this.component.addAnother || 'Add Another'); - } + get defaultValue() { + let value = super.defaultValue; - useWrapper() { - return Object.prototype.hasOwnProperty.call(this.component, 'multiple') && this.component.multiple; - } + if (this.component.multiple) { + if (_.isArray(value)) { + value = !value.length ? [super.emptyValue] : value; + } else { + value = [value]; + } + } - render() { - // If single value field. - if (!this.useWrapper()) { - return super.render( - `
    - ${this.renderElement( - this.component.type !== 'hidden' ? this.dataValue : '' - )} -
    ` - ); + return value; } - // Make sure dataValue is in the correct array format. - let dataValue = this.dataValue; - if (!Array.isArray(dataValue)) { - dataValue = dataValue ? [dataValue] : []; + get addAnother() { + return this.t(this.component.addAnother || 'Add Another'); } - // If multiple value field. - return super.render(this.renderTemplate('multiValueTable', { - rows: dataValue.map(this.renderRow.bind(this)).join(''), - disabled: this.disabled, - addAnother: this.addAnother, - })); - } - - renderElement() { - return ''; - } - - renderRow(value, index) { - return this.renderTemplate('multiValueRow', { - index, - disabled: this.disabled, - element: `${this.renderElement(value, index)}`, - }); - } - - attach(dom) { - const superAttach = super.attach(dom); - this.loadRefs(dom, { - addButton: 'multiple', - input: 'multiple', - removeRow: 'multiple', - mask: 'multiple', - select: 'multiple', - }); - - const promises = []; - - this.refs.input.forEach((element, index) => { - promises.push(this.attachElement.call(this, element, index)); - }); - - if (!this.component.multiple) { - return Promise.all(promises); + useWrapper() { + return ( + Object.prototype.hasOwnProperty.call(this.component, 'multiple') && + this.component.multiple + ); } - this.refs.removeRow.forEach((removeButton, index) => { - this.addEventListener(removeButton, 'click', (event) => { - event.preventDefault(); - this.removeValue(index); - }); - }); - - // If single value field. - this.refs.addButton.forEach((addButton) => { - this.addEventListener(addButton, 'click', (event) => { - event.preventDefault(); - this.addValue(); - }); - }); - return superAttach.then(() => { - return Promise.all(promises); - }); - } - - detach() { - if (this.refs.input && this.refs.input.length) { - this.refs.input.forEach((input) => { - if (input.mask) { - input.mask.destroy ? input.mask.destroy() : input.mask.remove(); + render() { + // If single value field. + if (!this.useWrapper()) { + return super.render( + `
    + ${this.renderElement( + this.component.type !== 'hidden' ? this.dataValue : '', + )} +
    `, + ); } - if (input.widget) { - input.widget.destroy(); + + // Make sure dataValue is in the correct array format. + let dataValue = this.dataValue; + if (!Array.isArray(dataValue)) { + dataValue = dataValue ? [dataValue] : []; } - }); + + // If multiple value field. + return super.render( + this.renderTemplate('multiValueTable', { + rows: dataValue.map(this.renderRow.bind(this)).join(''), + disabled: this.disabled, + addAnother: this.addAnother, + }), + ); } - if (this.refs.mask && this.refs.mask.length) { - this.refs.mask.forEach((input) => { - if (input.mask) { - input.mask.destroy ? input.mask.destroy() : input.mask.remove(); - } - }); + + renderElement() { + return ''; } - super.detach(); - } - - /** - * Attach inputs to the element. - * - * @param element - * @param index - */ - attachElement(element, index) { - this.addEventListener(element, this.inputInfo.changeEvent, () => { - // Delay update slightly to give input mask a chance to run. - const textCase = _.get(this.component, 'case', 'mixed'); - - if (textCase !== 'mixed') { - const { - selectionStart, - selectionEnd, - } = element; - - if (textCase === 'uppercase' && element.value) { - element.value = element.value.toUpperCase(); - } - if (textCase === 'lowercase' && element.value) { - element.value = element.value.toLowerCase(); + + renderRow(value, index) { + return this.renderTemplate('multiValueRow', { + index, + disabled: this.disabled, + element: `${this.renderElement(value, index)}`, + }); + } + + attach(dom) { + const superAttach = super.attach(dom); + this.loadRefs(dom, { + addButton: 'multiple', + input: 'multiple', + removeRow: 'multiple', + mask: 'multiple', + select: 'multiple', + }); + + const promises = []; + + this.refs.input.forEach((element, index) => { + promises.push(this.attachElement.call(this, element, index)); + }); + + if (!this.component.multiple) { + return Promise.all(promises); } - if (element.selectionStart && element.selectionEnd) { - element.selectionStart = selectionStart; - element.selectionEnd = selectionEnd; + this.refs.removeRow.forEach((removeButton, index) => { + this.addEventListener(removeButton, 'click', (event) => { + event.preventDefault(); + this.removeValue(index); + }); + }); + + // If single value field. + this.refs.addButton.forEach((addButton) => { + this.addEventListener(addButton, 'click', (event) => { + event.preventDefault(); + this.addValue(); + }); + }); + return superAttach.then(() => { + return Promise.all(promises); + }); + } + + detach() { + if (this.refs.input && this.refs.input.length) { + this.refs.input.forEach((input) => { + if (input.mask) { + input.mask.destroy + ? input.mask.destroy() + : input.mask.remove(); + } + if (input.widget) { + input.widget.destroy(); + } + }); } - } - - try { - this.saveCaretPosition(element, index); - } - catch (err) { - console.warn('An error occurred while trying to save caret position', err); - } - - // If a mask is present, delay the update to allow mask to update first. - if (element.mask) { - setTimeout(() => { - return this.updateValue(null, { - modified: (this.component.type !== 'hidden') - }, index); - }, 1); - } - else { - return this.updateValue(null, { - modified: (this.component.type !== 'hidden') - }, index); - } - }); - - if (!this.attachMultiMask(index)) { - const applyMask = () => { - this.setInputMask(element); - - const valueMask = this.component.inputMask; - const displayMask = this.component.displayMask; - - if (valueMask && displayMask && displayMask !== valueMask && this.refs.valueMaskInput) { - this.setInputMask(this.refs.valueMaskInput, valueMask); + if (this.refs.mask && this.refs.mask.length) { + this.refs.mask.forEach((input) => { + if (input.mask) { + input.mask.destroy + ? input.mask.destroy() + : input.mask.remove(); + } + }); } - }; + super.detach(); + } - if (this.inputInfo.changeEvent === 'blur') { + /** + * Attach inputs to the element. + * + * @param element + * @param index + */ + attachElement(element, index) { this.addEventListener(element, this.inputInfo.changeEvent, () => { - applyMask(); - this.dataValue = this.refs.input[0].value; - if (this.checkComponentValidity()) { - this.updateComponentValue(this.refs.input[0].value); - } + // Delay update slightly to give input mask a chance to run. + const textCase = _.get(this.component, 'case', 'mixed'); + + if (textCase !== 'mixed') { + const { selectionStart, selectionEnd } = element; + + if (textCase === 'uppercase' && element.value) { + element.value = element.value.toUpperCase(); + } + if (textCase === 'lowercase' && element.value) { + element.value = element.value.toLowerCase(); + } + + if (element.selectionStart && element.selectionEnd) { + element.selectionStart = selectionStart; + element.selectionEnd = selectionEnd; + } + } + + try { + this.saveCaretPosition(element, index); + } catch (err) { + console.warn( + 'An error occurred while trying to save caret position', + err, + ); + } + + // If a mask is present, delay the update to allow mask to update first. + if (element.mask) { + setTimeout(() => { + return this.updateValue( + null, + { + modified: this.component.type !== 'hidden', + }, + index, + ); + }, 1); + } else { + return this.updateValue( + null, + { + modified: this.component.type !== 'hidden', + }, + index, + ); + } }); - } - else { - applyMask(); - } - } - } - - onSelectMaskHandler(event) { - this.updateMask(event.target.maskInput, this.getMaskPattern(event.target.value)); - } - getMaskPattern(maskName) { - if (!this.multiMasks) { - this.multiMasks = {}; + if (!this.attachMultiMask(index)) { + const applyMask = () => { + this.setInputMask(element); + + const valueMask = this.component.inputMask; + const displayMask = this.component.displayMask; + + if ( + valueMask && + displayMask && + displayMask !== valueMask && + this.refs.valueMaskInput + ) { + this.setInputMask(this.refs.valueMaskInput, valueMask); + } + }; + + if (this.inputInfo.changeEvent === 'blur') { + this.addEventListener( + element, + this.inputInfo.changeEvent, + () => { + applyMask(); + this.dataValue = this.refs.input[0].value; + if (this.checkComponentValidity()) { + this.updateComponentValue(this.refs.input[0].value); + } + }, + ); + } else { + applyMask(); + } + } } - if (this.multiMasks[maskName]) { - return this.multiMasks[maskName]; + + onSelectMaskHandler(event) { + this.updateMask( + event.target.maskInput, + this.getMaskPattern(event.target.value), + ); } - const mask = this.component.inputMasks.find(inputMask => inputMask.label === maskName); - this.multiMasks[maskName] = mask ? mask.mask : this.component.inputMasks[0].mask; - return this.multiMasks[maskName]; - } - - attachMultiMask(index) { - if (!(this.isMultipleMasksField && this.component.inputMasks.length && this.refs.input.length)) { - return false; + + getMaskPattern(maskName) { + if (!this.multiMasks) { + this.multiMasks = {}; + } + if (this.multiMasks[maskName]) { + return this.multiMasks[maskName]; + } + const mask = this.component.inputMasks.find( + (inputMask) => inputMask.label === maskName, + ); + this.multiMasks[maskName] = mask + ? mask.mask + : this.component.inputMasks[0].mask; + return this.multiMasks[maskName]; } - const maskSelect = this.refs.select[index]; - maskSelect.onchange = this.onSelectMaskHandler.bind(this); - maskSelect.maskInput = this.refs.mask[index]; - this.setInputMask(maskSelect.maskInput, this.component.inputMasks[0].mask); - return true; - } + attachMultiMask(index) { + if ( + !( + this.isMultipleMasksField && + this.component.inputMasks.length && + this.refs.input.length + ) + ) { + return false; + } - updateMask(input, mask) { - if (!mask) { - return; - } - this.setInputMask(input, mask, !this.component.placeholder); - this.updateValue(); - } - - /** - * Adds a new empty value to the data array. - */ - addNewValue(value) { - if (value === undefined) { - value = this.component.defaultValue ? - this.component.defaultValue : this.emptyValue; - // If default is an empty aray, default back to empty value. - if (Array.isArray(value) && value.length === 0) { - value = this.emptyValue; - } - } - let dataValue = this.dataValue || []; - if (!Array.isArray(dataValue)) { - dataValue = [dataValue]; + const maskSelect = this.refs.select[index]; + maskSelect.onchange = this.onSelectMaskHandler.bind(this); + maskSelect.maskInput = this.refs.mask[index]; + this.setInputMask( + maskSelect.maskInput, + this.component.inputMasks[0].mask, + ); + return true; } - if (Array.isArray(value)) { - dataValue = dataValue.concat(value); - } - else { - dataValue.push(value); + updateMask(input, mask) { + if (!mask) { + return; + } + this.setInputMask(input, mask, !this.component.placeholder); + this.updateValue(); } - this.dataValue = dataValue; - } - - /** - * Adds a new empty value to the data array, and add a new row to contain it. - */ - addValue() { - this.addNewValue(); - this.redraw(); - this.checkConditions(); - if (!this.isEmpty(this.dataValue)) { - this.restoreValue(); + + /** + * Adds a new empty value to the data array. + */ + addNewValue(value) { + if (value === undefined) { + value = this.component.defaultValue + ? this.component.defaultValue + : this.emptyValue; + // If default is an empty aray, default back to empty value. + if (Array.isArray(value) && value.length === 0) { + value = this.emptyValue; + } + } + let dataValue = this.dataValue || []; + if (!Array.isArray(dataValue)) { + dataValue = [dataValue]; + } + + if (Array.isArray(value)) { + dataValue = dataValue.concat(value); + } else { + dataValue.push(value); + } + this.dataValue = dataValue; } - if (this.root) { - this.root.onChange(); + + /** + * Adds a new empty value to the data array, and add a new row to contain it. + */ + addValue() { + this.addNewValue(); + this.redraw(); + this.checkConditions(); + if (!this.isEmpty(this.dataValue)) { + this.restoreValue(); + } + if (this.root) { + this.root.onChange(); + } } - } } diff --git a/src/components/_classes/nested/NestedComponent.form.js b/src/components/_classes/nested/NestedComponent.form.js index 5795262cfb..dacee8d347 100644 --- a/src/components/_classes/nested/NestedComponent.form.js +++ b/src/components/_classes/nested/NestedComponent.form.js @@ -1,14 +1,17 @@ import Components from '../../Components'; -export default function(...extend) { - return Components.baseEditForm([ - { - key: 'data', - ignore: true - }, - { - key: 'validation', - ignore: true - } - ], ...extend); +export default function (...extend) { + return Components.baseEditForm( + [ + { + key: 'data', + ignore: true, + }, + { + key: 'validation', + ignore: true, + }, + ], + ...extend, + ); } diff --git a/src/components/_classes/nested/NestedComponent.js b/src/components/_classes/nested/NestedComponent.js index 5bff5686e0..8a813f49b3 100644 --- a/src/components/_classes/nested/NestedComponent.js +++ b/src/components/_classes/nested/NestedComponent.js @@ -2,794 +2,878 @@ import _ from 'lodash'; import Field from '../field/Field'; import Components from '../../Components'; -import { getArrayFromComponentPath, getStringFromComponentPath, getRandomComponentId } from '../../../utils/utils'; +import { + getArrayFromComponentPath, + getStringFromComponentPath, + getRandomComponentId, +} from '../../../utils/utils'; export default class NestedComponent extends Field { - static schema(...extend) { - return Field.schema({ - tree: false, - lazyLoad: false, - }, ...extend); - } - - constructor(component, options, data) { - super(component, options, data); - this.type = 'components'; - this._collapsed = !!this.component.collapsed; - } - - get defaultSchema() { - return NestedComponent.schema(); - } - - get schema() { - const schema = super.schema; - const components = _.uniqBy(this.getComponents(), 'component.key'); - schema.components = _.map(components, 'schema'); - return schema; - } - - get collapsed() { - return this._collapsed; - } - - collapse(value) { - const promise = this.redraw(); - if (!value) { - this.checkValidity(this.data, !this.pristine, null, this.pristine); - } - return promise; - } - - set collapsed(value) { - this._collapsed = value; - this.collapse(value); - } - - set visible(value) { - // DO NOT CALL super here. There is an issue where clearOnHide was getting triggered with - // subcomponents because the "parentVisible" flag was set to false when it should really be - // set to true. - const visibilityChanged = this._visible !== value; - this._visible = value; - const isVisible = this.visible; - const forceShow = this.shouldForceShow(); - const forceHide = this.shouldForceHide(); - this.components.forEach(component => { - // Set the parent visibility first since we may have nested components within nested components - // and they need to be able to determine their visibility based on the parent visibility. - component.parentVisible = isVisible; - - const conditionallyVisible = component.conditionallyVisible(); - if (forceShow || conditionallyVisible) { - component.visible = true; - } - else if (forceHide || !isVisible || !conditionallyVisible) { - component.visible = false; - } - // If hiding a nested component, clear all errors below. - if (!component.visible) { - component.error = ''; - } - }); - if (visibilityChanged) { - this.clearOnHide(); - this.redraw(); - } - } - - get visible() { - return super.visible; - } - - set parentVisible(value) { - super.parentVisible = value; - this.components.forEach(component => component.parentVisible = this.visible); - } - - get parentVisible() { - return super.parentVisible; - } - - get disabled() { - return super.disabled; - } - - set disabled(disabled) { - super.disabled = disabled; - this.components.forEach((component) => component.parentDisabled = disabled); - } - - set parentDisabled(value) { - super.parentDisabled = value; - this.components.forEach(component => { - component.parentDisabled = this.disabled; - }); - } - - get parentDisabled() { - return super.parentDisabled; - } - - get ready() { - return Promise.all(this.getComponents().map(component => component.ready)); - } - - get currentForm() { - return super.currentForm; - } - - set currentForm(instance) { - super.currentForm = instance; - this.getComponents().forEach(component => { - component.currentForm = instance; - }); - } - - get rowIndex() { - return this._rowIndex; - } - - set rowIndex(value) { - this._rowIndex = value; - this.eachComponent((component) => { - component.rowIndex = value; - }); - } - - componentContext() { - return this._data; - } - - get data() { - return this._data; - } - - set data(value) { - this._data = value; - this.eachComponent((component) => { - component.data = this.componentContext(component); - }); - } - - getComponents() { - return this.components || []; - } - - /** - * Perform a deep iteration over every component, including those - * within other container based components. - * - * @param {function} fn - Called for every component. - */ - everyComponent(fn, options) { - const components = this.getComponents(); - _.each(components, (component, index) => { - if (fn(component, components, index) === false) { - return false; - } - - if (typeof component.everyComponent === 'function') { - if (component.everyComponent(fn, options) === false) { - return false; + static schema(...extend) { + return Field.schema( + { + tree: false, + lazyLoad: false, + }, + ...extend, + ); + } + + constructor(component, options, data) { + super(component, options, data); + this.type = 'components'; + this._collapsed = !!this.component.collapsed; + } + + get defaultSchema() { + return NestedComponent.schema(); + } + + get schema() { + const schema = super.schema; + const components = _.uniqBy(this.getComponents(), 'component.key'); + schema.components = _.map(components, 'schema'); + return schema; + } + + get collapsed() { + return this._collapsed; + } + + collapse(value) { + const promise = this.redraw(); + if (!value) { + this.checkValidity(this.data, !this.pristine, null, this.pristine); + } + return promise; + } + + set collapsed(value) { + this._collapsed = value; + this.collapse(value); + } + + set visible(value) { + // DO NOT CALL super here. There is an issue where clearOnHide was getting triggered with + // subcomponents because the "parentVisible" flag was set to false when it should really be + // set to true. + const visibilityChanged = this._visible !== value; + this._visible = value; + const isVisible = this.visible; + const forceShow = this.shouldForceShow(); + const forceHide = this.shouldForceHide(); + this.components.forEach((component) => { + // Set the parent visibility first since we may have nested components within nested components + // and they need to be able to determine their visibility based on the parent visibility. + component.parentVisible = isVisible; + + const conditionallyVisible = component.conditionallyVisible(); + if (forceShow || conditionallyVisible) { + component.visible = true; + } else if (forceHide || !isVisible || !conditionallyVisible) { + component.visible = false; + } + // If hiding a nested component, clear all errors below. + if (!component.visible) { + component.error = ''; + } + }); + if (visibilityChanged) { + this.clearOnHide(); + this.redraw(); + } + } + + get visible() { + return super.visible; + } + + set parentVisible(value) { + super.parentVisible = value; + this.components.forEach( + (component) => (component.parentVisible = this.visible), + ); + } + + get parentVisible() { + return super.parentVisible; + } + + get disabled() { + return super.disabled; + } + + set disabled(disabled) { + super.disabled = disabled; + this.components.forEach( + (component) => (component.parentDisabled = disabled), + ); + } + + set parentDisabled(value) { + super.parentDisabled = value; + this.components.forEach((component) => { + component.parentDisabled = this.disabled; + }); + } + + get parentDisabled() { + return super.parentDisabled; + } + + get ready() { + return Promise.all( + this.getComponents().map((component) => component.ready), + ); + } + + get currentForm() { + return super.currentForm; + } + + set currentForm(instance) { + super.currentForm = instance; + this.getComponents().forEach((component) => { + component.currentForm = instance; + }); + } + + get rowIndex() { + return this._rowIndex; + } + + set rowIndex(value) { + this._rowIndex = value; + this.eachComponent((component) => { + component.rowIndex = value; + }); + } + + componentContext() { + return this._data; + } + + get data() { + return this._data; + } + + set data(value) { + this._data = value; + this.eachComponent((component) => { + component.data = this.componentContext(component); + }); + } + + getComponents() { + return this.components || []; + } + + /** + * Perform a deep iteration over every component, including those + * within other container based components. + * + * @param {function} fn - Called for every component. + */ + everyComponent(fn, options) { + const components = this.getComponents(); + _.each(components, (component, index) => { + if (fn(component, components, index) === false) { + return false; + } + + if (typeof component.everyComponent === 'function') { + if (component.everyComponent(fn, options) === false) { + return false; + } + } + }); + } + + hasComponent(component) { + let result = false; + + this.everyComponent((comp) => { + if (comp === component) { + result = true; + return false; + } + }); + + return result; + } + + flattenComponents() { + const result = {}; + + this.everyComponent((component) => { + result[component.component.flattenAs || component.key] = component; + }); + + return result; + } + + /** + * Perform an iteration over each component within this container component. + * + * @param {function} fn - Called for each component + */ + eachComponent(fn) { + _.each(this.getComponents(), (component, index) => { + if (fn(component, index) === false) { + return false; + } + }); + } + + /** + * Returns a component provided a key. This performs a deep search within the + * component tree. + * + * @param {string} key - The key of the component to retrieve. + * @param {function} fn - Called with the component once found. + * @return {Object} - The component that is located. + */ + getComponent(path, fn, originalPath) { + originalPath = originalPath || getStringFromComponentPath(path); + path = getArrayFromComponentPath(path); + const pathStr = originalPath; + const newPath = _.clone(path); + let key = newPath.shift(); + const remainingPath = newPath; + let comp = null; + let possibleComp = null; + + if (_.isNumber(key)) { + key = remainingPath.shift(); + } + + if (!_.isString(key)) { + return comp; + } + + this.everyComponent((component, components) => { + const matchPath = + component.hasInput && component.path + ? pathStr.includes(component.path) + : true; + if (component.component.key === key) { + possibleComp = component; + if (matchPath) { + comp = component; + if ( + remainingPath.length > 0 && + 'getComponent' in component + ) { + comp = component.getComponent( + remainingPath, + fn, + originalPath, + ); + } else if (fn) { + fn(component, components); + } + return false; + } + } + }); + + if (!comp) { + comp = possibleComp; + } + + return comp; + } + + /** + * Return a component provided the Id of the component. + * + * @param {string} id - The Id of the component. + * @param {function} fn - Called with the component once it is retrieved. + * @return {Object} - The component retrieved. + */ + getComponentById(id, fn) { + let comp = null; + this.everyComponent((component, components) => { + if (component.id === id) { + comp = component; + if (fn) { + fn(component, components); + } + return false; + } + }); + return comp; + } + + /** + * Return a path of component's value. + * + * @param {Object} component - The component instance. + * @return {string} - The component's value path. + */ + calculateComponentPath(component) { + let path = ''; + if (component.component.key) { + let thisPath = this; + while (thisPath && !thisPath.allowData && thisPath.parent) { + thisPath = thisPath.parent; + } + const rowIndex = component.row + ? `[${Number.parseInt(component.row)}]` + : ''; + path = thisPath.path ? `${thisPath.path}${rowIndex}.` : ''; + path += + component._parentPath && + component.component.shouldIncludeSubFormPath + ? component._parentPath + : ''; + path += component.component.key; + return path; + } + } + + /** + * Create a new component and add it to the components array. + * + * @param component + * @param data + */ + createComponent(component, options, data, before, replacedComp) { + if (!component) { + return; + } + options = options || this.options; + data = data || this.data; + options.parent = this; + options.parentVisible = this.visible; + options.root = options?.root || this.root || this; + options.localRoot = this.localRoot; + options.skipInit = true; + if (!(options.display === 'pdf' && this.builderMode)) { + component.id = getRandomComponentId(); + } + if (!this.isInputComponent && this.component.shouldIncludeSubFormPath) { + component.shouldIncludeSubFormPath = true; + } + const comp = Components.create(component, options, data, true); + + const path = this.calculateComponentPath(comp); + if (path) { + comp.path = path; + } + comp.init(); + if (component.internal) { + return comp; + } + + if (before) { + const index = _.findIndex(this.components, { id: before.id }); + if (index !== -1) { + this.components.splice(index, 0, comp); + } else { + this.components.push(comp); + } + } else if (replacedComp) { + const index = _.findIndex(this.components, { id: replacedComp.id }); + if (index !== -1) { + this.components[index] = comp; + } else { + this.components.push(comp); + } + } else { + this.components.push(comp); + } + return comp; + } + + getContainer() { + return this.element; + } + + get componentComponents() { + return this.component.components || []; + } + + get nestedKey() { + return `nested-${this.key}`; + } + + get templateName() { + return 'container'; + } + + init() { + this.components = this.components || []; + this.addComponents(); + return super.init(); + } + + /** + * + * @param element + * @param data + */ + addComponents(data, options) { + data = data || this.data; + this.components = this.components || []; + options = options || this.options; + if (options.components) { + this.components = options.components; + } else { + const components = + this.hook('addComponents', this.componentComponents, this) || + []; + components.forEach((component) => + this.addComponent(component, data), + ); + } + } + + /** + * Add a new component to the components array. + * + * @param {Object} component - The component JSON schema to add. + * @param {Object} data - The submission data object to house the data for this component. + * @param {HTMLElement} before - A DOM element to insert this element before. + * @return {Component} - The created component instance. + */ + addComponent(component, data, before, noAdd) { + data = data || this.data; + this.components = this.components || []; + if (this.options.parentPath) { + component.shouldIncludeSubFormPath = true; + } + component = this.hook('addComponent', component, data, before, noAdd); + const comp = this.createComponent( + component, + this.options, + data, + before ? before : null, + ); + if (noAdd) { + return comp; + } + return comp; + } + + beforeFocus() { + if (this.parent && 'beforeFocus' in this.parent) { + this.parent.beforeFocus(this); + } + } + + render(children) { + // If already rendering, don't re-render. + return super.render( + children || + this.renderTemplate(this.templateName, { + children: !this.visible ? '' : this.renderComponents(), + nestedKey: this.nestedKey, + collapsed: this.options.pdf ? false : this.collapsed, + }), + ); + } + + renderComponents(components) { + components = components || this.getComponents(); + const children = components.map((component) => component.render()); + return this.renderTemplate('components', { + children, + components, + }); + } + + attach(element) { + const superPromise = super.attach(element); + + this.loadRefs(element, { + header: 'single', + collapsed: this.collapsed, + [this.nestedKey]: 'single', + }); + + let childPromise = Promise.resolve(); + if (this.refs[this.nestedKey]) { + childPromise = this.attachComponents(this.refs[this.nestedKey]); + } + + if (!this.visible) { + this.attachComponentsLogic(); + } + + if (this.component.collapsible && this.refs.header) { + this.addEventListener(this.refs.header, 'click', () => { + this.collapsed = !this.collapsed; + }); + this.addEventListener(this.refs.header, 'keydown', (e) => { + if (e.keyCode === 13 || e.keyCode === 32) { + e.preventDefault(); + this.collapsed = !this.collapsed; + } + }); + } + + return Promise.all([superPromise, childPromise]); + } + + attachComponentsLogic(components) { + components = components || this.components; + + _.each(components, (comp) => { + comp.attachLogic(); + + if (_.isFunction(comp.attachComponentsLogic)) { + comp.attachComponentsLogic(); + } + }); + } + + attachComponents(element, components, container) { + components = components || this.components; + container = container || this.component.components; + + element = this.hook( + 'attachComponents', + element, + components, + container, + this, + ); + if (!element) { + // Return a non-resolving promise. + return new Promise(() => {}); + } + + let index = 0; + const promises = []; + Array.prototype.slice.call(element.children).forEach((child) => { + if (!child.getAttribute('data-noattach') && components[index]) { + promises.push(components[index].attach(child)); + index++; + } + }); + return Promise.all(promises); + } + + /** + * Remove a component from the components array. + * + * @param {Component} component - The component to remove from the components. + * @param {Array} components - An array of components to remove this component from. + */ + removeComponent(component, components, all = false) { + components = components || this.components; + component.destroy(all); + _.remove(components, { id: component.id }); + } + + /** + * Removes a component provided the API key of that component. + * + * @param {string} key - The API key of the component to remove. + * @param {function} fn - Called once the component is removed. + * @return {null} + */ + removeComponentByKey(key, fn) { + const comp = this.getComponent(key, (component, components) => { + this.removeComponent(component, components); + if (fn) { + fn(component, components); + } + }); + if (!comp) { + if (fn) { + fn(null); + } + return null; + } + } + + /** + * Removes a component provided the Id of the component. + * + * @param {string} id - The Id of the component to remove. + * @param {function} fn - Called when the component is removed. + * @return {null} + */ + removeComponentById(id, fn) { + const comp = this.getComponentById(id, (component, components) => { + this.removeComponent(component, components); + if (fn) { + fn(component, components); + } + }); + if (!comp) { + if (fn) { + fn(null); + } + return null; } - } - }); - } - - hasComponent(component) { - let result = false; - - this.everyComponent((comp) => { - if (comp === component) { - result = true; - return false; - } - }); - - return result; - } - - flattenComponents() { - const result = {}; - - this.everyComponent((component) => { - result[component.component.flattenAs || component.key] = component; - }); - - return result; - } - - /** - * Perform an iteration over each component within this container component. - * - * @param {function} fn - Called for each component - */ - eachComponent(fn) { - _.each(this.getComponents(), (component, index) => { - if (fn(component, index) === false) { - return false; - } - }); - } - - /** - * Returns a component provided a key. This performs a deep search within the - * component tree. - * - * @param {string} key - The key of the component to retrieve. - * @param {function} fn - Called with the component once found. - * @return {Object} - The component that is located. - */ - getComponent(path, fn, originalPath) { - originalPath = originalPath || getStringFromComponentPath(path); - path = getArrayFromComponentPath(path); - const pathStr = originalPath; - const newPath = _.clone(path); - let key = newPath.shift(); - const remainingPath = newPath; - let comp = null; - let possibleComp = null; - - if (_.isNumber(key)) { - key = remainingPath.shift(); - } - - if (!_.isString(key)) { - return comp; - } - - this.everyComponent((component, components) => { - const matchPath = component.hasInput && component.path ? pathStr.includes(component.path) : true; - if (component.component.key === key) { - possibleComp = component; - if (matchPath) { - comp = component; - if (remainingPath.length > 0 && 'getComponent' in component) { - comp = component.getComponent(remainingPath, fn, originalPath); - } - else if (fn) { - fn(component, components); - } - return false; + } + + updateValue(value, flags = {}) { + return this.components.reduce( + (changed, comp) => { + return comp.updateValue(null, flags) || changed; + }, + super.updateValue(value, flags), + ); + } + + shouldSkipValidation(data, dirty, row) { + // Nested components with no input should not be validated. + if (!this.component.input) { + return true; + } else { + return super.shouldSkipValidation(data, dirty, row); } - } - }); - - if (!comp) { - comp = possibleComp; - } - - return comp; - } - - /** - * Return a component provided the Id of the component. - * - * @param {string} id - The Id of the component. - * @param {function} fn - Called with the component once it is retrieved. - * @return {Object} - The component retrieved. - */ - getComponentById(id, fn) { - let comp = null; - this.everyComponent((component, components) => { - if (component.id === id) { - comp = component; - if (fn) { - fn(component, components); + } + + checkData(data, flags, row, components) { + if (this.builderMode) { + return true; + } + data = data || this.rootValue; + flags = flags || {}; + row = row || this.data; + components = + components && _.isArray(components) + ? components + : this.getComponents(); + const isValid = components.reduce( + (valid, comp) => { + return comp.checkData(data, { ...flags }, row) && valid; + }, + super.checkData(data, { ...flags }, row), + ); + + this.checkModal(isValid, this.isDirty); + return isValid; + } + + checkConditions(data, flags, row) { + // check conditions of parent component first, because it may influence on visibility of it's children + const check = super.checkConditions(data, flags, row); + //row data of parent component not always corresponds to row of nested components, use comp.data as row data for children instead + this.getComponents().forEach((comp) => + comp.checkConditions(data, flags, comp.data), + ); + return check; + } + + clearOnHide(show) { + super.clearOnHide(show); + if (this.component.clearOnHide) { + if ( + this.allowData && + !this.hasValue() && + !(this.options.server && !this.visible) + ) { + this.dataValue = this.defaultValue; + } + if (this.hasValue()) { + this.restoreComponentsContext(); + } } - return false; - } - }); - return comp; - } - - /** - * Return a path of component's value. - * - * @param {Object} component - The component instance. - * @return {string} - The component's value path. - */ - calculateComponentPath(component) { - let path = ''; - if (component.component.key) { - let thisPath = this; - while (thisPath && !thisPath.allowData && thisPath.parent) { - thisPath = thisPath.parent; - } - const rowIndex = component.row ? `[${Number.parseInt(component.row)}]` : ''; - path = thisPath.path ? `${thisPath.path}${rowIndex}.` : ''; - path += component._parentPath && component.component.shouldIncludeSubFormPath ? component._parentPath : ''; - path += component.component.key; - return path; - } - } - - /** - * Create a new component and add it to the components array. - * - * @param component - * @param data - */ - createComponent(component, options, data, before, replacedComp) { - if (!component) { - return; - } - options = options || this.options; - data = data || this.data; - options.parent = this; - options.parentVisible = this.visible; - options.root = options?.root || this.root || this; - options.localRoot = this.localRoot; - options.skipInit = true; - if (!(options.display === 'pdf' && this.builderMode)) { - component.id = getRandomComponentId(); - } - if (!this.isInputComponent && this.component.shouldIncludeSubFormPath) { - component.shouldIncludeSubFormPath = true; - } - const comp = Components.create(component, options, data, true); - - const path = this.calculateComponentPath(comp); - if (path) { - comp.path = path; - } - comp.init(); - if (component.internal) { - return comp; - } - - if (before) { - const index = _.findIndex(this.components, { id: before.id }); - if (index !== -1) { - this.components.splice(index, 0, comp); - } - else { - this.components.push(comp); - } - } - else if (replacedComp) { - const index = _.findIndex(this.components, { id: replacedComp.id }); - if (index !== -1) { - this.components[index] = comp; - } - else { - this.components.push(comp); - } - } - else { - this.components.push(comp); - } - return comp; - } - - getContainer() { - return this.element; - } - - get componentComponents() { - return this.component.components || []; - } - - get nestedKey() { - return `nested-${this.key}`; - } - - get templateName() { - return 'container'; - } - - init() { - this.components = this.components || []; - this.addComponents(); - return super.init(); - } - - /** - * - * @param element - * @param data - */ - addComponents(data, options) { - data = data || this.data; - this.components = this.components || []; - options = options || this.options; - if (options.components) { - this.components = options.components; - } - else { - const components = this.hook('addComponents', this.componentComponents, this) || []; - components.forEach((component) => this.addComponent(component, data)); - } - } - - /** - * Add a new component to the components array. - * - * @param {Object} component - The component JSON schema to add. - * @param {Object} data - The submission data object to house the data for this component. - * @param {HTMLElement} before - A DOM element to insert this element before. - * @return {Component} - The created component instance. - */ - addComponent(component, data, before, noAdd) { - data = data || this.data; - this.components = this.components || []; - if (this.options.parentPath) { - component.shouldIncludeSubFormPath = true; - } - component = this.hook('addComponent', component, data, before, noAdd); - const comp = this.createComponent(component, this.options, data, before ? before : null); - if (noAdd) { - return comp; - } - return comp; - } - - beforeFocus() { - if (this.parent && 'beforeFocus' in this.parent) { - this.parent.beforeFocus(this); - } - } - - render(children) { - // If already rendering, don't re-render. - return super.render(children || this.renderTemplate(this.templateName, { - children: !this.visible ? '' : this.renderComponents(), - nestedKey: this.nestedKey, - collapsed: this.options.pdf ? false : this.collapsed, - })); - } - - renderComponents(components) { - components = components || this.getComponents(); - const children = components.map(component => component.render()); - return this.renderTemplate('components', { - children, - components, - }); - } - - attach(element) { - const superPromise = super.attach(element); - - this.loadRefs(element, { - header: 'single', - collapsed: this.collapsed, - [this.nestedKey]: 'single', - }); - - let childPromise = Promise.resolve(); - if (this.refs[this.nestedKey]) { - childPromise = this.attachComponents(this.refs[this.nestedKey]); - } - - if (!this.visible) { - this.attachComponentsLogic(); - } - - if (this.component.collapsible && this.refs.header) { - this.addEventListener(this.refs.header, 'click', () => { - this.collapsed = !this.collapsed; - }); - this.addEventListener(this.refs.header, 'keydown', (e) => { - if (e.keyCode === 13 || e.keyCode === 32) { - e.preventDefault(); - this.collapsed = !this.collapsed; + this.getComponents().forEach((component) => + component.clearOnHide(show), + ); + } + + restoreComponentsContext() { + this.getComponents().forEach( + (component) => (component.data = this.dataValue), + ); + } + + /** + * Allow components to hook into the next page trigger to perform their own logic. + * + * @return {*} + */ + beforePage(next) { + return Promise.all( + this.getComponents().map((comp) => comp.beforePage(next)), + ); + } + + /** + * Allow components to hook into the submission to provide their own async data. + * + * @return {*} + */ + beforeSubmit() { + return Promise.all( + this.getComponents().map((comp) => comp.beforeSubmit()), + ); + } + + calculateValue(data, flags, row) { + // Do not iterate into children and calculateValues if this nested component is conditionally hidden. + if (!this.conditionallyVisible()) { + return false; } - }); - } - - return Promise.all([ - superPromise, - childPromise, - ]); - } - - attachComponentsLogic(components) { - components = components || this.components; - - _.each(components, (comp) => { - comp.attachLogic(); - - if (_.isFunction(comp.attachComponentsLogic)) { - comp.attachComponentsLogic(); - } - }); - } - - attachComponents(element, components, container) { - components = components || this.components; - container = container || this.component.components; - - element = this.hook('attachComponents', element, components, container, this); - if (!element) { - // Return a non-resolving promise. - return (new Promise(() => {})); - } - - let index = 0; - const promises = []; - Array.prototype.slice.call(element.children).forEach(child => { - if (!child.getAttribute('data-noattach') && components[index]) { - promises.push(components[index].attach(child)); - index++; - } - }); - return Promise.all(promises); - } - - /** - * Remove a component from the components array. - * - * @param {Component} component - The component to remove from the components. - * @param {Array} components - An array of components to remove this component from. - */ - removeComponent(component, components, all = false) { - components = components || this.components; - component.destroy(all); - _.remove(components, { id: component.id }); - } - - /** - * Removes a component provided the API key of that component. - * - * @param {string} key - The API key of the component to remove. - * @param {function} fn - Called once the component is removed. - * @return {null} - */ - removeComponentByKey(key, fn) { - const comp = this.getComponent(key, (component, components) => { - this.removeComponent(component, components); - if (fn) { - fn(component, components); - } - }); - if (!comp) { - if (fn) { - fn(null); - } - return null; - } - } - - /** - * Removes a component provided the Id of the component. - * - * @param {string} id - The Id of the component to remove. - * @param {function} fn - Called when the component is removed. - * @return {null} - */ - removeComponentById(id, fn) { - const comp = this.getComponentById(id, (component, components) => { - this.removeComponent(component, components); - if (fn) { - fn(component, components); - } - }); - if (!comp) { - if (fn) { - fn(null); - } - return null; - } - } - - updateValue(value, flags = {}) { - return this.components.reduce((changed, comp) => { - return comp.updateValue(null, flags) || changed; - }, super.updateValue(value, flags)); - } - - shouldSkipValidation(data, dirty, row) { - // Nested components with no input should not be validated. - if (!this.component.input) { - return true; - } - else { - return super.shouldSkipValidation(data, dirty, row); - } - } - - checkData(data, flags, row, components) { - if (this.builderMode) { - return true; - } - data = data || this.rootValue; - flags = flags || {}; - row = row || this.data; - components = components && _.isArray(components) ? components : this.getComponents(); - const isValid = components.reduce((valid, comp) => { - return comp.checkData(data, { ...flags }, row) && valid; - }, super.checkData(data, { ...flags }, row)); - - this.checkModal(isValid, this.isDirty); - return isValid; - } - - checkConditions(data, flags, row) { - // check conditions of parent component first, because it may influence on visibility of it's children - const check = super.checkConditions(data, flags, row); - //row data of parent component not always corresponds to row of nested components, use comp.data as row data for children instead - this.getComponents().forEach(comp => comp.checkConditions(data, flags, comp.data)); - return check; - } - - clearOnHide(show) { - super.clearOnHide(show); - if (this.component.clearOnHide) { - if (this.allowData && !this.hasValue() && !(this.options.server && !this.visible)) { - this.dataValue = this.defaultValue; - } - if (this.hasValue()) { - this.restoreComponentsContext(); - } - } - this.getComponents().forEach(component => component.clearOnHide(show)); - } - - restoreComponentsContext() { - this.getComponents().forEach((component) => component.data = this.dataValue); - } - - /** - * Allow components to hook into the next page trigger to perform their own logic. - * - * @return {*} - */ - beforePage(next) { - return Promise.all(this.getComponents().map((comp) => comp.beforePage(next))); - } - - /** - * Allow components to hook into the submission to provide their own async data. - * - * @return {*} - */ - beforeSubmit() { - return Promise.all(this.getComponents().map((comp) => comp.beforeSubmit())); - } - - calculateValue(data, flags, row) { - // Do not iterate into children and calculateValues if this nested component is conditionally hidden. - if (!this.conditionallyVisible()) { - return false; - } - return this.getComponents().reduce( - (changed, comp) => comp.calculateValue(data, flags, row) || changed, - super.calculateValue(data, flags, row) - ); - } - - isLastPage() { - return this.pages.length - 1 === this.page; - } - - isValid(data, dirty) { - return this.getComponents().reduce( - (valid, comp) => comp.isValid(data, dirty) && valid, - super.isValid(data, dirty) - ); - } - - checkChildComponentsValidity(data, dirty, row, silentCheck, isParentValid) { - return this.getComponents().reduce( - (check, comp) => comp.checkValidity(data, dirty, row, silentCheck) && check, - isParentValid - ); - } - - checkValidity(data, dirty, row, silentCheck) { - if (!this.checkCondition(row, data)) { - this.setCustomValidity(''); - return true; - } - - const isValid = this.checkChildComponentsValidity(data, dirty, row, silentCheck, super.checkValidity(data, dirty, row, silentCheck)); - this.checkModal(isValid, dirty); - return isValid; - } - - checkAsyncValidity(data, dirty, row, silentCheck) { - return this.ready.then(() => { - const promises = [super.checkAsyncValidity(data, dirty, row, silentCheck)]; - this.eachComponent((component) => promises.push(component.checkAsyncValidity(data, dirty, row, silentCheck))); - return Promise.all(promises).then((results) => results.reduce((valid, result) => (valid && result), true)); - }); - } - - setPristine(pristine) { - super.setPristine(pristine); - this.getComponents().forEach((comp) => comp.setPristine(pristine)); - } - - get isPristine() { - return this.pristine && this.getComponents().every((c) => c.isPristine); - } - - get isDirty() { - return this.dirty && this.getComponents().every((c) => c.isDirty); - } - - detach() { - this.components.forEach(component => { - component.detach(); - }); - super.detach(); - } - - clear() { - this.components.forEach(component => { - component.clear(); - }); - super.clear(); - } - - destroy(all = false) { - this.destroyComponents(all); - super.destroy(all); - } - - destroyComponents(all = false) { - const components = this.getComponents().slice(); - components.forEach((comp) => this.removeComponent(comp, this.components, all)); - this.components = []; - } - - get errors() { - const thisErrors = this.error ? [this.error] : []; - return this.getComponents() - .reduce((errors, comp) => errors.concat(comp.errors || []), thisErrors) - .filter(err => err.level !== 'hidden'); - } - - getValue() { - return this.data; - } - - resetValue() { - super.resetValue(); - this.getComponents().forEach((comp) => comp.resetValue()); - this.setPristine(true); - } - - get dataReady() { - return Promise.all(this.getComponents().map((component) => component.dataReady)); - } - - setNestedValue(component, value, flags = {}) { - component._data = this.componentContext(component); - if (component.type === 'button') { - return false; - } - if (component.type === 'components') { - if (component.tree && component.hasValue(value)) { - return component.setValue(_.get(value, component.key), flags); - } - return component.setValue(value, flags); - } - else if (value && component.hasValue(value)) { - return component.setValue(_.get(value, component.key), flags); - } - else if ((!this.rootPristine || component.visible) && component.shouldAddDefaultValue) { - flags.noValidate = !flags.dirty; - flags.resetValue = true; - return component.setValue(component.defaultValue, flags); - } - } - - setValue(value, flags = {}) { - if (!value) { - return false; - } - if (value.submitAsDraft && !value.submit) { - flags.noValidate = true; - } - return this.getComponents().reduce((changed, component) => { - return this.setNestedValue(component, value, flags, changed) || changed; - }, false); - } - - get lazyLoad() { - return this.component.lazyLoad ?? false; - } + return this.getComponents().reduce( + (changed, comp) => comp.calculateValue(data, flags, row) || changed, + super.calculateValue(data, flags, row), + ); + } + + isLastPage() { + return this.pages.length - 1 === this.page; + } + + isValid(data, dirty) { + return this.getComponents().reduce( + (valid, comp) => comp.isValid(data, dirty) && valid, + super.isValid(data, dirty), + ); + } + + checkChildComponentsValidity(data, dirty, row, silentCheck, isParentValid) { + return this.getComponents().reduce( + (check, comp) => + comp.checkValidity(data, dirty, row, silentCheck) && check, + isParentValid, + ); + } + + checkValidity(data, dirty, row, silentCheck) { + if (!this.checkCondition(row, data)) { + this.setCustomValidity(''); + return true; + } + + const isValid = this.checkChildComponentsValidity( + data, + dirty, + row, + silentCheck, + super.checkValidity(data, dirty, row, silentCheck), + ); + this.checkModal(isValid, dirty); + return isValid; + } + + checkAsyncValidity(data, dirty, row, silentCheck) { + return this.ready.then(() => { + const promises = [ + super.checkAsyncValidity(data, dirty, row, silentCheck), + ]; + this.eachComponent((component) => + promises.push( + component.checkAsyncValidity(data, dirty, row, silentCheck), + ), + ); + return Promise.all(promises).then((results) => + results.reduce((valid, result) => valid && result, true), + ); + }); + } + + setPristine(pristine) { + super.setPristine(pristine); + this.getComponents().forEach((comp) => comp.setPristine(pristine)); + } + + get isPristine() { + return this.pristine && this.getComponents().every((c) => c.isPristine); + } + + get isDirty() { + return this.dirty && this.getComponents().every((c) => c.isDirty); + } + + detach() { + this.components.forEach((component) => { + component.detach(); + }); + super.detach(); + } + + clear() { + this.components.forEach((component) => { + component.clear(); + }); + super.clear(); + } + + destroy(all = false) { + this.destroyComponents(all); + super.destroy(all); + } + + destroyComponents(all = false) { + const components = this.getComponents().slice(); + components.forEach((comp) => + this.removeComponent(comp, this.components, all), + ); + this.components = []; + } + + get errors() { + const thisErrors = this.error ? [this.error] : []; + return this.getComponents() + .reduce( + (errors, comp) => errors.concat(comp.errors || []), + thisErrors, + ) + .filter((err) => err.level !== 'hidden'); + } + + getValue() { + return this.data; + } + + resetValue() { + super.resetValue(); + this.getComponents().forEach((comp) => comp.resetValue()); + this.setPristine(true); + } + + get dataReady() { + return Promise.all( + this.getComponents().map((component) => component.dataReady), + ); + } + + setNestedValue(component, value, flags = {}) { + component._data = this.componentContext(component); + if (component.type === 'button') { + return false; + } + if (component.type === 'components') { + if (component.tree && component.hasValue(value)) { + return component.setValue(_.get(value, component.key), flags); + } + return component.setValue(value, flags); + } else if (value && component.hasValue(value)) { + return component.setValue(_.get(value, component.key), flags); + } else if ( + (!this.rootPristine || component.visible) && + component.shouldAddDefaultValue + ) { + flags.noValidate = !flags.dirty; + flags.resetValue = true; + return component.setValue(component.defaultValue, flags); + } + } + + setValue(value, flags = {}) { + if (!value) { + return false; + } + if (value.submitAsDraft && !value.submit) { + flags.noValidate = true; + } + return this.getComponents().reduce((changed, component) => { + return ( + this.setNestedValue(component, value, flags, changed) || changed + ); + }, false); + } + + get lazyLoad() { + return this.component.lazyLoad ?? false; + } } diff --git a/src/components/_classes/nested/NestedComponent.unit.js b/src/components/_classes/nested/NestedComponent.unit.js index 18b85bc31d..300cfab5bc 100644 --- a/src/components/_classes/nested/NestedComponent.unit.js +++ b/src/components/_classes/nested/NestedComponent.unit.js @@ -10,326 +10,362 @@ import _map from 'lodash/map'; import Webform from '../../../Webform'; let component = null; -describe('NestedComponent class', function() { - it('Should create a new NestedComponent class', function() { - return Harness.testCreate(NestedComponent, { - // key: 'nested', - components: [ - { - type: 'textfield', - key: 'firstName', - input: true - }, - { - type: 'textfield', - key: 'lastName', - input: true - } - ] - }).then((_component) => { - component = _component; - Harness.testElements(component, 'input[name="data[firstName]"]', 1); - Harness.testElements(component, 'input[name="data[lastName]"]', 1); +describe('NestedComponent class', function () { + it('Should create a new NestedComponent class', function () { + return Harness.testCreate(NestedComponent, { + // key: 'nested', + components: [ + { + type: 'textfield', + key: 'firstName', + input: true, + }, + { + type: 'textfield', + key: 'lastName', + input: true, + }, + ], + }).then((_component) => { + component = _component; + Harness.testElements(component, 'input[name="data[firstName]"]', 1); + Harness.testElements(component, 'input[name="data[lastName]"]', 1); + }); }); - }); - it('Should be able to add new components', function(done) { - component.addComponent({ - type: 'email', - key: 'email', - input: true + it('Should be able to add new components', function (done) { + component.addComponent({ + type: 'email', + key: 'email', + input: true, + }); + component.redraw(); + Harness.testElements(component, 'input[name="data[email]"]', 1); + done(); }); - component.redraw(); - Harness.testElements(component, 'input[name="data[email]"]', 1); - done(); - }); - - it('Should be able to set data within the components.', function(done) { - const value = { - firstName: 'Joe', - lastName: 'Smith', - email: 'joe@example.com' - }; - component.setValue(value); - assert.deepEqual(component.getValue(), value); - each(component.components, (component) => { - assert.equal(component.getValue(), value[component.key]); + + it('Should be able to set data within the components.', function (done) { + const value = { + firstName: 'Joe', + lastName: 'Smith', + email: 'joe@example.com', + }; + component.setValue(value); + assert.deepEqual(component.getValue(), value); + each(component.components, (component) => { + assert.equal(component.getValue(), value[component.key]); + }); + done(); }); - done(); - }); - - it('Should create nested visibility elements.', function() { - return Harness.testCreate(NestedComponent, { - components: [ - { - type: 'checkbox', - key: 'showPanel', - label: 'Show Panel', - input: true - }, - { - type: 'panel', - key: 'parent', - title: 'Parent Panel', - conditional: { - json: { var: 'data.showPanel' } - }, - components: [ - { - type: 'checkbox', - key: 'showChild', - label: 'Child 1', - input: true, - conditional: { - json: { var: 'data.showChild' } - } - }, - { - type: 'checkbox', - key: 'forceParent', - label: 'Child 2', - input: true, - conditional: { - json: { var: 'data.forceParent' }, - } - } - ] - } - ] - }).then((comp) => { - // Make sure we built the components tree. - assert.equal(comp.components.length, 2); - assert.equal(comp.components[1].components.length, 2, 'two'); - const data = { - showPanel: true, - showChild: false, - forceParent: false - }; - - comp.setValue(data); - comp.checkConditions(data); - assert.equal(comp.components[1]._visible, true); - assert.equal(comp.components[1].components[0]._visible, false); - assert.equal(comp.components[1].components[1]._visible, false); - - data.showChild = true; - comp.setValue(data); - comp.checkConditions(data); - assert.equal(comp.components[1]._visible, true); - assert.equal(comp.components[1].components[0]._visible, true); - assert.equal(comp.components[1].components[1]._visible, false); - - data.showPanel = false; - comp.setValue(data); - comp.checkConditions(data); - assert.equal(comp.components[1]._visible, false); - assert.equal(comp.components[1].components[0]._visible, true); - assert.equal(comp.components[1].components[1]._visible, false); - - // overrideParent is depricated. - data.forceParent = true; - comp.setValue(data); - comp.checkConditions(data); - assert.equal(comp.components[1]._visible, false); - assert.equal(comp.components[1].components[0]._visible, true); - assert.equal(comp.components[1].components[1]._visible, true); + + it('Should create nested visibility elements.', function () { + return Harness.testCreate(NestedComponent, { + components: [ + { + type: 'checkbox', + key: 'showPanel', + label: 'Show Panel', + input: true, + }, + { + type: 'panel', + key: 'parent', + title: 'Parent Panel', + conditional: { + json: { var: 'data.showPanel' }, + }, + components: [ + { + type: 'checkbox', + key: 'showChild', + label: 'Child 1', + input: true, + conditional: { + json: { var: 'data.showChild' }, + }, + }, + { + type: 'checkbox', + key: 'forceParent', + label: 'Child 2', + input: true, + conditional: { + json: { var: 'data.forceParent' }, + }, + }, + ], + }, + ], + }).then((comp) => { + // Make sure we built the components tree. + assert.equal(comp.components.length, 2); + assert.equal(comp.components[1].components.length, 2, 'two'); + const data = { + showPanel: true, + showChild: false, + forceParent: false, + }; + + comp.setValue(data); + comp.checkConditions(data); + assert.equal(comp.components[1]._visible, true); + assert.equal(comp.components[1].components[0]._visible, false); + assert.equal(comp.components[1].components[1]._visible, false); + + data.showChild = true; + comp.setValue(data); + comp.checkConditions(data); + assert.equal(comp.components[1]._visible, true); + assert.equal(comp.components[1].components[0]._visible, true); + assert.equal(comp.components[1].components[1]._visible, false); + + data.showPanel = false; + comp.setValue(data); + comp.checkConditions(data); + assert.equal(comp.components[1]._visible, false); + assert.equal(comp.components[1].components[0]._visible, true); + assert.equal(comp.components[1].components[1]._visible, false); + + // overrideParent is depricated. + data.forceParent = true; + comp.setValue(data); + comp.checkConditions(data); + assert.equal(comp.components[1]._visible, false); + assert.equal(comp.components[1].components[0]._visible, true); + assert.equal(comp.components[1].components[1]._visible, true); + }); }); - }); - describe('set/get visible', function() { - it('should set/get visible flag on instance and child components', function(done) { - Harness.testCreate(NestedComponent, comp1) - .then(nested => { - expect(nested.visible).to.be.true; - nested.components.forEach(cmp => { - expect(cmp.parentVisible).to.be.true; - }); + describe('set/get visible', function () { + it('should set/get visible flag on instance and child components', function (done) { + Harness.testCreate(NestedComponent, comp1) + .then((nested) => { + expect(nested.visible).to.be.true; + nested.components.forEach((cmp) => { + expect(cmp.parentVisible).to.be.true; + }); - nested.visible = false; + nested.visible = false; - expect(nested.visible).to.be.false; + expect(nested.visible).to.be.false; - nested.components.forEach(cmp => { - expect(cmp.parentVisible).to.be.false; - }); + nested.components.forEach((cmp) => { + expect(cmp.parentVisible).to.be.false; + }); - nested.visible = true; + nested.visible = true; - nested.components.forEach(cmp => { - expect(cmp.parentVisible).to.be.true; - }); + nested.components.forEach((cmp) => { + expect(cmp.parentVisible).to.be.true; + }); - done(); - }, done) - .catch(done); + done(); + }, done) + .catch(done); + }); }); - }); - describe('set/get parentVisible', function() { - it('should set/get parentVisible flag on instance and child components', function(done) { - Harness.testCreate(NestedComponent, comp1) - .then(nested => { - expect(nested.parentVisible).to.be.true; + describe('set/get parentVisible', function () { + it('should set/get parentVisible flag on instance and child components', function (done) { + Harness.testCreate(NestedComponent, comp1) + .then((nested) => { + expect(nested.parentVisible).to.be.true; - nested.components.forEach(cmp => { - expect(cmp.parentVisible).to.be.true; - }); + nested.components.forEach((cmp) => { + expect(cmp.parentVisible).to.be.true; + }); - nested.parentVisible = false; + nested.parentVisible = false; - expect(nested.parentVisible).to.be.false; + expect(nested.parentVisible).to.be.false; - nested.components.forEach(cmp => { - expect(cmp.parentVisible).to.be.false; - }); + nested.components.forEach((cmp) => { + expect(cmp.parentVisible).to.be.false; + }); - nested.parentVisible = true; + nested.parentVisible = true; - expect(nested.parentVisible).to.be.true; + expect(nested.parentVisible).to.be.true; - nested.components.forEach(cmp => { - expect(cmp.parentVisible).to.be.true; - }); + nested.components.forEach((cmp) => { + expect(cmp.parentVisible).to.be.true; + }); - done(); - }, done) - .catch(done); - }); - }); - - describe('get schema', function() { - it('components array shouldn\'t have duplicates', function(done) { - Harness.testCreate(NestedComponent, comp1) - .then(nested => { - const child = nested.components[0]; - nested.components = [...nested.components, child, child, child]; - expect(nested.components).to.have.lengthOf(5); - expect(nested.schema.components).to.be.lengthOf(2); - expect(_map(nested.schema.components, 'key')).to.deep.equal(['firstName', 'lastName']); - done(); - }, done) - .catch(done); - }); - }); - - describe('calculateComponentPath', function() { - it('the first layer components', function(done) { - Harness.testCreate(NestedComponent, comp1) - .then((nested) => { - assert(nested.components[0].path === 'firstName'); - assert(nested.components[1].path === 'lastName'); - done(); - }) - .catch(done); + done(); + }, done) + .catch(done); + }); }); - it('inside data components', function(done) { - Harness.testCreate(NestedComponent, comp2) - .then((nested) => { - assert(nested.components[0].path === 'dataGrid'); - const dataGrid = nested.components[0]; - dataGrid.setValue([{ textField: '' }, { textField: '' }]); - setTimeout(() => { - assert(dataGrid.components[0].path === 'dataGrid[0].textField'); - assert(dataGrid.components[1].path === 'dataGrid[1].textField'); - done(); - },250); - }) - .catch(done); + describe('get schema', function () { + it("components array shouldn't have duplicates", function (done) { + Harness.testCreate(NestedComponent, comp1) + .then((nested) => { + const child = nested.components[0]; + nested.components = [ + ...nested.components, + child, + child, + child, + ]; + expect(nested.components).to.have.lengthOf(5); + expect(nested.schema.components).to.be.lengthOf(2); + expect(_map(nested.schema.components, 'key')).to.deep.equal( + ['firstName', 'lastName'], + ); + done(); + }, done) + .catch(done); + }); }); - it('inside nested forms', function(done) { - const formElement = document.createElement('div'); - const form = new Webform(formElement); - form.setForm(nestedForm) - .then(() => { - assert(form.components[0].path === 'form'); - - const childForm = form.components[0].subForm; - const textField = childForm.components[0]; - const dataGrid = childForm.components[1]; - const tabs = childForm.components[2]; - - assert(textField.path === 'form.data.textField'); - assert(dataGrid.path === 'form.data.dataGrid'); - assert(dataGrid.components[0].path === 'form.data.dataGrid[0].textField'); - assert(tabs.path === 'form.data.tabs'); - assert(tabs.tabs[0][0].path === 'form.data.tabsTextfield'); - done(); - }) - .catch(done); - }); - }); - - describe('getComponent', function() { - it('the first layer components', function(done) { - Harness.testCreate(NestedComponent, comp1) - .then((nested) => { - const firstNameTextFieldByStringPath = nested.getComponent('firstName'); - const firstNameTextFieldByArrayPath = nested.getComponent(['firstName']); - assert(firstNameTextFieldByStringPath.path === 'firstName'); - assert(firstNameTextFieldByArrayPath.path === 'firstName'); - done(); - }) - .catch(done); + describe('calculateComponentPath', function () { + it('the first layer components', function (done) { + Harness.testCreate(NestedComponent, comp1) + .then((nested) => { + assert(nested.components[0].path === 'firstName'); + assert(nested.components[1].path === 'lastName'); + done(); + }) + .catch(done); + }); + + it('inside data components', function (done) { + Harness.testCreate(NestedComponent, comp2) + .then((nested) => { + assert(nested.components[0].path === 'dataGrid'); + const dataGrid = nested.components[0]; + dataGrid.setValue([{ textField: '' }, { textField: '' }]); + setTimeout(() => { + assert( + dataGrid.components[0].path === + 'dataGrid[0].textField', + ); + assert( + dataGrid.components[1].path === + 'dataGrid[1].textField', + ); + done(); + }, 250); + }) + .catch(done); + }); + + it('inside nested forms', function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement); + form.setForm(nestedForm) + .then(() => { + assert(form.components[0].path === 'form'); + + const childForm = form.components[0].subForm; + const textField = childForm.components[0]; + const dataGrid = childForm.components[1]; + const tabs = childForm.components[2]; + + assert(textField.path === 'form.data.textField'); + assert(dataGrid.path === 'form.data.dataGrid'); + assert( + dataGrid.components[0].path === + 'form.data.dataGrid[0].textField', + ); + assert(tabs.path === 'form.data.tabs'); + assert(tabs.tabs[0][0].path === 'form.data.tabsTextfield'); + done(); + }) + .catch(done); + }); }); - it('inside data components', function(done) { - Harness.testCreate(NestedComponent, comp2) - .then((nested) => { - assert(nested.components[0].path === 'dataGrid'); - const dataGrid = nested.components[0]; - dataGrid.setValue([{ textField: '' }, { textField: '' }]); - setTimeout(() => { - const dataGridFirstRowTextField= nested.getComponent('dataGrid[0].textField'); - const dataGridSecondRowTextField= nested.getComponent('dataGrid[1].textField'); - - assert(dataGrid.components[0] === dataGridFirstRowTextField); - assert(dataGrid.components[1] === dataGridSecondRowTextField); - done(); - },250); - }) - .catch(done); + describe('getComponent', function () { + it('the first layer components', function (done) { + Harness.testCreate(NestedComponent, comp1) + .then((nested) => { + const firstNameTextFieldByStringPath = + nested.getComponent('firstName'); + const firstNameTextFieldByArrayPath = nested.getComponent([ + 'firstName', + ]); + assert(firstNameTextFieldByStringPath.path === 'firstName'); + assert(firstNameTextFieldByArrayPath.path === 'firstName'); + done(); + }) + .catch(done); + }); + + it('inside data components', function (done) { + Harness.testCreate(NestedComponent, comp2) + .then((nested) => { + assert(nested.components[0].path === 'dataGrid'); + const dataGrid = nested.components[0]; + dataGrid.setValue([{ textField: '' }, { textField: '' }]); + setTimeout(() => { + const dataGridFirstRowTextField = nested.getComponent( + 'dataGrid[0].textField', + ); + const dataGridSecondRowTextField = nested.getComponent( + 'dataGrid[1].textField', + ); + + assert( + dataGrid.components[0] === + dataGridFirstRowTextField, + ); + assert( + dataGrid.components[1] === + dataGridSecondRowTextField, + ); + done(); + }, 250); + }) + .catch(done); + }); + + it('inside nested forms', function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement); + form.setForm(nestedForm) + .then(() => { + const childForm = form.components[0].subForm; + const textField = form.getComponent('form.data.textField'); + const dataGrid = form.getComponent('form.data.dataGrid'); + const dataGridTextField = form.getComponent( + 'form.data.dataGrid[0].textField', + ); + + assert(textField === childForm.components[0]); + assert(dataGrid === childForm.components[1]); + assert( + dataGridTextField === + childForm.components[1].components[0], + ); + done(); + }) + .catch(done); + }); }); - it('inside nested forms', function(done) { - const formElement = document.createElement('div'); - const form = new Webform(formElement); - form.setForm(nestedForm) - .then(() => { - const childForm = form.components[0].subForm; - const textField = form.getComponent('form.data.textField'); - const dataGrid = form.getComponent('form.data.dataGrid'); - const dataGridTextField = form.getComponent('form.data.dataGrid[0].textField'); - - assert(textField === childForm.components[0]); - assert(dataGrid === childForm.components[1]); - assert(dataGridTextField === childForm.components[1].components[0]); - done(); - }) - .catch(done); - }); - }); - - describe('render value as String', function() { - it('Should render a Select\'s value template', function(done) { - Harness.testCreate(NestedComponent, comp3) - .then((nested) => { - const editGrid = nested.components[0]; - editGrid.addRow(); - editGrid.editRows[0].components[0].setValue(2); - setTimeout(() => { - editGrid.saveRow(0); - setTimeout(() => { - assert.equal(editGrid.dataValue[0].select, 2); - const rowContent = editGrid.element.querySelector('[ref="editgrid-editGrid-row"] .row .col-sm-2 span'); - assert(rowContent); - assert.equal(rowContent.textContent, 'Banana'); - done(); - }, 250); - }, 250); - }) - .catch(done); + describe('render value as String', function () { + it("Should render a Select's value template", function (done) { + Harness.testCreate(NestedComponent, comp3) + .then((nested) => { + const editGrid = nested.components[0]; + editGrid.addRow(); + editGrid.editRows[0].components[0].setValue(2); + setTimeout(() => { + editGrid.saveRow(0); + setTimeout(() => { + assert.equal(editGrid.dataValue[0].select, 2); + const rowContent = editGrid.element.querySelector( + '[ref="editgrid-editGrid-row"] .row .col-sm-2 span', + ); + assert(rowContent); + assert.equal(rowContent.textContent, 'Banana'); + done(); + }, 250); + }, 250); + }) + .catch(done); + }); }); - }); }); diff --git a/src/components/_classes/nested/fixtures/comp1.js b/src/components/_classes/nested/fixtures/comp1.js index 0f86f4a7a2..94dcf33039 100644 --- a/src/components/_classes/nested/fixtures/comp1.js +++ b/src/components/_classes/nested/fixtures/comp1.js @@ -1,14 +1,14 @@ export default { - components: [ - { - type: 'textfield', - key: 'firstName', - input: true - }, - { - type: 'textfield', - key: 'lastName', - input: true - } - ] + components: [ + { + type: 'textfield', + key: 'firstName', + input: true, + }, + { + type: 'textfield', + key: 'lastName', + input: true, + }, + ], }; diff --git a/src/components/_classes/nested/fixtures/comp2.js b/src/components/_classes/nested/fixtures/comp2.js index 5b76be11e0..595d983c79 100644 --- a/src/components/_classes/nested/fixtures/comp2.js +++ b/src/components/_classes/nested/fixtures/comp2.js @@ -1,225 +1,223 @@ export default { - components: [ - { - label: 'Data Grid', - reorder: false, - addAnotherPosition: 'bottom', - defaultOpen: false, - layoutFixed: false, - enableRowGroups: false, - tableView: false, - defaultValue: [ - {} - ], - key: 'dataGrid', - type: 'datagrid', - input: true, - components: [ + components: [ { - label: 'Text Field', - tableView: true, - validate: { - required: true, - custom: '', - customPrivate: false, - strictDateValidation: false, + label: 'Data Grid', + reorder: false, + addAnotherPosition: 'bottom', + defaultOpen: false, + layoutFixed: false, + enableRowGroups: false, + tableView: false, + defaultValue: [{}], + key: 'dataGrid', + type: 'datagrid', + input: true, + components: [ + { + label: 'Text Field', + tableView: true, + validate: { + required: true, + custom: '', + customPrivate: false, + strictDateValidation: false, + multiple: false, + unique: false, + minLength: '', + maxLength: '', + pattern: '', + }, + key: 'textField', + type: 'textfield', + input: true, + placeholder: '', + prefix: '', + customClass: '', + suffix: '', + multiple: false, + defaultValue: null, + protected: false, + unique: false, + persistent: true, + hidden: false, + clearOnHide: true, + refreshOn: '', + redrawOn: '', + modalEdit: false, + labelPosition: 'top', + description: '', + errorLabel: '', + tooltip: '', + hideLabel: false, + tabindex: '', + disabled: false, + autofocus: false, + dbIndex: false, + customDefaultValue: '', + calculateValue: '', + calculateServer: false, + widget: { + type: 'input', + }, + attributes: {}, + validateOn: 'change', + conditional: { + show: null, + when: null, + eq: '', + }, + overlay: { + style: '', + left: '', + top: '', + width: '', + height: '', + }, + allowCalculateOverride: false, + encrypted: false, + showCharCount: false, + showWordCount: false, + properties: {}, + allowMultipleMasks: false, + mask: false, + inputType: 'text', + inputFormat: 'plain', + inputMask: '', + spellcheck: true, + id: 'emxxdbi', + }, + ], + placeholder: '', + prefix: '', + customClass: '', + suffix: '', multiple: false, + protected: false, unique: false, - minLength: '', - maxLength: '', - pattern: '' - }, - key: 'textField', - type: 'textfield', - input: true, - placeholder: '', - prefix: '', - customClass: '', - suffix: '', - multiple: false, - defaultValue: null, - 'protected': false, - unique: false, - persistent: true, - hidden: false, - clearOnHide: true, - refreshOn: '', - redrawOn: '', - modalEdit: false, - labelPosition: 'top', - description: '', - errorLabel: '', - tooltip: '', - hideLabel: false, - tabindex: '', - disabled: false, - autofocus: false, - dbIndex: false, - customDefaultValue: '', - calculateValue: '', - calculateServer: false, - widget: { - type: 'input' - }, - attributes: {}, - validateOn: 'change', - conditional: { - show: null, - when: null, - eq: '' - }, - overlay: { - style: '', - left: '', - top: '', - width: '', - height: '' - }, - allowCalculateOverride: false, - encrypted: false, - showCharCount: false, - showWordCount: false, - properties: {}, - allowMultipleMasks: false, - mask: false, - inputType: 'text', - inputFormat: 'plain', - inputMask: '', - spellcheck: true, - id: 'emxxdbi' - } - ], - placeholder: '', - prefix: '', - customClass: '', - suffix: '', - multiple: false, - 'protected': false, - unique: false, - persistent: true, - hidden: false, - clearOnHide: true, - refreshOn: '', - redrawOn: '', - modalEdit: false, - labelPosition: 'top', - description: '', - errorLabel: '', - tooltip: '', - hideLabel: false, - tabindex: '', - disabled: false, - autofocus: false, - dbIndex: false, - customDefaultValue: '', - calculateValue: '', - calculateServer: false, - widget: null, - attributes: {}, - validateOn: 'change', - validate: { - required: false, - custom: '', - customPrivate: false, - strictDateValidation: false, - multiple: false, - unique: false - }, - conditional: { - show: null, - when: null, - eq: '' - }, - overlay: { - style: '', - left: '', - top: '', - width: '', - height: '' - }, - allowCalculateOverride: false, - encrypted: false, - showCharCount: false, - showWordCount: false, - properties: {}, - allowMultipleMasks: false, - tree: true, - disableAddingRemovingRows: false, - id: 'erygkn' - }, - { - label: 'Submit', - showValidations: false, - tableView: false, - key: 'submit', - type: 'button', - input: true, - placeholder: '', - prefix: '', - customClass: '', - suffix: '', - multiple: false, - defaultValue: null, - 'protected': false, - unique: false, - persistent: false, - hidden: false, - clearOnHide: true, - refreshOn: '', - redrawOn: '', - modalEdit: false, - labelPosition: 'top', - description: '', - errorLabel: '', - tooltip: '', - hideLabel: false, - tabindex: '', - disabled: false, - autofocus: false, - dbIndex: false, - customDefaultValue: '', - calculateValue: '', - calculateServer: false, - widget: { - type: 'input' - }, - attributes: {}, - validateOn: 'change', - validate: { - required: false, - custom: '', - customPrivate: false, - strictDateValidation: false, - multiple: false, - unique: false - }, - conditional: { - show: null, - when: null, - eq: '' - }, - overlay: { - style: '', - left: '', - top: '', - width: '', - height: '' - }, - allowCalculateOverride: false, - encrypted: false, - showCharCount: false, - showWordCount: false, - properties: {}, - allowMultipleMasks: false, - size: 'md', - leftIcon: '', - rightIcon: '', - block: false, - action: 'submit', - disableOnInvalid: false, - theme: 'primary', - dataGridLabel: true, - id: 'evn07ja' - } - ] + persistent: true, + hidden: false, + clearOnHide: true, + refreshOn: '', + redrawOn: '', + modalEdit: false, + labelPosition: 'top', + description: '', + errorLabel: '', + tooltip: '', + hideLabel: false, + tabindex: '', + disabled: false, + autofocus: false, + dbIndex: false, + customDefaultValue: '', + calculateValue: '', + calculateServer: false, + widget: null, + attributes: {}, + validateOn: 'change', + validate: { + required: false, + custom: '', + customPrivate: false, + strictDateValidation: false, + multiple: false, + unique: false, + }, + conditional: { + show: null, + when: null, + eq: '', + }, + overlay: { + style: '', + left: '', + top: '', + width: '', + height: '', + }, + allowCalculateOverride: false, + encrypted: false, + showCharCount: false, + showWordCount: false, + properties: {}, + allowMultipleMasks: false, + tree: true, + disableAddingRemovingRows: false, + id: 'erygkn', + }, + { + label: 'Submit', + showValidations: false, + tableView: false, + key: 'submit', + type: 'button', + input: true, + placeholder: '', + prefix: '', + customClass: '', + suffix: '', + multiple: false, + defaultValue: null, + protected: false, + unique: false, + persistent: false, + hidden: false, + clearOnHide: true, + refreshOn: '', + redrawOn: '', + modalEdit: false, + labelPosition: 'top', + description: '', + errorLabel: '', + tooltip: '', + hideLabel: false, + tabindex: '', + disabled: false, + autofocus: false, + dbIndex: false, + customDefaultValue: '', + calculateValue: '', + calculateServer: false, + widget: { + type: 'input', + }, + attributes: {}, + validateOn: 'change', + validate: { + required: false, + custom: '', + customPrivate: false, + strictDateValidation: false, + multiple: false, + unique: false, + }, + conditional: { + show: null, + when: null, + eq: '', + }, + overlay: { + style: '', + left: '', + top: '', + width: '', + height: '', + }, + allowCalculateOverride: false, + encrypted: false, + showCharCount: false, + showWordCount: false, + properties: {}, + allowMultipleMasks: false, + size: 'md', + leftIcon: '', + rightIcon: '', + block: false, + action: 'submit', + disableOnInvalid: false, + theme: 'primary', + dataGridLabel: true, + id: 'evn07ja', + }, + ], }; diff --git a/src/components/_classes/nested/fixtures/comp3.js b/src/components/_classes/nested/fixtures/comp3.js index ea23bfff19..89f8eb363b 100644 --- a/src/components/_classes/nested/fixtures/comp3.js +++ b/src/components/_classes/nested/fixtures/comp3.js @@ -1,27 +1,29 @@ export default { - 'components': [{ - 'label': 'Edit Grid', - 'tableView': false, - 'rowDrafts': false, - 'key': 'editGrid', - 'type': 'editgrid', - 'input': true, - 'components': [ - { - 'label': 'Select', - 'widget': 'choicesjs', - 'tableView': true, - 'dataSrc': 'custom', - 'data': { - 'custom': "values = [\n {id: 1, name: 'Apple'},\n {id: 2, name: 'Banana'},\n {id: 3, name: 'Orange'},\n {id: 3, name: 'Pineapple'}\n];" + components: [ + { + label: 'Edit Grid', + tableView: false, + rowDrafts: false, + key: 'editGrid', + type: 'editgrid', + input: true, + components: [ + { + label: 'Select', + widget: 'choicesjs', + tableView: true, + dataSrc: 'custom', + data: { + custom: "values = [\n {id: 1, name: 'Apple'},\n {id: 2, name: 'Banana'},\n {id: 3, name: 'Orange'},\n {id: 3, name: 'Pineapple'}\n];", + }, + valueProperty: 'id', + template: '{{ item.name }}', + selectThreshold: 0.3, + key: 'select', + type: 'select', + input: true, + }, + ], }, - 'valueProperty': 'id', - 'template': '{{ item.name }}', - 'selectThreshold': 0.3, - 'key': 'select', - 'type': 'select', - 'input': true - } - ] - }] + ], }; diff --git a/src/components/_classes/nestedarray/NestedArrayComponent.js b/src/components/_classes/nestedarray/NestedArrayComponent.js index d718fd606a..f7ebd2ecff 100644 --- a/src/components/_classes/nestedarray/NestedArrayComponent.js +++ b/src/components/_classes/nestedarray/NestedArrayComponent.js @@ -7,212 +7,249 @@ import Component from '../component/Component'; import NestedDataComponent from '../nesteddata/NestedDataComponent'; export default class NestedArrayComponent extends NestedDataComponent { - static schema(...extend) { - return NestedDataComponent.schema({ - disableAddingRemovingRows: false - }, ...extend); - } - - static savedValueTypes() { - return [componentValueTypes.array]; - } - - componentContext(component) { - return this.iteratableRows[component.rowIndex].data; - } - - get iteratableRows() { - throw new Error('Getter #iteratableRows() is not implemented'); - } - - get rowIndex() { - return super.rowIndex; - } - - set rowIndex(value) { - this._rowIndex = value; - } - - init() { - super.init(); - this.prevHasAddButton = this.hasAddButton(); - } - - checkAddButtonChanged() { - const isAddButton = this.hasAddButton(); - if (isAddButton !== this.prevHasAddButton) { - this.prevHasAddButton = isAddButton; - this.redraw(); - } - } - - checkData(data, flags, row) { - data = data || this.rootValue; - flags = flags || {}; - row = row || this.data; - this.checkAddButtonChanged(); - - return this.checkRows('checkData', data, flags, Component.prototype.checkData.call(this, data, flags, row)); - } - - checkRows(method, data, opts, defaultValue, silentCheck) { - return this.iteratableRows.reduce( - (valid, row, rowIndex) => { - if (!opts?.rowIndex || opts?.rowIndex === rowIndex) { - return this.checkRow(method, data, opts, row.data, row.components, silentCheck) && valid; - } - else { - return valid; + static schema(...extend) { + return NestedDataComponent.schema( + { + disableAddingRemovingRows: false, + }, + ...extend, + ); + } + + static savedValueTypes() { + return [componentValueTypes.array]; + } + + componentContext(component) { + return this.iteratableRows[component.rowIndex].data; + } + + get iteratableRows() { + throw new Error('Getter #iteratableRows() is not implemented'); + } + + get rowIndex() { + return super.rowIndex; + } + + set rowIndex(value) { + this._rowIndex = value; + } + + init() { + super.init(); + this.prevHasAddButton = this.hasAddButton(); + } + + checkAddButtonChanged() { + const isAddButton = this.hasAddButton(); + if (isAddButton !== this.prevHasAddButton) { + this.prevHasAddButton = isAddButton; + this.redraw(); } - }, - defaultValue, - ); - } - - checkRow(method, data, opts, row, components, silentCheck) { - if (opts?.isolateRow) { - silentCheck = true; - opts.noRefresh = true; - } - - const valid = _.reduce( - components, - (valid, component) => component[method](data, opts, row, silentCheck) && valid, - true, - ); - if (opts?.noRefresh) { - delete opts.noRefresh; - } - return valid; - } - - hasAddButton() { - const maxLength = _.get(this.component, 'validate.maxLength'); - const conditionalAddButton = _.get(this.component, 'conditionalAddButton'); - - return !this.component.disableAddingRemovingRows && - !this.options.readOnly && - !this.disabled && - this.fullMode && - !this.options.preview && - (!maxLength || (this.iteratableRows.length < maxLength)) && - (!conditionalAddButton || this.evaluate(conditionalAddButton, { - value: this.dataValue, - }, 'show')); - } - - getComponent(path, fn, originalPath) { - path = Array.isArray(path) ? path : [path]; - let key = path.shift(); - const remainingPath = path; - let result = []; - let possibleComp = null; - let comp = null; - let rowIndex = null; - - if (_.isNumber(key)) { - rowIndex = key; - key = remainingPath.shift(); - } - if (!_.isString(key)) { - return result; - } - - this.everyComponent((component, components) => { - if (component.component.key === key) { - possibleComp = component; - if (remainingPath.length > 0 && 'getComponent' in component) { - comp = component.getComponent(remainingPath, fn, originalPath); + } + + checkData(data, flags, row) { + data = data || this.rootValue; + flags = flags || {}; + row = row || this.data; + this.checkAddButtonChanged(); + + return this.checkRows( + 'checkData', + data, + flags, + Component.prototype.checkData.call(this, data, flags, row), + ); + } + + checkRows(method, data, opts, defaultValue, silentCheck) { + return this.iteratableRows.reduce((valid, row, rowIndex) => { + if (!opts?.rowIndex || opts?.rowIndex === rowIndex) { + return ( + this.checkRow( + method, + data, + opts, + row.data, + row.components, + silentCheck, + ) && valid + ); + } else { + return valid; + } + }, defaultValue); + } + + checkRow(method, data, opts, row, components, silentCheck) { + if (opts?.isolateRow) { + silentCheck = true; + opts.noRefresh = true; } - else if (fn) { - fn(component, components); + + const valid = _.reduce( + components, + (valid, component) => + component[method](data, opts, row, silentCheck) && valid, + true, + ); + if (opts?.noRefresh) { + delete opts.noRefresh; } - result = rowIndex !== null ? comp : result.concat(comp || possibleComp); - } - }, rowIndex); - if ((!result || result.length === 0) && possibleComp) { - result = rowIndex !== null ? possibleComp : [possibleComp]; + return valid; } - return result; - } - everyComponent(fn, rowIndex, options) { - if (_.isObject(rowIndex)) { - options = rowIndex; - rowIndex = null; + hasAddButton() { + const maxLength = _.get(this.component, 'validate.maxLength'); + const conditionalAddButton = _.get( + this.component, + 'conditionalAddButton', + ); + + return ( + !this.component.disableAddingRemovingRows && + !this.options.readOnly && + !this.disabled && + this.fullMode && + !this.options.preview && + (!maxLength || this.iteratableRows.length < maxLength) && + (!conditionalAddButton || + this.evaluate( + conditionalAddButton, + { + value: this.dataValue, + }, + 'show', + )) + ); } - if (options?.email) { - return; + getComponent(path, fn, originalPath) { + path = Array.isArray(path) ? path : [path]; + let key = path.shift(); + const remainingPath = path; + let result = []; + let possibleComp = null; + let comp = null; + let rowIndex = null; + + if (_.isNumber(key)) { + rowIndex = key; + key = remainingPath.shift(); + } + if (!_.isString(key)) { + return result; + } + + this.everyComponent((component, components) => { + if (component.component.key === key) { + possibleComp = component; + if (remainingPath.length > 0 && 'getComponent' in component) { + comp = component.getComponent( + remainingPath, + fn, + originalPath, + ); + } else if (fn) { + fn(component, components); + } + result = + rowIndex !== null + ? comp + : result.concat(comp || possibleComp); + } + }, rowIndex); + if ((!result || result.length === 0) && possibleComp) { + result = rowIndex !== null ? possibleComp : [possibleComp]; + } + return result; } - const components = this.getComponents(rowIndex); - _.each(components, (component, index) => { - if (fn(component, components, index) === false) { - return false; - } + everyComponent(fn, rowIndex, options) { + if (_.isObject(rowIndex)) { + options = rowIndex; + rowIndex = null; + } - if (typeof component.everyComponent === 'function') { - if (component.everyComponent(fn, options) === false) { - return false; + if (options?.email) { + return; } - } - }); - } - getValueAsString(value, options) { - if (options?.email) { - let result = (` + const components = this.getComponents(rowIndex); + _.each(components, (component, index) => { + if (fn(component, components, index) === false) { + return false; + } + + if (typeof component.everyComponent === 'function') { + if (component.everyComponent(fn, options) === false) { + return false; + } + } + }); + } + + getValueAsString(value, options) { + if (options?.email) { + let result = ` - `); + `; - this.component.components?.forEach((component) => { - const label = component.label || component.key; - result += ``; - }); + this.component.components?.forEach((component) => { + const label = component.label || component.key; + result += ``; + }); - result += (` + result += ` - `); - - this.iteratableRows.forEach(({ components }) => { - result += ''; - _.each(components, (component) => { - result += ''; - }); - result += ''; - }); - - result += (` + `; + + this.iteratableRows.forEach(({ components }) => { + result += ''; + _.each(components, (component) => { + result += ''; + }); + result += ''; + }); + + result += `
    ${label}${label}
    '; - if (component.isInputComponent && component.visible && !component.skipInEmail) { - result += component.getView(component.dataValue, options); - } - result += '
    '; + if ( + component.isInputComponent && + component.visible && + !component.skipInEmail + ) { + result += component.getView( + component.dataValue, + options, + ); + } + result += '
    - `); + `; - return result; - } + return result; + } - if (!value || !value.length) { - return ''; - } + if (!value || !value.length) { + return ''; + } - return super.getValueAsString(value, options); - } + return super.getValueAsString(value, options); + } - getComponents(rowIndex) { - if (rowIndex !== undefined) { - if (!this.iteratableRows[rowIndex]) { - return []; - } - return this.iteratableRows[rowIndex].components; + getComponents(rowIndex) { + if (rowIndex !== undefined) { + if (!this.iteratableRows[rowIndex]) { + return []; + } + return this.iteratableRows[rowIndex].components; + } + return super.getComponents(); } - return super.getComponents(); - } } diff --git a/src/components/_classes/nestedarray/NestedArrayComponent.unit.js b/src/components/_classes/nestedarray/NestedArrayComponent.unit.js index d6e129228e..42ec516d2e 100644 --- a/src/components/_classes/nestedarray/NestedArrayComponent.unit.js +++ b/src/components/_classes/nestedarray/NestedArrayComponent.unit.js @@ -3,26 +3,26 @@ import NestedArrayComponent from './NestedArrayComponent'; import Harness from '../../../../test/harness'; let component = null; -describe('NestedArrayComponent class', function() { - it('Should create a new NestedArrayComponent class', function() { - return Harness.testCreate(NestedArrayComponent, { - // key: 'nested', - components: [ - { - type: 'textfield', - key: 'firstName', - input: true - }, - { - type: 'textfield', - key: 'lastName', - input: true - } - ] - }).then((_component) => { - component = _component; - Harness.testElements(component, 'input[name="data[firstName]"]', 1); - Harness.testElements(component, 'input[name="data[lastName]"]', 1); +describe('NestedArrayComponent class', function () { + it('Should create a new NestedArrayComponent class', function () { + return Harness.testCreate(NestedArrayComponent, { + // key: 'nested', + components: [ + { + type: 'textfield', + key: 'firstName', + input: true, + }, + { + type: 'textfield', + key: 'lastName', + input: true, + }, + ], + }).then((_component) => { + component = _component; + Harness.testElements(component, 'input[name="data[firstName]"]', 1); + Harness.testElements(component, 'input[name="data[lastName]"]', 1); + }); }); - }); }); diff --git a/src/components/_classes/nesteddata/NestedDataComponent.js b/src/components/_classes/nesteddata/NestedDataComponent.js index b88ab3307b..0bdcf84781 100644 --- a/src/components/_classes/nesteddata/NestedDataComponent.js +++ b/src/components/_classes/nesteddata/NestedDataComponent.js @@ -2,154 +2,178 @@ import Component from '../component/Component'; import NestedComponent from '../nested/NestedComponent'; import _ from 'lodash'; -import { componentValueTypes, getComponentSavedTypes } from '../../../utils/utils'; +import { + componentValueTypes, + getComponentSavedTypes, +} from '../../../utils/utils'; export default class NestedDataComponent extends NestedComponent { - hasChanged(newValue, oldValue) { - // If we do not have a value and are getting set to anything other than undefined or null, then we changed. - if ( - newValue !== undefined && - newValue !== null && - !this.hasValue() - ) { - return true; + hasChanged(newValue, oldValue) { + // If we do not have a value and are getting set to anything other than undefined or null, then we changed. + if (newValue !== undefined && newValue !== null && !this.hasValue()) { + return true; + } + return !_.isEqual(newValue, oldValue); } - return !_.isEqual(newValue, oldValue); - } - static savedValueTypes(schema) { - return getComponentSavedTypes(schema) || [componentValueTypes.object]; - } + static savedValueTypes(schema) { + return getComponentSavedTypes(schema) || [componentValueTypes.object]; + } - get allowData() { - return true; - } + get allowData() { + return true; + } - get emptyValue() { - return {}; - } + get emptyValue() { + return {}; + } - componentContext() { - return this.dataValue; - } + componentContext() { + return this.dataValue; + } - getValueAsString(value, options) { - if (options?.email) { - let result = (` + getValueAsString(value, options) { + if (options?.email) { + let result = ` - `); - - this.everyComponent((component) => { - if (component.isInputComponent && component.visible && !component.skipInEmail) { - result += (` + `; + + this.everyComponent( + (component) => { + if ( + component.isInputComponent && + component.visible && + !component.skipInEmail + ) { + result += ` - + - `); - } - }, { - ...options, - fromRoot: true, - }); - - result += (` + `; + } + }, + { + ...options, + fromRoot: true, + }, + ); + + result += `
    ${component.label}${component.getView(component.dataValue, options)}${component.getView( + component.dataValue, + options, + )}
    - `); + `; - return result; - } - if (_.isEmpty(value)) { - return ''; - } - if (options?.modalPreview) { - delete options.modalPreview; - return this.getDataValueAsTable(value, options); - } + return result; + } + if (_.isEmpty(value)) { + return ''; + } + if (options?.modalPreview) { + delete options.modalPreview; + return this.getDataValueAsTable(value, options); + } - return '[Complex Data]'; - } + return '[Complex Data]'; + } - getDataValueAsTable(value, options) { - let result = (` + getDataValueAsTable(value, options) { + let result = ` - `); - - const htmlTagRegExp = new RegExp('<(.*?)>'); - - this.components.forEach((component) => { - if (component.isInputComponent && component.visible && !component.skipInEmail) { - const componentValue = component.getView(component.dataValue, options); - result += (` + `; + + const htmlTagRegExp = new RegExp('<(.*?)>'); + + this.components.forEach( + (component) => { + if ( + component.isInputComponent && + component.visible && + !component.skipInEmail + ) { + const componentValue = component.getView( + component.dataValue, + options, + ); + result += ` - - `); - } - }, { - ...options, - fromRoot: true, - }); - - result += (` + `; + } + }, + { + ...options, + fromRoot: true, + }, + ); + + result += `
    ${component.label}${component.component && component.component.inputFormat === 'html' && htmlTagRegExp.test(componentValue) - ? componentValue - : `` + ${ + component.component && + component.component.inputFormat === 'html' && + htmlTagRegExp.test(componentValue) + ? componentValue + : `` }
    - `); - - return result; - } - - everyComponent(fn, options) { - if (options?.email) { - if (options.fromRoot) { - delete options.fromRoot; - } - else { - return; - } + `; + + return result; + } + + everyComponent(fn, options) { + if (options?.email) { + if (options.fromRoot) { + delete options.fromRoot; + } else { + return; + } + } + + return super.everyComponent(fn, options); } - return super.everyComponent(fn, options); - } - - /** - * Get the value of this component. - * - * @returns {*} - */ - getValue() { - return this.dataValue; - } - - updateValue(value, flags = {}) { - // Intentionally skip over nested component updateValue method to keep - // recursive update from occurring with sub components. - return Component.prototype.updateValue.call(this, value, flags); - } - - setValue(value, flags = {}) { - let changed = false; - - const hasValue = this.hasValue(); - if (hasValue && _.isEmpty(this.dataValue)) { - flags.noValidate = true; + /** + * Get the value of this component. + * + * @returns {*} + */ + getValue() { + return this.dataValue; } - if (!value || !_.isObject(value) || !hasValue) { - changed = true; - this.dataValue = this.defaultValue; + updateValue(value, flags = {}) { + // Intentionally skip over nested component updateValue method to keep + // recursive update from occurring with sub components. + return Component.prototype.updateValue.call(this, value, flags); } - changed = super.setValue(value, flags) || changed; + setValue(value, flags = {}) { + let changed = false; + + const hasValue = this.hasValue(); + if (hasValue && _.isEmpty(this.dataValue)) { + flags.noValidate = true; + } - this.updateOnChange(flags, changed); - return changed; - } + if (!value || !_.isObject(value) || !hasValue) { + changed = true; + this.dataValue = this.defaultValue; + } + + changed = super.setValue(value, flags) || changed; + + this.updateOnChange(flags, changed); + return changed; + } } diff --git a/src/components/_classes/nesteddata/NestedDataComponent.unit.js b/src/components/_classes/nesteddata/NestedDataComponent.unit.js index 5b3ffc4f09..3707e6f75f 100644 --- a/src/components/_classes/nesteddata/NestedDataComponent.unit.js +++ b/src/components/_classes/nesteddata/NestedDataComponent.unit.js @@ -3,26 +3,26 @@ import NestedDataComponent from './NestedDataComponent'; import Harness from '../../../../test/harness'; let component = null; -describe('NestedDataComponent class', function() { - it('Should create a new NestedDataComponent class', function() { - return Harness.testCreate(NestedDataComponent, { - // key: 'nested', - components: [ - { - type: 'textfield', - key: 'firstName', - input: true - }, - { - type: 'textfield', - key: 'lastName', - input: true - } - ] - }).then((_component) => { - component = _component; - Harness.testElements(component, 'input[name="data[firstName]"]', 1); - Harness.testElements(component, 'input[name="data[lastName]"]', 1); +describe('NestedDataComponent class', function () { + it('Should create a new NestedDataComponent class', function () { + return Harness.testCreate(NestedDataComponent, { + // key: 'nested', + components: [ + { + type: 'textfield', + key: 'firstName', + input: true, + }, + { + type: 'textfield', + key: 'lastName', + input: true, + }, + ], + }).then((_component) => { + component = _component; + Harness.testElements(component, 'input[name="data[firstName]"]', 1); + Harness.testElements(component, 'input[name="data[lastName]"]', 1); + }); }); - }); }); diff --git a/src/components/address/Address.form.js b/src/components/address/Address.form.js index aae69d7cd5..e304ae476e 100644 --- a/src/components/address/Address.form.js +++ b/src/components/address/Address.form.js @@ -3,21 +3,24 @@ import AddressEditData from './editForm/Address.edit.data'; import AddressEditDisplay from './editForm/Address.edit.display'; import AddressEditProvider from './editForm/Address.edit.provider'; -export default function(...extend) { - return Components.baseEditForm([ - { - key: 'data', - components: AddressEditData, - }, - { - key: 'display', - components: AddressEditDisplay, - }, - { - label: 'Provider', - key: 'provider', - weight: 15, - components: AddressEditProvider, - }, - ], ...extend); +export default function (...extend) { + return Components.baseEditForm( + [ + { + key: 'data', + components: AddressEditData, + }, + { + key: 'display', + components: AddressEditDisplay, + }, + { + label: 'Provider', + key: 'provider', + weight: 15, + components: AddressEditProvider, + }, + ], + ...extend, + ); } diff --git a/src/components/address/Address.js b/src/components/address/Address.js index 9ea06812a4..ca9726846a 100644 --- a/src/components/address/Address.js +++ b/src/components/address/Address.js @@ -10,655 +10,682 @@ import ContainerComponent from '../container/Container'; import { componentValueTypes, getComponentSavedTypes } from '../../utils/utils'; export const AddressComponentMode = { - Autocomplete: 'autocomplete', - Manual: 'manual', + Autocomplete: 'autocomplete', + Manual: 'manual', }; -const RemoveValueIconHiddenClass = 'address-autocomplete-remove-value-icon--hidden'; -const ChildConditional = 'show = _.get(instance, \'parent.manualMode\', false);'; +const RemoveValueIconHiddenClass = + 'address-autocomplete-remove-value-icon--hidden'; +const ChildConditional = "show = _.get(instance, 'parent.manualMode', false);"; export default class AddressComponent extends ContainerComponent { - static schema(...extend) { - return ContainerComponent.schema({ - type: 'address', - label: 'Address', - key: 'address', - switchToManualModeLabel: 'Can\'t find address? Switch to manual mode.', - provider: '', - providerOptions: {}, - manualModeViewString: '', - hideLabel: false, - disableClearIcon: false, - enableManualMode: false, - components: [ - { - label: 'Address 1', - tableView: false, - key: 'address1', - type: 'textfield', - input: true, - customConditional: ChildConditional, - }, - { - label: 'Address 2', - tableView: false, - key: 'address2', - type: 'textfield', - input: true, - customConditional: ChildConditional, - }, - { - label: 'City', - tableView: false, - key: 'city', - type: 'textfield', - input: true, - customConditional: ChildConditional, - }, - { - label: 'State', - tableView: false, - key: 'state', - type: 'textfield', - input: true, - customConditional: ChildConditional, - }, - { - label: 'Country', - tableView: false, - key: 'country', - type: 'textfield', - input: true, - customConditional: ChildConditional, - }, - { - label: 'Zip Code', - tableView: false, - key: 'zip', - type: 'textfield', - input: true, - customConditional: ChildConditional, - }, - ], - }, ...extend); - } - - static savedValueTypes(schema) { - schema = schema || {}; - - return getComponentSavedTypes(schema) || [componentValueTypes.object]; - } - - static get builderInfo() { - return { - title: 'Address', - group: 'advanced', - icon: 'home', - documentation: '/userguide/form-building/advanced-components#address', - weight: 35, - schema: AddressComponent.schema(), - }; - } - - mergeSchema(component = {}) { - let { defaultSchema } = this; - - if (component.components) { - defaultSchema = _.omit(defaultSchema, 'components'); - } - - return _.defaultsDeep(component , defaultSchema); - } - - init() { - this.components = this.components || []; - if (this.builderMode || this.manualModeEnabled) { - NestedComponent.prototype.addComponents.call(this, this.manualMode ? this.address : {}); - } - Field.prototype.init.call(this); - - if (!this.builderMode) { - if (this.component.provider) { - const { - provider, - providerOptions, - } = this.component; - this.provider = this.initializeProvider(provider, providerOptions); - } - else if (this.component.map) { - // Fallback to legacy version where Google Maps was the only provider. - this.component.provider = GoogleAddressProvider.name; - this.component.providerOptions = this.component.providerOptions || {}; - - const { - map, - provider, - providerOptions, - } = this.component; - - const { - key, - region, - } = map; - - if (key) { - _.set(providerOptions, 'params.key', key); + static schema(...extend) { + return ContainerComponent.schema( + { + type: 'address', + label: 'Address', + key: 'address', + switchToManualModeLabel: + "Can't find address? Switch to manual mode.", + provider: '', + providerOptions: {}, + manualModeViewString: '', + hideLabel: false, + disableClearIcon: false, + enableManualMode: false, + components: [ + { + label: 'Address 1', + tableView: false, + key: 'address1', + type: 'textfield', + input: true, + customConditional: ChildConditional, + }, + { + label: 'Address 2', + tableView: false, + key: 'address2', + type: 'textfield', + input: true, + customConditional: ChildConditional, + }, + { + label: 'City', + tableView: false, + key: 'city', + type: 'textfield', + input: true, + customConditional: ChildConditional, + }, + { + label: 'State', + tableView: false, + key: 'state', + type: 'textfield', + input: true, + customConditional: ChildConditional, + }, + { + label: 'Country', + tableView: false, + key: 'country', + type: 'textfield', + input: true, + customConditional: ChildConditional, + }, + { + label: 'Zip Code', + tableView: false, + key: 'zip', + type: 'textfield', + input: true, + customConditional: ChildConditional, + }, + ], + }, + ...extend, + ); + } + + static savedValueTypes(schema) { + schema = schema || {}; + + return getComponentSavedTypes(schema) || [componentValueTypes.object]; + } + + static get builderInfo() { + return { + title: 'Address', + group: 'advanced', + icon: 'home', + documentation: + '/userguide/form-building/advanced-components#address', + weight: 35, + schema: AddressComponent.schema(), + }; + } + + mergeSchema(component = {}) { + let { defaultSchema } = this; + + if (component.components) { + defaultSchema = _.omit(defaultSchema, 'components'); } - if (region) { - _.set(providerOptions, 'params.region', region); + + return _.defaultsDeep(component, defaultSchema); + } + + init() { + this.components = this.components || []; + if (this.builderMode || this.manualModeEnabled) { + NestedComponent.prototype.addComponents.call( + this, + this.manualMode ? this.address : {}, + ); } + Field.prototype.init.call(this); - this.provider = this.initializeProvider(provider, providerOptions); - } + if (!this.builderMode) { + if (this.component.provider) { + const { provider, providerOptions } = this.component; + this.provider = this.initializeProvider( + provider, + providerOptions, + ); + } else if (this.component.map) { + // Fallback to legacy version where Google Maps was the only provider. + this.component.provider = GoogleAddressProvider.name; + this.component.providerOptions = + this.component.providerOptions || {}; + + const { map, provider, providerOptions } = this.component; + + const { key, region } = map; + + if (key) { + _.set(providerOptions, 'params.key', key); + } + if (region) { + _.set(providerOptions, 'params.region', region); + } + + this.provider = this.initializeProvider( + provider, + providerOptions, + ); + } + } } - } - initializeProvider(provider, options = {}) { - const url = this.interpolate(options.url); - const Provider = Formio.Providers.getProvider('address', provider); - return new Provider({ ...options, url }); - } + initializeProvider(provider, options = {}) { + const url = this.interpolate(options.url); + const Provider = Formio.Providers.getProvider('address', provider); + return new Provider({ ...options, url }); + } + + get emptyValue() { + return this.manualModeEnabled + ? { + mode: AddressComponentMode.Autocomplete, + address: {}, + } + : {}; + } - get emptyValue() { - return this.manualModeEnabled - ? { - mode: AddressComponentMode.Autocomplete, - address: {}, - } - : {}; - } + get mode() { + if (!this.manualModeEnabled) { + return AddressComponentMode.Autocomplete; + } - get mode() { - if (!this.manualModeEnabled) { - return AddressComponentMode.Autocomplete; + return this.dataValue?.mode ?? AddressComponentMode.Autocomplete; } - return this.dataValue?.mode ?? AddressComponentMode.Autocomplete; - } + set mode(value) { + if (this.manualModeEnabled) { + this.dataValue.mode = value; + } + } - set mode(value) { - if (this.manualModeEnabled) { - this.dataValue.mode = value; + get autocompleteMode() { + return this.mode === AddressComponentMode.Autocomplete; } - } - get autocompleteMode() { - return this.mode === AddressComponentMode.Autocomplete; - } + get manualMode() { + return this.mode === AddressComponentMode.Manual; + } - get manualMode() { - return this.mode === AddressComponentMode.Manual; - } + get manualModeEnabled() { + return !this.isMultiple && Boolean(this.component.enableManualMode); + } - get manualModeEnabled() { - return !this.isMultiple && Boolean(this.component.enableManualMode); - } + restoreComponentsContext() { + this.getComponents().forEach((component) => { + component.data = this.address; + component.setValue(component.dataValue, { + noUpdateEvent: true, + }); + }); + } - restoreComponentsContext() { - this.getComponents().forEach((component) => { - component.data = this.address; - component.setValue(component.dataValue, { - noUpdateEvent: true, - }); - }); - } + get isMultiple() { + return Boolean(this.component.multiple); + } - get isMultiple() { - return Boolean(this.component.multiple); - } + get address() { + if (this.isMultiple) { + return _.isArray(this.dataValue) + ? this.dataValue + : [this.dataValue]; + } + // Manual mode is not implementing for multiple value + return this.manualModeEnabled && this.dataValue + ? this.dataValue.address + : this.dataValue; + } - get address() { - if (this.isMultiple) { - return _.isArray(this.dataValue) ? this.dataValue : [this.dataValue]; + set address(value) { + if (this.manualModeEnabled && !this.isMultiple) { + this.dataValue.address = value; + } else { + this.dataValue = value; + } } - // Manual mode is not implementing for multiple value - return (this.manualModeEnabled && this.dataValue) ? this.dataValue.address : this.dataValue; - } - set address(value) { - if (this.manualModeEnabled && !this.isMultiple) { - this.dataValue.address = value; + get defaultValue() { + let defaultValue = super.defaultValue; + + if (this.isMultiple) { + defaultValue = _.isArray(defaultValue) + ? defaultValue + : [defaultValue]; + } + + return defaultValue; } - else { - this.dataValue = value; + + get defaultSchema() { + return AddressComponent.schema(); } - } - get defaultValue() { - let defaultValue = super.defaultValue; + isValueInLegacyFormat(value) { + return value && !value.mode; + } - if (this.isMultiple) { - defaultValue = _.isArray(defaultValue) ? defaultValue : [defaultValue]; + normalizeValue(value) { + return this.manualModeEnabled && this.isValueInLegacyFormat(value) + ? { + mode: AddressComponentMode.Autocomplete, + address: value, + } + : value; } - return defaultValue; - } + setValue(value, flags = {}) { + const changed = Field.prototype.setValue.call(this, value, flags); + + if (this.manualMode) { + this.restoreComponentsContext(); + } + + if (changed || (!_.isEmpty(value) && flags.fromSubmission)) { + this.redraw(); + } - get defaultSchema() { - return AddressComponent.schema(); - } + return changed; + } - isValueInLegacyFormat(value) { - return value && !value.mode; - } + static get modeSwitcherRef() { + return 'modeSwitcher'; + } - normalizeValue(value) { - return (this.manualModeEnabled && this.isValueInLegacyFormat(value)) - ? { - mode: AddressComponentMode.Autocomplete, - address: value, - } - : value; - } + static get removeValueIconRef() { + return 'removeValueIcon'; + } - setValue(value, flags = {}) { - const changed = Field.prototype.setValue.call(this, value, flags); + static get searchInputRef() { + return 'searchInput'; + } - if (this.manualMode) { - this.restoreComponentsContext(); + static get addRowButtonRef() { + return 'addButton'; } - if (changed || !_.isEmpty(value) && flags.fromSubmission) { - this.redraw(); + static get removeRowButtonRef() { + return 'removeRow'; } - return changed; - } + get modeSwitcher() { + return this.refs + ? this.refs[AddressComponent.modeSwitcherRef] || null + : null; + } - static get modeSwitcherRef() { - return 'modeSwitcher'; - } + get removeValueIcon() { + return this.refs + ? this.refs[AddressComponent.removeValueIconRef] || null + : null; + } - static get removeValueIconRef() { - return 'removeValueIcon'; - } - - static get searchInputRef() { - return 'searchInput'; - } - - static get addRowButtonRef() { - return 'addButton'; - } - - static get removeRowButtonRef() { - return 'removeRow'; - } - - get modeSwitcher() { - return this.refs - ? (this.refs[AddressComponent.modeSwitcherRef] || null) - : null; - } - - get removeValueIcon() { - return this.refs - ? (this.refs[AddressComponent.removeValueIconRef] || null) - : null; - } - - get searchInput() { - return this.refs - ? (this.refs[AddressComponent.searchInputRef] || null) - : null; - } - - get addRowButton() { - return this.refs - ? (this.refs[AddressComponent.addRowButtonRef] || null) - : null; - } - - get removeRowButton() { - return this.refs - ? (this.refs[AddressComponent.removeRowButtonRef] || null) - : null; - } + get searchInput() { + return this.refs + ? this.refs[AddressComponent.searchInputRef] || null + : null; + } - get searchInputAttributes() { - const attr = { - name: this.options.name, - type: 'text', - class: 'form-control', - lang: this.options.language, - tabindex: this.component.tabindex || 0, - }; + get addRowButton() { + return this.refs + ? this.refs[AddressComponent.addRowButtonRef] || null + : null; + } - if (this.component.placeholder) { - attr.placeholder = this.t(this.component.placeholder), { _userInput: true }; + get removeRowButton() { + return this.refs + ? this.refs[AddressComponent.removeRowButtonRef] || null + : null; } - - if (this.disabled) { - attr.disabled = 'disabled'; - } - - _.defaults(attr, this.component.attributes); - - return attr; - } - - get templateName() { - return 'address'; - } - - get gridTemplateName() { - return 'multiValueTable'; - } - - get rowTemplateName() { - return 'multiValueRow'; - } - - get hasChildren() { - return !this.isMultiple && (this.builderMode || this.manualModeEnabled); - } - - get addAnother() { - return this.t(this.component.addAnother || 'Add Another'); - } - - renderElement(value) { - return this.renderTemplate(this.templateName, { - children: this.hasChildren ? this.renderComponents() : '', - nestedKey: this.nestedKey, - inputAttributes: this.searchInputAttributes, - ref: { - modeSwitcher: AddressComponent.modeSwitcherRef, - removeValueIcon: AddressComponent.removeValueIconRef, - searchInput: AddressComponent.searchInputRef, - }, - displayValue: this.getDisplayValue(value), - mode: { - autocomplete: this.autocompleteMode, - manual: this.manualMode, - }, - }); - } - - renderRow(value, index) { - return this.renderTemplate(this.rowTemplateName, { - index, - disabled: this.disabled, - element: `${this.renderElement(value, index)}`, - }); - } - - renderGrid() { - return this.renderTemplate(this.gridTemplateName, { - rows: this.address.map(this.renderRow.bind(this)).join(''), - disabled: this.disabled, - addAnother: this.addAnother, - }); - } - - render() { - if (this.isMultiple) { - return super.render(this.renderGrid()); - } - - return super.render(this.renderElement()); - } - - onSelectAddress(address, element, index) { - if (this.isMultiple) { - this.address[index] = address; - this.address = [...this.address]; - } - else { - this.address = address; - } - - this.triggerChange({ - modified: true, - }); - - if (element) { - element.value = this.getDisplayValue(this.isMultiple ? this.address[index] : this.address); - } - - this.updateRemoveIcon(index); - } - - addRow() { - this.address = this.address.concat(this.emptyValue); - super.redraw(); - } - - attach(element) { - const result = ((this.builderMode || this.manualMode) ? super.attach : Field.prototype.attach).call(this, element); - - if (!this.builderMode) { - if (!this.provider && this.component.provider) { - const { - provider, - providerOptions, - } = this.component; - this.provider = this.initializeProvider(provider, providerOptions); - } - } - - this.loadRefs(element, { - [AddressComponent.addRowButtonRef]: 'single', - [AddressComponent.modeSwitcherRef]: 'single', - [AddressComponent.removeRowButtonRef]: 'multiple', - [AddressComponent.removeValueIconRef]: 'multiple', - [AddressComponent.searchInputRef]: 'multiple', - }); - - this.searchInput.forEach((element, index) => { - if (!this.builderMode && element && this.provider) { - if (this.component.provider === 'google') { - this.provider.attachAutocomplete(element, index, this.onSelectAddress.bind(this)); + + get searchInputAttributes() { + const attr = { + name: this.options.name, + type: 'text', + class: 'form-control', + lang: this.options.language, + tabindex: this.component.tabindex || 0, + }; + + if (this.component.placeholder) { + (attr.placeholder = this.t(this.component.placeholder)), + { _userInput: true }; } - else { - autocompleter({ - input: element, - debounceWaitMs: 300, - fetch: (text, update) => { - const query = text; - this.provider.search(query).then(update); - }, - render: (address) => { - const div = this.ce('div'); - div.textContent = this.getDisplayValue(address); - return div; - }, - onSelect: (address) => { - this.onSelectAddress(address, element, index); - }, - }); + + if (this.disabled) { + attr.disabled = 'disabled'; } - this.addEventListener(element, 'blur', () => { - if (!element) { - return; - } + _.defaults(attr, this.component.attributes); + + return attr; + } + + get templateName() { + return 'address'; + } + + get gridTemplateName() { + return 'multiValueTable'; + } + + get rowTemplateName() { + return 'multiValueRow'; + } + + get hasChildren() { + return !this.isMultiple && (this.builderMode || this.manualModeEnabled); + } - if (element.value) { - element.value = this.getDisplayValue(this.isMultiple ? this.address[index] : this.address); - } + get addAnother() { + return this.t(this.component.addAnother || 'Add Another'); + } + + renderElement(value) { + return this.renderTemplate(this.templateName, { + children: this.hasChildren ? this.renderComponents() : '', + nestedKey: this.nestedKey, + inputAttributes: this.searchInputAttributes, + ref: { + modeSwitcher: AddressComponent.modeSwitcherRef, + removeValueIcon: AddressComponent.removeValueIconRef, + searchInput: AddressComponent.searchInputRef, + }, + displayValue: this.getDisplayValue(value), + mode: { + autocomplete: this.autocompleteMode, + manual: this.manualMode, + }, }); + } - this.addEventListener(element, 'keyup', () => { - if (!element) { - return; - } + renderRow(value, index) { + return this.renderTemplate(this.rowTemplateName, { + index, + disabled: this.disabled, + element: `${this.renderElement(value, index)}`, + }); + } - if (!element.value) { - this.clearAddress(element, index); - } + renderGrid() { + return this.renderTemplate(this.gridTemplateName, { + rows: this.address.map(this.renderRow.bind(this)).join(''), + disabled: this.disabled, + addAnother: this.addAnother, }); - } - }); - if (this.addRowButton) { - this.addEventListener(this.addRowButton, 'click', event => { - event.preventDefault(); - this.addRow(); - }); - } - this.removeRowButton.forEach((removeRowButton, index) => { - this.addEventListener(removeRowButton, 'click', event => { - event.preventDefault(); - this.removeValue(index); - }); - }); - - if (this.modeSwitcher) { - this.addEventListener(this.modeSwitcher, 'change', () => { - if (!this.modeSwitcher) { - return; + } + + render() { + if (this.isMultiple) { + return super.render(this.renderGrid()); } - this.dataValue = this.emptyValue; - this.mode = this.modeSwitcher.checked - ? AddressComponentMode.Manual - : AddressComponentMode.Autocomplete; + return super.render(this.renderElement()); + } - if (!this.builderMode) { - if (this.manualMode) { - this.restoreComponentsContext(); - } + onSelectAddress(address, element, index) { + if (this.isMultiple) { + this.address[index] = address; + this.address = [...this.address]; + } else { + this.address = address; + } - this.triggerChange({ + this.triggerChange({ modified: true, - }); + }); + + if (element) { + element.value = this.getDisplayValue( + this.isMultiple ? this.address[index] : this.address, + ); } - this.redraw(); - }); + this.updateRemoveIcon(index); } - if (!this.builderMode) { - this.removeValueIcon.forEach((removeValueIcon, index) => { - this.updateRemoveIcon(index); + addRow() { + this.address = this.address.concat(this.emptyValue); + super.redraw(); + } - const removeValueHandler = () => { - const searchInput = this.searchInput?.[index]; - this.clearAddress(searchInput, index); + attach(element) { + const result = ( + this.builderMode || this.manualMode + ? super.attach + : Field.prototype.attach + ).call(this, element); - if (searchInput) { - searchInput.focus(); - } - }; + if (!this.builderMode) { + if (!this.provider && this.component.provider) { + const { provider, providerOptions } = this.component; + this.provider = this.initializeProvider( + provider, + providerOptions, + ); + } + } - this.addEventListener(removeValueIcon, 'click', removeValueHandler); - this.addEventListener(removeValueIcon, 'keydown', ({ key }) => { - if (key === 'Enter') { - removeValueHandler(); - } + this.loadRefs(element, { + [AddressComponent.addRowButtonRef]: 'single', + [AddressComponent.modeSwitcherRef]: 'single', + [AddressComponent.removeRowButtonRef]: 'multiple', + [AddressComponent.removeValueIconRef]: 'multiple', + [AddressComponent.searchInputRef]: 'multiple', }); - }); - _.each(this.refs.searchInput || [], el => this.addFocusBlurEvents(el)); - } - - return result; - } - - addChildComponent(component) { - component.customConditional = ChildConditional; - } + this.searchInput.forEach((element, index) => { + if (!this.builderMode && element && this.provider) { + if (this.component.provider === 'google') { + this.provider.attachAutocomplete( + element, + index, + this.onSelectAddress.bind(this), + ); + } else { + autocompleter({ + input: element, + debounceWaitMs: 300, + fetch: (text, update) => { + const query = text; + this.provider.search(query).then(update); + }, + render: (address) => { + const div = this.ce('div'); + div.textContent = this.getDisplayValue(address); + return div; + }, + onSelect: (address) => { + this.onSelectAddress(address, element, index); + }, + }); + } + + this.addEventListener(element, 'blur', () => { + if (!element) { + return; + } + + if (element.value) { + element.value = this.getDisplayValue( + this.isMultiple + ? this.address[index] + : this.address, + ); + } + }); + + this.addEventListener(element, 'keyup', () => { + if (!element) { + return; + } + + if (!element.value) { + this.clearAddress(element, index); + } + }); + } + }); + if (this.addRowButton) { + this.addEventListener(this.addRowButton, 'click', (event) => { + event.preventDefault(); + this.addRow(); + }); + } + this.removeRowButton.forEach((removeRowButton, index) => { + this.addEventListener(removeRowButton, 'click', (event) => { + event.preventDefault(); + this.removeValue(index); + }); + }); - redraw() { - const modeSwitcherInFocus = (this.modeSwitcher && (document.activeElement === this.modeSwitcher)); + if (this.modeSwitcher) { + this.addEventListener(this.modeSwitcher, 'change', () => { + if (!this.modeSwitcher) { + return; + } + + this.dataValue = this.emptyValue; + this.mode = this.modeSwitcher.checked + ? AddressComponentMode.Manual + : AddressComponentMode.Autocomplete; + + if (!this.builderMode) { + if (this.manualMode) { + this.restoreComponentsContext(); + } + + this.triggerChange({ + modified: true, + }); + } + + this.redraw(); + }); + } - return super.redraw() - .then((result) => { - if (modeSwitcherInFocus && this.modeSwitcher) { - this.modeSwitcher.focus(); + if (!this.builderMode) { + this.removeValueIcon.forEach((removeValueIcon, index) => { + this.updateRemoveIcon(index); + + const removeValueHandler = () => { + const searchInput = this.searchInput?.[index]; + this.clearAddress(searchInput, index); + + if (searchInput) { + searchInput.focus(); + } + }; + + this.addEventListener( + removeValueIcon, + 'click', + removeValueHandler, + ); + this.addEventListener(removeValueIcon, 'keydown', ({ key }) => { + if (key === 'Enter') { + removeValueHandler(); + } + }); + }); + + _.each(this.refs.searchInput || [], (el) => + this.addFocusBlurEvents(el), + ); } return result; - }); - } - - clearAddress(element, index) { - if (!this.isEmpty()) { - this.triggerChange(); } - if (this.address?.[index]) { - this.address[index] = this.emptyValue; - } - else { - this.address = this.emptyValue; + addChildComponent(component) { + component.customConditional = ChildConditional; } - if (element) { - element.value = ''; + + redraw() { + const modeSwitcherInFocus = + this.modeSwitcher && document.activeElement === this.modeSwitcher; + + return super.redraw().then((result) => { + if (modeSwitcherInFocus && this.modeSwitcher) { + this.modeSwitcher.focus(); + } + + return result; + }); } - this.updateRemoveIcon(index); - } - getDisplayValue(value = this.address) { - return (this.provider && !this.manualMode) - ? this.provider.getDisplayValue(value) - : ''; - } + clearAddress(element, index) { + if (!this.isEmpty()) { + this.triggerChange(); + } - validateMultiple() { - return this.isMultiple; - } + if (this.address?.[index]) { + this.address[index] = this.emptyValue; + } else { + this.address = this.emptyValue; + } + if (element) { + element.value = ''; + } + this.updateRemoveIcon(index); + } - updateRemoveIcon(index) { - const removeValueIcon = this.removeValueIcon?.[index]; - if (removeValueIcon) { - const value = this.isMultiple ? this.address[index] : this.address; - if (this.isEmpty(value) || this.disabled) { - this.addClass(removeValueIcon, RemoveValueIconHiddenClass); - } - else { - this.removeClass(removeValueIcon, RemoveValueIconHiddenClass); - } + getDisplayValue(value = this.address) { + return this.provider && !this.manualMode + ? this.provider.getDisplayValue(value) + : ''; } - } - getValueAsString(value, options) { - if (!value) { - return ''; + validateMultiple() { + return this.isMultiple; } - const normalizedValue = this.normalizeValue(value); + updateRemoveIcon(index) { + const removeValueIcon = this.removeValueIcon?.[index]; + if (removeValueIcon) { + const value = this.isMultiple ? this.address[index] : this.address; + if (this.isEmpty(value) || this.disabled) { + this.addClass(removeValueIcon, RemoveValueIconHiddenClass); + } else { + this.removeClass(removeValueIcon, RemoveValueIconHiddenClass); + } + } + } - const { - address, - mode, - } = ( - this.manualModeEnabled - ? normalizedValue - : { - address: normalizedValue, - mode: AddressComponentMode.Autocomplete, + getValueAsString(value, options) { + if (!value) { + return ''; } - ); - const valueInManualMode = (mode === AddressComponentMode.Manual); - if (this.provider && !valueInManualMode) { - return this.getDisplayValue(address); - } + const normalizedValue = this.normalizeValue(value); - if (valueInManualMode) { - if (this.component.manualModeViewString) { - return this.interpolate(this.component.manualModeViewString, { - address, - data: this.data, - component: this.component, - }); - } + const { address, mode } = this.manualModeEnabled + ? normalizedValue + : { + address: normalizedValue, + mode: AddressComponentMode.Autocomplete, + }; + const valueInManualMode = mode === AddressComponentMode.Manual; - return this.getComponents() - .filter((component) => component.hasValue(address)) - .map((component) => [component, _.get(address, component.key)]) - .filter(([component, componentValue]) => !component.isEmpty(componentValue)) - .map(([component, componentValue]) => component.getValueAsString(componentValue, options)) - .join(', '); - } + if (this.provider && !valueInManualMode) { + return this.getDisplayValue(address); + } - return super.getValueAsString(address, options); - } + if (valueInManualMode) { + if (this.component.manualModeViewString) { + return this.interpolate(this.component.manualModeViewString, { + address, + data: this.data, + component: this.component, + }); + } + + return this.getComponents() + .filter((component) => component.hasValue(address)) + .map((component) => [component, _.get(address, component.key)]) + .filter( + ([component, componentValue]) => + !component.isEmpty(componentValue), + ) + .map(([component, componentValue]) => + component.getValueAsString(componentValue, options), + ) + .join(', '); + } - focus() { - if (this.searchInput && this.searchInput[0]) { - this.searchInput[0].focus(); + return super.getValueAsString(address, options); + } + + focus() { + if (this.searchInput && this.searchInput[0]) { + this.searchInput[0].focus(); + } } - } } diff --git a/src/components/address/Address.unit.js b/src/components/address/Address.unit.js index 3e5c51e051..1941c78d25 100644 --- a/src/components/address/Address.unit.js +++ b/src/components/address/Address.unit.js @@ -4,230 +4,264 @@ import assert from 'power-assert'; import { Formio } from './../../Formio'; import _ from 'lodash'; -import { - comp1, - comp2, - comp3, - comp4, -} from './fixtures'; - -describe('Address Component', function() { - it('Should build an address component', function() { - return Harness.testCreate(AddressComponent, comp1); - }); - - it('Should set default value and clear it on "clear icon" click (openStreetMap provider)', function(done) { - const form = _.cloneDeep(comp2); - const element = document.createElement('div'); - - Formio.createForm(element, form).then(form => { - const address = form.getComponent('address'); - assert.equal(!!address.provider, true); - assert.equal(address.refs.searchInput[0].value, 'Dallas County, Texas, United States'); - const clearIcon = address.refs.removeValueIcon[0]; - - const clickEvent = new Event('click'); - clearIcon.dispatchEvent(clickEvent); - - setTimeout(() => { - assert.equal(address.refs.searchInput[0].value, ''); - - document.innerHTML = ''; - done(); - }, 300); - }).catch(done); - }); - - it('Should disable "clear icon"', function(done) { - const form = _.cloneDeep(comp2); - const element = document.createElement('div'); - form.components[0].disableClearIcon = true; - - Formio.createForm(element, form).then(form => { - const address = form.getComponent('address'); - assert.equal(!!address.provider, true); - assert.equal(address.refs.removeValueIcon.length, 0); - - done(); - }).catch(done); - }); - - it('Test manual mode', function(done) { - const form = _.cloneDeep(comp2); - const element = document.createElement('div'); - form.components[0].disableClearIcon = true; - form.components[0].enableManualMode = true; - form.components[0].switchToManualModeLabel = 'Check it to switch to manual mode'; - Formio.createForm(element, form).then(form => { - const address = form.getComponent('address'); - assert.equal(!!address.provider, true); - assert.equal(address.mode, 'autocomplete'); - assert.equal(address.refs.modeSwitcher.checked, false, 'Manual mode should be turned off'); - assert.equal(address.refs.modeSwitcher.nextElementSibling.textContent, 'Check it to switch to manual mode', 'Should set custom label for manual mode checkbox'); - - address.components.forEach(comp => { - assert.equal(comp.visible, false, 'Manual mode components should be hidden'); - }); - address.refs.modeSwitcher.checked = true; - - const changeEvent = new Event('change'); - address.refs.modeSwitcher.dispatchEvent(changeEvent); - - setTimeout(() => { - assert.equal(address.refs.modeSwitcher.checked, true, 'Manual mode should be turned on'); - assert.equal(address.mode, 'manual'); - const manualModeValue = { - address1: 'test address1', - address2: 'test address2', - city: 'test city', - country: 'test country', - state: 'test state', - zip: '1111111', - }; +import { comp1, comp2, comp3, comp4 } from './fixtures'; - address.components.forEach(comp => { - assert.equal(comp.visible, true, 'Manual mode components should be visible'); +describe('Address Component', function () { + it('Should build an address component', function () { + return Harness.testCreate(AddressComponent, comp1); + }); - const inputEvent = new Event('input'); - const input = comp.refs.input[0]; - input.value = manualModeValue[`${comp.component.key}`]; - input.dispatchEvent(inputEvent); - }); + it('Should set default value and clear it on "clear icon" click (openStreetMap provider)', function (done) { + const form = _.cloneDeep(comp2); + const element = document.createElement('div'); + + Formio.createForm(element, form) + .then((form) => { + const address = form.getComponent('address'); + assert.equal(!!address.provider, true); + assert.equal( + address.refs.searchInput[0].value, + 'Dallas County, Texas, United States', + ); + const clearIcon = address.refs.removeValueIcon[0]; + + const clickEvent = new Event('click'); + clearIcon.dispatchEvent(clickEvent); + + setTimeout(() => { + assert.equal(address.refs.searchInput[0].value, ''); + + document.innerHTML = ''; + done(); + }, 300); + }) + .catch(done); + }); + + it('Should disable "clear icon"', function (done) { + const form = _.cloneDeep(comp2); + const element = document.createElement('div'); + form.components[0].disableClearIcon = true; + + Formio.createForm(element, form) + .then((form) => { + const address = form.getComponent('address'); + assert.equal(!!address.provider, true); + assert.equal(address.refs.removeValueIcon.length, 0); + + done(); + }) + .catch(done); + }); + + it('Test manual mode', function (done) { + const form = _.cloneDeep(comp2); + const element = document.createElement('div'); + form.components[0].disableClearIcon = true; + form.components[0].enableManualMode = true; + form.components[0].switchToManualModeLabel = + 'Check it to switch to manual mode'; + Formio.createForm(element, form) + .then((form) => { + const address = form.getComponent('address'); + assert.equal(!!address.provider, true); + assert.equal(address.mode, 'autocomplete'); + assert.equal( + address.refs.modeSwitcher.checked, + false, + 'Manual mode should be turned off', + ); + assert.equal( + address.refs.modeSwitcher.nextElementSibling.textContent, + 'Check it to switch to manual mode', + 'Should set custom label for manual mode checkbox', + ); + + address.components.forEach((comp) => { + assert.equal( + comp.visible, + false, + 'Manual mode components should be hidden', + ); + }); + address.refs.modeSwitcher.checked = true; + + const changeEvent = new Event('change'); + address.refs.modeSwitcher.dispatchEvent(changeEvent); + + setTimeout(() => { + assert.equal( + address.refs.modeSwitcher.checked, + true, + 'Manual mode should be turned on', + ); + assert.equal(address.mode, 'manual'); + const manualModeValue = { + address1: 'test address1', + address2: 'test address2', + city: 'test city', + country: 'test country', + state: 'test state', + zip: '1111111', + }; + + address.components.forEach((comp) => { + assert.equal( + comp.visible, + true, + 'Manual mode components should be visible', + ); - setTimeout(() => { - assert.deepEqual(address.dataValue, { address: manualModeValue, mode: 'manual' }, 'Should set manual mode value'); - - document.innerHTML = ''; - done(); - }, 300); - }, 300); - }).catch(done); - }); - - it('Should close modal window without showing dialog if value not changed', function(done) { - const form = _.cloneDeep(comp3); - const element = document.createElement('div'); - - Formio.createForm(element, form).then(form => { - const value = { - 'address_components': [ - { - 'long_name': 'Los Angeles', - 'short_name': 'Los Angeles', - types: ['locality', 'political'], - }, - { - 'long_name': 'Los Angeles County', - 'short_name': 'Los Angeles County', - types: ['administrative_area_level_2', 'political'], - }, - { - 'long_name': 'California', - 'short_name': 'CA', - types: ['administrative_area_level_1', 'political'], - }, - { - 'long_name': 'United States', - 'short_name': 'US', - types: ['country', 'political'], - }, - ], - 'formatted_address': 'Los Angeles, CA, USA', - geometry: { - location: { lat: 34.0522342, lng: -118.2436849 }, - viewport: { - south: 33.70365193147634, - west: -118.6681759484859, - north: 34.33730608759191, - east: -118.155289077463, - }, - }, - 'place_id': 'ChIJE9on3F3HwoAR9AhGJW_fL-I', - types: ['locality', 'political'], - formattedPlace: 'Los Angeles, CA, USA', - }; - const address = form.getComponent('address'); - const openModalButton = address.componentModal.refs.openModal; - const clickEvent = new Event('click'); - openModalButton.dispatchEvent(clickEvent); - - setTimeout(() => { - assert.equal(address.componentModal.isOpened, true); - address.dataValue = value; - address.componentModal.closeModal(); - address.redraw(); - - setTimeout(() => { - address.componentModal.isOpened = true; - openModalButton.dispatchEvent(clickEvent); - - setTimeout(() => { - assert.equal(address.componentModal.isOpened, true); - assert.equal(!!address.dataValue, true); - const modalOverlayButton = address.componentModal.refs.modalOverlay; - modalOverlayButton.dispatchEvent(clickEvent); + const inputEvent = new Event('input'); + const input = comp.refs.input[0]; + input.value = manualModeValue[`${comp.component.key}`]; + input.dispatchEvent(inputEvent); + }); + + setTimeout(() => { + assert.deepEqual( + address.dataValue, + { address: manualModeValue, mode: 'manual' }, + 'Should set manual mode value', + ); + + document.innerHTML = ''; + done(); + }, 300); + }, 300); + }) + .catch(done); + }); + + it('Should close modal window without showing dialog if value not changed', function (done) { + const form = _.cloneDeep(comp3); + const element = document.createElement('div'); + + Formio.createForm(element, form) + .then((form) => { + const value = { + address_components: [ + { + long_name: 'Los Angeles', + short_name: 'Los Angeles', + types: ['locality', 'political'], + }, + { + long_name: 'Los Angeles County', + short_name: 'Los Angeles County', + types: ['administrative_area_level_2', 'political'], + }, + { + long_name: 'California', + short_name: 'CA', + types: ['administrative_area_level_1', 'political'], + }, + { + long_name: 'United States', + short_name: 'US', + types: ['country', 'political'], + }, + ], + formatted_address: 'Los Angeles, CA, USA', + geometry: { + location: { lat: 34.0522342, lng: -118.2436849 }, + viewport: { + south: 33.70365193147634, + west: -118.6681759484859, + north: 34.33730608759191, + east: -118.155289077463, + }, + }, + place_id: 'ChIJE9on3F3HwoAR9AhGJW_fL-I', + types: ['locality', 'political'], + formattedPlace: 'Los Angeles, CA, USA', + }; + const address = form.getComponent('address'); + const openModalButton = address.componentModal.refs.openModal; + const clickEvent = new Event('click'); + openModalButton.dispatchEvent(clickEvent); + + setTimeout(() => { + assert.equal(address.componentModal.isOpened, true); + address.dataValue = value; + address.componentModal.closeModal(); + address.redraw(); + + setTimeout(() => { + address.componentModal.isOpened = true; + openModalButton.dispatchEvent(clickEvent); + + setTimeout(() => { + assert.equal(address.componentModal.isOpened, true); + assert.equal(!!address.dataValue, true); + const modalOverlayButton = + address.componentModal.refs.modalOverlay; + modalOverlayButton.dispatchEvent(clickEvent); + + setTimeout(() => { + assert.equal( + !!address.componentModal.isValueChanged(), + false, + ); + assert.equal( + !!address.componentModal.dialogElement, + false, + ); + done(); + }, 200); + }, 200); + }, 200); + }, 200); + }) + .catch(done); + }); + + it('Should correctly display component that has a conditional based on the Address component', function (done) { + const value = { + place_id: 298032694, + licence: + 'Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright', + osm_type: 'relation', + osm_id: 1180520, + boundingbox: [37.4148293, 37.907822, -93.191483, -92.845795], + lat: 37.6832712, + lon: -93.0219376, + display_name: 'Dallas County, Missouri, United States', + class: 'boundary', + type: 'administrative', + importance: 0.6131235182618818, + icon: 'https://nominatim.openstreetmap.org/ui/mapicons/poi_boundary_administrative.p.20.png', + address: { + county: 'Dallas County', + state: 'Missouri', + 'ISO3166-2-lvl4': 'US-MO', + country: 'United States', + country_code: 'us', + }, + }; + + const form = _.cloneDeep(comp4); + const element = document.createElement('div'); + + Formio.createForm(element, form).then((form) => { + const address = form.getComponent('address'); + const textfield = form.getComponent('textField'); setTimeout(() => { - assert.equal(!!address.componentModal.isValueChanged(), false); - assert.equal(!!address.componentModal.dialogElement, false); - done(); - }, 200); - }, 200); - }, 200); - }, 200); - }).catch(done); - }); - - it('Should correctly display component that has a conditional based on the Address component', function(done) { - const value = { - 'place_id': 298032694, - licence: 'Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright', - 'osm_type': 'relation', - 'osm_id': 1180520, - boundingbox: [ - 37.4148293, - 37.907822, - -93.191483, - -92.845795 - ], - lat: 37.6832712, - lon: -93.0219376, - 'display_name': 'Dallas County, Missouri, United States', - class: 'boundary', - type: 'administrative', - importance: 0.6131235182618818, - icon: 'https://nominatim.openstreetmap.org/ui/mapicons/poi_boundary_administrative.p.20.png', - address: { - county: 'Dallas County', - state: 'Missouri', - 'ISO3166-2-lvl4': 'US-MO', - country: 'United States', - 'country_code': 'us' - } - }; - - const form = _.cloneDeep(comp4); - const element = document.createElement('div'); - - Formio.createForm(element, form).then(form => { - const address = form.getComponent('address'); - const textfield = form.getComponent('textField'); - - setTimeout(() => { - address.setValue(value); - - setTimeout(() => { - assert.equal(textfield.visible, true); - const clearIcon = address.refs.removeValueIcon[0]; - const clickEvent = new Event('click'); - clearIcon.dispatchEvent(clickEvent); - - setTimeout(() => { - assert.equal(textfield.visible, false); - done(); - }, 300); - }, 300); - }, 300); + address.setValue(value); + + setTimeout(() => { + assert.equal(textfield.visible, true); + const clearIcon = address.refs.removeValueIcon[0]; + const clickEvent = new Event('click'); + clearIcon.dispatchEvent(clickEvent); + + setTimeout(() => { + assert.equal(textfield.visible, false); + done(); + }, 300); + }, 300); + }, 300); + }); }); - }); }); diff --git a/src/components/address/editForm/Address.edit.data.js b/src/components/address/editForm/Address.edit.data.js index 5b84522e89..613e7609dc 100644 --- a/src/components/address/editForm/Address.edit.data.js +++ b/src/components/address/editForm/Address.edit.data.js @@ -1,23 +1,23 @@ export default [ - { - key: 'multiple', - customConditional: ({ data }) => !data.enableManualMode, - }, - { - type: 'address', - label: 'Default Value', - key: 'defaultValue', - weight: 5, - placeholder: 'Default Value', - tooltip: 'The Default Value will be the value for this field, before user interaction. Having a default value will override the placeholder text.', - input: true, - customDefaultValue: ({ instance }) => ( - instance.manualModeEnabled - ? { - mode: 'autocomplete', - address: {}, - } - : {} - ), - }, + { + key: 'multiple', + customConditional: ({ data }) => !data.enableManualMode, + }, + { + type: 'address', + label: 'Default Value', + key: 'defaultValue', + weight: 5, + placeholder: 'Default Value', + tooltip: + 'The Default Value will be the value for this field, before user interaction. Having a default value will override the placeholder text.', + input: true, + customDefaultValue: ({ instance }) => + instance.manualModeEnabled + ? { + mode: 'autocomplete', + address: {}, + } + : {}, + }, ]; diff --git a/src/components/address/editForm/Address.edit.display.js b/src/components/address/editForm/Address.edit.display.js index 1f39081278..eb38d89384 100644 --- a/src/components/address/editForm/Address.edit.display.js +++ b/src/components/address/editForm/Address.edit.display.js @@ -1,42 +1,42 @@ export default [ - { - weight: 20, - type: 'checkbox', - input: true, - key: 'enableManualMode', - label: 'Enable Manual Mode', - tooltip: 'Should Manual Mode be enabled for that component or not.', - customConditional: ({ data }) => !data.multiple, - }, - { - weight: 30, - type: 'textfield', - input: true, - key: 'switchToManualModeLabel', - label: 'Switch To Manual Mode Label', - placeholder: 'Switch To Manual Mode Label', - tooltip: 'The label for the checkbox used to switch to manual mode.', - validate: { - required: true, + { + weight: 20, + type: 'checkbox', + input: true, + key: 'enableManualMode', + label: 'Enable Manual Mode', + tooltip: 'Should Manual Mode be enabled for that component or not.', + customConditional: ({ data }) => !data.multiple, + }, + { + weight: 30, + type: 'textfield', + input: true, + key: 'switchToManualModeLabel', + label: 'Switch To Manual Mode Label', + placeholder: 'Switch To Manual Mode Label', + tooltip: 'The label for the checkbox used to switch to manual mode.', + validate: { + required: true, + }, + customConditional: ({ data }) => Boolean(data.enableManualMode), + }, + { + weight: 40, + type: 'checkbox', + input: true, + key: 'disableClearIcon', + label: 'Disable Clear Icon', + tooltip: "Clear Icon allows easily clear component's value.", + }, + { + type: 'textfield', + label: 'Add Another Text', + key: 'addAnother', + tooltip: 'Set the text of the Add Another button.', + placeholder: 'Add Another', + weight: 410, + input: true, + customConditional: ({ data }) => data.multiple, }, - customConditional: ({ data }) => Boolean(data.enableManualMode), - }, - { - weight: 40, - type: 'checkbox', - input: true, - key: 'disableClearIcon', - label: 'Disable Clear Icon', - tooltip: 'Clear Icon allows easily clear component\'s value.', - }, - { - type: 'textfield', - label: 'Add Another Text', - key: 'addAnother', - tooltip: 'Set the text of the Add Another button.', - placeholder: 'Add Another', - weight: 410, - input: true, - customConditional: ({ data }) => data.multiple, - }, ]; diff --git a/src/components/address/editForm/Address.edit.provider.js b/src/components/address/editForm/Address.edit.provider.js index 2e8f2842b0..a11857d274 100644 --- a/src/components/address/editForm/Address.edit.provider.js +++ b/src/components/address/editForm/Address.edit.provider.js @@ -3,151 +3,161 @@ import _ from 'lodash'; import { Formio } from '../../../Formio'; export default [ - { - type: 'select', - input: true, - key: 'provider', - label: 'Provider', - placeholder: 'Select your address search provider', - weight: 0, - tooltip: 'Which address search service should be used.', - valueProperty: 'value', - dataSrc: 'custom', - data: { - custom() { - return _.values(Formio.Providers.getProviders('address')).sort().map((provider) => ({ - label: provider.displayName, - value: provider.name, - })); - }, + { + type: 'select', + input: true, + key: 'provider', + label: 'Provider', + placeholder: 'Select your address search provider', + weight: 0, + tooltip: 'Which address search service should be used.', + valueProperty: 'value', + dataSrc: 'custom', + data: { + custom() { + return _.values(Formio.Providers.getProviders('address')) + .sort() + .map((provider) => ({ + label: provider.displayName, + value: provider.name, + })); + }, + }, + validate: { + required: true, + }, }, - validate: { - required: true, + { + type: 'textfield', + input: true, + key: "providerOptions.params['subscription-key']", + label: 'Subscription Key', + placeholder: 'Enter Subscription Key', + weight: 10, + tooltip: 'Use your Azure Maps subscription key here.', + validate: { + required: true, + }, + conditional: { + json: { '===': [{ var: 'data.provider' }, 'azure'] }, + }, }, - }, - { - type: 'textfield', - input: true, - key: "providerOptions.params['subscription-key']", - label: 'Subscription Key', - placeholder: 'Enter Subscription Key', - weight: 10, - tooltip: 'Use your Azure Maps subscription key here.', - validate: { - required: true, + { + type: 'textfield', + input: true, + key: 'providerOptions.url', + label: 'Url', + placeholder: 'Enter Url', + weight: 10, + tooltip: + 'Url to the service which should be used to search addresses for autocomplete.', + validate: { + required: true, + }, + conditional: { + json: { '===': [{ var: 'data.provider' }, 'custom'] }, + }, }, - conditional: { - json: { '===': [{ var: 'data.provider' }, 'azure'] }, + { + type: 'textfield', + input: true, + key: 'providerOptions.queryProperty', + label: 'Query Property', + defaultValue: 'query', + placeholder: 'Enter Query Property', + weight: 20, + tooltip: + 'Which query param should be used to pass as a search string. Default is `query`.', + conditional: { + json: { '===': [{ var: 'data.provider' }, 'custom'] }, + }, }, - }, - { - type: 'textfield', - input: true, - key: 'providerOptions.url', - label: 'Url', - placeholder: 'Enter Url', - weight: 10, - tooltip: 'Url to the service which should be used to search addresses for autocomplete.', - validate: { - required: true, + { + type: 'textfield', + input: true, + key: 'providerOptions.responseProperty', + label: 'Response Property', + placeholder: 'Enter Response Property', + weight: 30, + tooltip: + 'The property within the response data, where iterable addresses reside. For example: results.', + conditional: { + json: { '===': [{ var: 'data.provider' }, 'custom'] }, + }, }, - conditional: { - json: { '===': [{ var: 'data.provider' }, 'custom'] }, + { + type: 'textfield', + input: true, + key: 'providerOptions.displayValueProperty', + label: 'Display Value Property', + placeholder: 'Display Value Property', + weight: 40, + tooltip: + 'The property of each address in the response to use as the display value.', + conditional: { + json: { '===': [{ var: 'data.provider' }, 'custom'] }, + }, }, - }, - { - type: 'textfield', - input: true, - key: 'providerOptions.queryProperty', - label: 'Query Property', - defaultValue: 'query', - placeholder: 'Enter Query Property', - weight: 20, - tooltip: 'Which query param should be used to pass as a search string. Default is `query`.', - conditional: { - json: { '===': [{ var: 'data.provider' }, 'custom'] }, + { + type: 'textarea', + input: true, + key: 'providerOptions.params', + label: 'Params', + placeholder: '{ ... }', + weight: 50, + rows: 5, + editor: 'ace', + as: 'json', + tooltip: + 'Additional query params can be specified here in a way of JSON object.', + conditional: { + json: { '===': [{ var: 'data.provider' }, 'custom'] }, + }, }, - }, - { - type: 'textfield', - input: true, - key: 'providerOptions.responseProperty', - label: 'Response Property', - placeholder: 'Enter Response Property', - weight: 30, - tooltip: 'The property within the response data, where iterable addresses reside. For example: results.', - conditional: { - json: { '===': [{ var: 'data.provider' }, 'custom'] }, + { + type: 'textfield', + input: true, + key: 'providerOptions.params.key', + label: 'API Key', + placeholder: 'Enter API Key', + weight: 10, + tooltip: 'Use your Google API key here.', + validate: { + required: true, + }, + conditional: { + json: { '===': [{ var: 'data.provider' }, 'google'] }, + }, }, - }, - { - type: 'textfield', - input: true, - key: 'providerOptions.displayValueProperty', - label: 'Display Value Property', - placeholder: 'Display Value Property', - weight: 40, - tooltip: 'The property of each address in the response to use as the display value.', - conditional: { - json: { '===': [{ var: 'data.provider' }, 'custom'] }, + { + type: 'textarea', + input: true, + key: 'providerOptions.params.autocompleteOptions', + label: 'Provider options', + placeholder: 'Enter provider options as JSON object', + defaultValue: {}, + weight: 60, + rows: 5, + as: 'json', + editor: 'ace', + tooltip: + "Specify Google Maps Autocomplete options used for address searching as JSON object. Follow the link for available options", + conditional: { + json: { '===': [{ var: 'data.provider' }, 'google'] }, + }, }, - }, - { - type: 'textarea', - input: true, - key: 'providerOptions.params', - label: 'Params', - placeholder: '{ ... }', - weight: 50, - rows: 5, - editor: 'ace', - as: 'json', - tooltip: 'Additional query params can be specified here in a way of JSON object.', - conditional: { - json: { '===': [{ var: 'data.provider' }, 'custom'] }, + { + type: 'textarea', + input: true, + key: 'manualModeViewString', + label: 'Manual Mode View String', + placeholder: 'Enter Manual Mode View String', + description: + '"address" variable references component value, "data" - submission data and "component" - address component schema.', + weight: 60, + rows: 5, + editor: 'ace', + tooltip: + 'Specify template which should be when quering view string for the component value entered in manual mode. This string is used in table view, CSV export and email rendering. When left blank combined value of all components joined with comma will be used.', }, - }, - { - type: 'textfield', - input: true, - key: 'providerOptions.params.key', - label: 'API Key', - placeholder: 'Enter API Key', - weight: 10, - tooltip: 'Use your Google API key here.', - validate: { - required: true, - }, - conditional: { - json: { '===': [{ var: 'data.provider' }, 'google'] }, - }, - }, - { - type: 'textarea', - input: true, - key: 'providerOptions.params.autocompleteOptions', - label: 'Provider options', - placeholder: 'Enter provider options as JSON object', - defaultValue:{}, - weight: 60, - rows: 5, - as: 'json', - editor: 'ace', - tooltip: 'Specify Google Maps Autocomplete options used for address searching as JSON object. Follow the link for available options', - conditional: { - json: { '===': [{ var: 'data.provider' }, 'google'] }, - }, - }, - { - type: 'textarea', - input: true, - key: 'manualModeViewString', - label: 'Manual Mode View String', - placeholder: 'Enter Manual Mode View String', - description: '"address" variable references component value, "data" - submission data and "component" - address component schema.', - weight: 60, - rows: 5, - editor: 'ace', - tooltip: 'Specify template which should be when quering view string for the component value entered in manual mode. This string is used in table view, CSV export and email rendering. When left blank combined value of all components joined with comma will be used.', - }, ]; diff --git a/src/components/address/fixtures/comp1.js b/src/components/address/fixtures/comp1.js index 11fc37ad13..25afb5b509 100644 --- a/src/components/address/fixtures/comp1.js +++ b/src/components/address/fixtures/comp1.js @@ -1,71 +1,69 @@ export default { - 'input': true, - 'tableView': true, - 'label': 'address', - 'key': 'address', - 'placeholder': '', - 'multiple': false, - 'protected': false, - 'clearOnHide': true, - 'unique': false, - 'persistent': true, - 'provider': 'nominatim', - 'validate': { - 'customPrivate': false, - 'custom': '', - 'required': false, - }, - 'type': 'address', - 'tags': [ - - ], - 'conditional': { - 'show': '', - 'when': null, - 'eq': '', - }, - 'components': [ - { - label: 'Street', - tableView: true, - key: 'street', - type: 'textfield', - input: true, + input: true, + tableView: true, + label: 'address', + key: 'address', + placeholder: '', + multiple: false, + protected: false, + clearOnHide: true, + unique: false, + persistent: true, + provider: 'nominatim', + validate: { + customPrivate: false, + custom: '', + required: false, }, - { - label: 'City', - tableView: true, - key: 'city', - type: 'textfield', - input: true, + type: 'address', + tags: [], + conditional: { + show: '', + when: null, + eq: '', }, - { - label: 'County', - tableView: true, - key: 'county', - type: 'textfield', - input: true, - }, - { - label: 'State', - tableView: true, - key: 'state', - type: 'textfield', - input: true, - }, - { - label: 'Zip Code', - tableView: true, - key: 'zip', - type: 'textfield', - input: true, - }, - { - label: 'Country', - tableView: true, - key: 'country', - type: 'textfield', - input: true, - }, - ], + components: [ + { + label: 'Street', + tableView: true, + key: 'street', + type: 'textfield', + input: true, + }, + { + label: 'City', + tableView: true, + key: 'city', + type: 'textfield', + input: true, + }, + { + label: 'County', + tableView: true, + key: 'county', + type: 'textfield', + input: true, + }, + { + label: 'State', + tableView: true, + key: 'state', + type: 'textfield', + input: true, + }, + { + label: 'Zip Code', + tableView: true, + key: 'zip', + type: 'textfield', + input: true, + }, + { + label: 'Country', + tableView: true, + key: 'country', + type: 'textfield', + input: true, + }, + ], }; diff --git a/src/components/address/fixtures/comp2.js b/src/components/address/fixtures/comp2.js index f0d71a37c3..375e092ef6 100644 --- a/src/components/address/fixtures/comp2.js +++ b/src/components/address/fixtures/comp2.js @@ -1,101 +1,113 @@ export default { - type: 'form', - components: [ - { - label: 'Address', - tableView: false, - provider: 'nominatim', - key: 'address', - type: 'address', - providerOptions: { - params: { - autocompleteOptions: {} - }, - url: 'undefined' - }, - input: true, - components: [ - { - label: 'Address 1', - tableView: false, - key: 'address1', - type: 'textfield', - input: true, - customConditional: "show = _.get(instance, 'parent.manualMode', false);" - }, - { - label: 'Address 2', - tableView: false, - key: 'address2', - type: 'textfield', - input: true, - customConditional: "show = _.get(instance, 'parent.manualMode', false);" - }, - { - label: 'City', - tableView: false, - key: 'city', - type: 'textfield', - input: true, - customConditional: "show = _.get(instance, 'parent.manualMode', false);" - }, - { - label: 'State', - tableView: false, - key: 'state', - type: 'textfield', - input: true, - customConditional: "show = _.get(instance, 'parent.manualMode', false);" - }, - { - label: 'Country', - tableView: false, - key: 'country', - type: 'textfield', - input: true, - customConditional: "show = _.get(instance, 'parent.manualMode', false);" - }, - { - label: 'Zip Code', - tableView: false, - key: 'zip', - type: 'textfield', - input: true, - customConditional: "show = _.get(instance, 'parent.manualMode', false);" - } - ], - defaultValue: { - 'place_id': 256774876, - licence: 'Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright', - 'osm_type': 'relation', - 'osm_id': 1837698, - boundingbox: ['32.5453486', '32.9899027', '-97.0383833', '-96.5168819'], - lat: '32.7620405', - lon: '-96.7790069', - 'display_name': 'Dallas County, Texas, United States', - class: 'boundary', - type: 'administrative', - importance: 0.6662149661993487, - icon: 'https://nominatim.openstreetmap.org/ui/mapicons//poi_boundary_administrative.p.20.png', - address: { - county: 'Dallas County', - state: 'Texas', - country: 'United States', - 'country_code': 'us' - } - } - }, - { - label: 'Submit', - showValidations: false, - tableView: false, - key: 'submit', - type: 'button', - input: true - } - ], - title: 'test11', - display: 'form', - name: 'test11', - path: 'test11' + type: 'form', + components: [ + { + label: 'Address', + tableView: false, + provider: 'nominatim', + key: 'address', + type: 'address', + providerOptions: { + params: { + autocompleteOptions: {}, + }, + url: 'undefined', + }, + input: true, + components: [ + { + label: 'Address 1', + tableView: false, + key: 'address1', + type: 'textfield', + input: true, + customConditional: + "show = _.get(instance, 'parent.manualMode', false);", + }, + { + label: 'Address 2', + tableView: false, + key: 'address2', + type: 'textfield', + input: true, + customConditional: + "show = _.get(instance, 'parent.manualMode', false);", + }, + { + label: 'City', + tableView: false, + key: 'city', + type: 'textfield', + input: true, + customConditional: + "show = _.get(instance, 'parent.manualMode', false);", + }, + { + label: 'State', + tableView: false, + key: 'state', + type: 'textfield', + input: true, + customConditional: + "show = _.get(instance, 'parent.manualMode', false);", + }, + { + label: 'Country', + tableView: false, + key: 'country', + type: 'textfield', + input: true, + customConditional: + "show = _.get(instance, 'parent.manualMode', false);", + }, + { + label: 'Zip Code', + tableView: false, + key: 'zip', + type: 'textfield', + input: true, + customConditional: + "show = _.get(instance, 'parent.manualMode', false);", + }, + ], + defaultValue: { + place_id: 256774876, + licence: + 'Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright', + osm_type: 'relation', + osm_id: 1837698, + boundingbox: [ + '32.5453486', + '32.9899027', + '-97.0383833', + '-96.5168819', + ], + lat: '32.7620405', + lon: '-96.7790069', + display_name: 'Dallas County, Texas, United States', + class: 'boundary', + type: 'administrative', + importance: 0.6662149661993487, + icon: 'https://nominatim.openstreetmap.org/ui/mapicons//poi_boundary_administrative.p.20.png', + address: { + county: 'Dallas County', + state: 'Texas', + country: 'United States', + country_code: 'us', + }, + }, + }, + { + label: 'Submit', + showValidations: false, + tableView: false, + key: 'submit', + type: 'button', + input: true, + }, + ], + title: 'test11', + display: 'form', + name: 'test11', + path: 'test11', }; diff --git a/src/components/address/fixtures/comp3.js b/src/components/address/fixtures/comp3.js index f1df1c86be..30046f872f 100644 --- a/src/components/address/fixtures/comp3.js +++ b/src/components/address/fixtures/comp3.js @@ -1,89 +1,89 @@ export default { - _id: '610cd91f1c0d42a708a148de', - type: 'form', - components: [ - { - label: 'Address', - tableView: false, - modalEdit: true, - provider: 'google', - key: 'address', - type: 'address', - providerOptions: { - params: { - autocompleteOptions: {}, - key: '', - }, - }, - input: true, - components: [ - { - label: 'Address 1', - tableView: false, - key: 'address1', - type: 'textfield', - input: true, - customConditional: - 'show = _.get(instance, "parent.manualMode", false);', - }, - { - label: 'Address 2', - tableView: false, - key: 'address2', - type: 'textfield', - input: true, - customConditional: - 'show = _.get(instance, "parent.manualMode", false);', - }, - { - label: 'City', - tableView: false, - key: 'city', - type: 'textfield', - input: true, - customConditional: - 'show = _.get(instance, "parent.manualMode", false);', - }, - { - label: 'State', - tableView: false, - key: 'state', - type: 'textfield', - input: true, - customConditional: - 'show = _.get(instance, "parent.manualMode", false);', - }, + _id: '610cd91f1c0d42a708a148de', + type: 'form', + components: [ { - label: 'Country', - tableView: false, - key: 'country', - type: 'textfield', - input: true, - customConditional: - 'show = _.get(instance, "parent.manualMode", false);', + label: 'Address', + tableView: false, + modalEdit: true, + provider: 'google', + key: 'address', + type: 'address', + providerOptions: { + params: { + autocompleteOptions: {}, + key: '', + }, + }, + input: true, + components: [ + { + label: 'Address 1', + tableView: false, + key: 'address1', + type: 'textfield', + input: true, + customConditional: + 'show = _.get(instance, "parent.manualMode", false);', + }, + { + label: 'Address 2', + tableView: false, + key: 'address2', + type: 'textfield', + input: true, + customConditional: + 'show = _.get(instance, "parent.manualMode", false);', + }, + { + label: 'City', + tableView: false, + key: 'city', + type: 'textfield', + input: true, + customConditional: + 'show = _.get(instance, "parent.manualMode", false);', + }, + { + label: 'State', + tableView: false, + key: 'state', + type: 'textfield', + input: true, + customConditional: + 'show = _.get(instance, "parent.manualMode", false);', + }, + { + label: 'Country', + tableView: false, + key: 'country', + type: 'textfield', + input: true, + customConditional: + 'show = _.get(instance, "parent.manualMode", false);', + }, + { + label: 'Zip Code', + tableView: false, + key: 'zip', + type: 'textfield', + input: true, + customConditional: + 'show = _.get(instance, "parent.manualMode", false);', + }, + ], }, { - label: 'Zip Code', - tableView: false, - key: 'zip', - type: 'textfield', - input: true, - customConditional: - 'show = _.get(instance, "parent.manualMode", false);', + type: 'button', + label: 'Submit', + key: 'submit', + disableOnInvalid: true, + input: true, + tableView: false, }, - ], - }, - { - type: 'button', - label: 'Submit', - key: 'submit', - disableOnInvalid: true, - input: true, - tableView: false, - }, - ], - title: 'FIO-3280', - display: 'form', - name: 'fio3280', - path: 'fio3280', + ], + title: 'FIO-3280', + display: 'form', + name: 'fio3280', + path: 'fio3280', }; diff --git a/src/components/address/fixtures/comp4.js b/src/components/address/fixtures/comp4.js index 97a7d78171..2b1bfcba0b 100644 --- a/src/components/address/fixtures/comp4.js +++ b/src/components/address/fixtures/comp4.js @@ -1,97 +1,97 @@ export default { - type: 'form', - components: [ - { - label: 'Address', - tableView: false, - provider: 'nominatim', - key: 'address', - type: 'address', - providerOptions: { - params: { - autocompleteOptions: {}, - }, - }, - input: true, - components: [ - { - label: 'Address 1', - tableView: false, - key: 'address1', - type: 'textfield', - input: true, - customConditional: - "show = _.get(instance, 'parent.manualMode', false);", - }, - { - label: 'Address 2', - tableView: false, - key: 'address2', - type: 'textfield', - input: true, - customConditional: - "show = _.get(instance, 'parent.manualMode', false);", - }, - { - label: 'City', - tableView: false, - key: 'city', - type: 'textfield', - input: true, - customConditional: - "show = _.get(instance, 'parent.manualMode', false);", - }, + type: 'form', + components: [ { - label: 'State', - tableView: false, - key: 'state', - type: 'textfield', - input: true, - customConditional: - "show = _.get(instance, 'parent.manualMode', false);", + label: 'Address', + tableView: false, + provider: 'nominatim', + key: 'address', + type: 'address', + providerOptions: { + params: { + autocompleteOptions: {}, + }, + }, + input: true, + components: [ + { + label: 'Address 1', + tableView: false, + key: 'address1', + type: 'textfield', + input: true, + customConditional: + "show = _.get(instance, 'parent.manualMode', false);", + }, + { + label: 'Address 2', + tableView: false, + key: 'address2', + type: 'textfield', + input: true, + customConditional: + "show = _.get(instance, 'parent.manualMode', false);", + }, + { + label: 'City', + tableView: false, + key: 'city', + type: 'textfield', + input: true, + customConditional: + "show = _.get(instance, 'parent.manualMode', false);", + }, + { + label: 'State', + tableView: false, + key: 'state', + type: 'textfield', + input: true, + customConditional: + "show = _.get(instance, 'parent.manualMode', false);", + }, + { + label: 'Country', + tableView: false, + key: 'country', + type: 'textfield', + input: true, + customConditional: + "show = _.get(instance, 'parent.manualMode', false);", + }, + { + label: 'Zip Code', + tableView: false, + key: 'zip', + type: 'textfield', + input: true, + customConditional: + "show = _.get(instance, 'parent.manualMode', false);", + }, + ], }, { - label: 'Country', - tableView: false, - key: 'country', - type: 'textfield', - input: true, - customConditional: - "show = _.get(instance, 'parent.manualMode', false);", + label: 'Text Field', + tableView: true, + key: 'textField', + conditional: { + show: false, + when: 'address', + }, + type: 'textfield', + input: true, }, { - label: 'Zip Code', - tableView: false, - key: 'zip', - type: 'textfield', - input: true, - customConditional: - "show = _.get(instance, 'parent.manualMode', false);", + label: 'Submit', + showValidations: false, + tableView: false, + key: 'submit', + type: 'button', + input: true, }, - ], - }, - { - label: 'Text Field', - tableView: true, - key: 'textField', - conditional: { - show: false, - when: 'address', - }, - type: 'textfield', - input: true, - }, - { - label: 'Submit', - showValidations: false, - tableView: false, - key: 'submit', - type: 'button', - input: true, - }, - ], - title: 'testconditional', - display: 'form', - name: 'testconditional', - path: 'testconditional', + ], + title: 'testconditional', + display: 'form', + name: 'testconditional', + path: 'testconditional', }; diff --git a/src/components/address/fixtures/values.js b/src/components/address/fixtures/values.js index dee28be341..2f6f274b1e 100644 --- a/src/components/address/fixtures/values.js +++ b/src/components/address/fixtures/values.js @@ -1,17 +1,17 @@ export default [ - { - mode: 'autocomplete', - address: {}, - }, - { - mode: 'manual', - address: { - street: '', - city: '', - county: '', - state: '', - zip: '', - country: '', + { + mode: 'autocomplete', + address: {}, + }, + { + mode: 'manual', + address: { + street: '', + city: '', + county: '', + state: '', + zip: '', + country: '', + }, }, - }, ]; diff --git a/src/components/alert/Alert.js b/src/components/alert/Alert.js index e41e4c3725..42b00f5a3b 100644 --- a/src/components/alert/Alert.js +++ b/src/components/alert/Alert.js @@ -2,202 +2,216 @@ import _ from 'lodash'; import { getStringFromComponentPath } from '../../utils/utils'; export default class Alert { - constructor(container, component) { - this.container = container; - this.alert = null; - this.parentComponent = component; - this.refs = {}; - this.loadRefs = this.parentComponent.loadRefs.bind(this); - } - - get refsNames() { - return { - messageRef: 'multiple' - }; - } - - get alertTypes() { - return { - error: 'danger', - success: 'success', - info: 'info', - warning: 'warning' - }; - } - - showErrors(errors = [], triggerEvent = false, options = {}) { - errors = _.isArray(errors) ? errors : [errors]; - - const messagesList = this.createMessagesList('error', errors); - this.showAlert('error', messagesList, options); - - if (triggerEvent) { - this.parentComponent.emit('error', errors); + constructor(container, component) { + this.container = container; + this.alert = null; + this.parentComponent = component; + this.refs = {}; + this.loadRefs = this.parentComponent.loadRefs.bind(this); } - return errors; - } + get refsNames() { + return { + messageRef: 'multiple', + }; + } - showMessage(message, type, options = {}) { - let messageElement = message; - if (messageElement instanceof HTMLElement) { - messageElement.setAttribute('ref', 'messageRef'); + get alertTypes() { + return { + error: 'danger', + success: 'success', + info: 'info', + warning: 'warning', + }; } - else { - messageElement = this.parentComponent.ce('p', { - ref: 'messageRef' - }); + + showErrors(errors = [], triggerEvent = false, options = {}) { + errors = _.isArray(errors) ? errors : [errors]; + + const messagesList = this.createMessagesList('error', errors); + this.showAlert('error', messagesList, options); + + if (triggerEvent) { + this.parentComponent.emit('error', errors); + } + + return errors; } - this.showAlert(type, messageElement, options); - } - createMessagesList(type, ...args) { - switch (type) { - case 'error': - return this.createErrorList(...args); + showMessage(message, type, options = {}) { + let messageElement = message; + if (messageElement instanceof HTMLElement) { + messageElement.setAttribute('ref', 'messageRef'); + } else { + messageElement = this.parentComponent.ce('p', { + ref: 'messageRef', + }); + } + this.showAlert(type, messageElement, options); } - } - - createErrorList(errors) { - const p = this.parentComponent.ce('p'); - this.parentComponent.setContent(p, this.parentComponent.t('error')); - const ul = this.parentComponent.ce('ul'); - const messagesList = document.createDocumentFragment(); - - errors.forEach(err => this.appendErrorToList(err, ul)); - - p.appendChild(ul); - messagesList.appendChild(p); - return messagesList; - } - - showAlert(type, messagesList, options = {}) { - const { customClasses, customEvents } = options; - this.setAlert(type, messagesList, { customClasses }); - if (!this.alert) { - return; + + createMessagesList(type, ...args) { + switch (type) { + case 'error': + return this.createErrorList(...args); + } } - this.attach({ customEvents }); - this.parentComponent.prependTo(this.alert, this.container); - } - - setAlert(type, messagesList, options = {}) { - const alertType = this.alertTypes[type]; - if (this.alert) { - this.clear(); + + createErrorList(errors) { + const p = this.parentComponent.ce('p'); + this.parentComponent.setContent(p, this.parentComponent.t('error')); + const ul = this.parentComponent.ce('ul'); + const messagesList = document.createDocumentFragment(); + + errors.forEach((err) => this.appendErrorToList(err, ul)); + + p.appendChild(ul); + messagesList.appendChild(p); + return messagesList; } - if (messagesList) { - const { - id = `${type}-list-${this.parentComponent.id}`, - customClasses = `alert alert-${alertType}` - } = options; - this.alert = this.parentComponent.ce('div', { id, class: customClasses }); - if (messagesList instanceof HTMLElement) { - this.parentComponent.appendTo(messagesList, this.alert); - } - else { - this.parentComponent.setContent(this.alert, messagesList); - } + + showAlert(type, messagesList, options = {}) { + const { customClasses, customEvents } = options; + this.setAlert(type, messagesList, { customClasses }); + if (!this.alert) { + return; + } + this.attach({ customEvents }); + this.parentComponent.prependTo(this.alert, this.container); } - } - - attach(options) { - let { customEvents = {} } = options; - this.eventListenersKeys = []; - this.loadRefs(this.alert, this.refsNames); - const clickListeners = customEvents.click?.listeners || []; - const keyPressListeners = customEvents.keypress?.listeners || []; - customEvents = { - ...customEvents, - click: [ - ...clickListeners, - (e) => { - const key = e.currentTarget.dataset.componentKey; - this.focusOnComponent(key); + + setAlert(type, messagesList, options = {}) { + const alertType = this.alertTypes[type]; + if (this.alert) { + this.clear(); } - ], - keypress: [ - ...keyPressListeners, - (e) => { - const key = e.currentTarget.dataset.componentKey; - this.focusOnComponent(key); + if (messagesList) { + const { + id = `${type}-list-${this.parentComponent.id}`, + customClasses = `alert alert-${alertType}`, + } = options; + this.alert = this.parentComponent.ce('div', { + id, + class: customClasses, + }); + if (messagesList instanceof HTMLElement) { + this.parentComponent.appendTo(messagesList, this.alert); + } else { + this.parentComponent.setContent(this.alert, messagesList); + } } - ] - }; - - if (this.refs.messageRef?.length) { - this.refs.messageRef.forEach(el => { - Object.entries(customEvents).forEach(([event, listeners]) => { - listeners.forEach((listener) => this.parentComponent.addEventListener(el, event, listener)); - this.eventListenersKeys.push(event); - }); - }); } - } - - clear() { - try { - if (this.refs.messageRef?.length) { - this.refs.messageRef.forEach(el => { - this.eventListenersKeys.forEach(event => this.parentComponent.removeEventListener(el, event)); - }); - } - this.refs = {}; - this.parentComponent.removeChildFrom(this.alert, this.container); - this.alert = null; - } - catch (err) { - // ignore + + attach(options) { + let { customEvents = {} } = options; + this.eventListenersKeys = []; + this.loadRefs(this.alert, this.refsNames); + const clickListeners = customEvents.click?.listeners || []; + const keyPressListeners = customEvents.keypress?.listeners || []; + customEvents = { + ...customEvents, + click: [ + ...clickListeners, + (e) => { + const key = e.currentTarget.dataset.componentKey; + this.focusOnComponent(key); + }, + ], + keypress: [ + ...keyPressListeners, + (e) => { + const key = e.currentTarget.dataset.componentKey; + this.focusOnComponent(key); + }, + ], + }; + + if (this.refs.messageRef?.length) { + this.refs.messageRef.forEach((el) => { + Object.entries(customEvents).forEach(([event, listeners]) => { + listeners.forEach((listener) => + this.parentComponent.addEventListener( + el, + event, + listener, + ), + ); + this.eventListenersKeys.push(event); + }); + }); + } } - } - - focusOnComponent(keyOrPath) { - if (keyOrPath) { - const path = this.parentComponent._parentPath ? keyOrPath.replace(this.parentComponent._parentPath, '') : keyOrPath; - const component = this.parentComponent.root?.getComponent(path, null, keyOrPath); - if (component && _.isFunction(component.focus)) { - component.focus(); - } + + clear() { + try { + if (this.refs.messageRef?.length) { + this.refs.messageRef.forEach((el) => { + this.eventListenersKeys.forEach((event) => + this.parentComponent.removeEventListener(el, event), + ); + }); + } + this.refs = {}; + this.parentComponent.removeChildFrom(this.alert, this.container); + this.alert = null; + } catch (err) { + // ignore + } } - } - createMessage(type, element, message, index, err) { - switch (type) { - case 'error': - return this.createErrorMessage(element, message, index, err); + focusOnComponent(keyOrPath) { + if (keyOrPath) { + const path = this.parentComponent._parentPath + ? keyOrPath.replace(this.parentComponent._parentPath, '') + : keyOrPath; + const component = this.parentComponent.root?.getComponent( + path, + null, + keyOrPath, + ); + if (component && _.isFunction(component.focus)) { + component.focus(); + } + } } - } - - createErrorMessage(element, message, index, err) { - const params = { - style: 'cursor: pointer', - ref: 'messageRef', - tabIndex: 0, - 'aria-label': `${message}. Click to navigate to the field with following error.` - }; - - const li = this.parentComponent.ce('li', params); - this.parentComponent.setContent(li, message); - - const messageFromIndex = !_.isUndefined(index) && err?.messages?.[index]; - const keyOrPath = messageFromIndex?.path || err?.component?.key; - if (keyOrPath) { - const formattedKeyOrPath = getStringFromComponentPath(keyOrPath); - li.dataset.componentKey = formattedKeyOrPath; + + createMessage(type, element, message, index, err) { + switch (type) { + case 'error': + return this.createErrorMessage(element, message, index, err); + } } - this.parentComponent.appendTo(li, element); - } + createErrorMessage(element, message, index, err) { + const params = { + style: 'cursor: pointer', + ref: 'messageRef', + tabIndex: 0, + 'aria-label': `${message}. Click to navigate to the field with following error.`, + }; + + const li = this.parentComponent.ce('li', params); + this.parentComponent.setContent(li, message); + + const messageFromIndex = + !_.isUndefined(index) && err?.messages?.[index]; + const keyOrPath = messageFromIndex?.path || err?.component?.key; + if (keyOrPath) { + const formattedKeyOrPath = getStringFromComponentPath(keyOrPath); + li.dataset.componentKey = formattedKeyOrPath; + } - appendErrorToList(err, ul) { - if (err?.messages?.length) { - err.messages.forEach(({ message }, index) => { - this.createMessage('error', ul, message, index, err); - }); + this.parentComponent.appendTo(li, element); } - else if (err) { - const message = _.isObject(err) ? err.message || '' : err; - this.createMessage('error', ul, message); + + appendErrorToList(err, ul) { + if (err?.messages?.length) { + err.messages.forEach(({ message }, index) => { + this.createMessage('error', ul, message, index, err); + }); + } else if (err) { + const message = _.isObject(err) ? err.message || '' : err; + this.createMessage('error', ul, message); + } } - } } diff --git a/src/components/button/Button.form.js b/src/components/button/Button.form.js index 1a7d2099ff..129fcdfed8 100644 --- a/src/components/button/Button.form.js +++ b/src/components/button/Button.form.js @@ -1,19 +1,22 @@ import Components from '../Components'; import ButtonEditDisplay from './editForm/Button.edit.display'; -export default function(...extend) { - return Components.baseEditForm([ - { - key: 'display', - components: ButtonEditDisplay - }, - { - key: 'data', - ignore: true, - }, - { - key: 'validation', - ignore: true, - }, - ], ...extend); +export default function (...extend) { + return Components.baseEditForm( + [ + { + key: 'display', + components: ButtonEditDisplay, + }, + { + key: 'data', + ignore: true, + }, + { + key: 'validation', + ignore: true, + }, + ], + ...extend, + ); } diff --git a/src/components/button/Button.js b/src/components/button/Button.js index 8bfa069245..a67bf79075 100644 --- a/src/components/button/Button.js +++ b/src/components/button/Button.js @@ -1,526 +1,715 @@ import _ from 'lodash'; import Field from '../_classes/field/Field'; import Input from '../_classes/input/Input'; -import { componentValueTypes, eachComponent, getArrayFromComponentPath, getComponentSavedTypes } from '../../utils/utils'; +import { + componentValueTypes, + eachComponent, + getArrayFromComponentPath, + getComponentSavedTypes, +} from '../../utils/utils'; export default class ButtonComponent extends Field { - static schema(...extend) { - return Input.schema({ - type: 'button', - label: 'Submit', - key: 'submit', - size: 'md', - leftIcon: '', - rightIcon: '', - block: false, - action: 'submit', - persistent: false, - disableOnInvalid: false, - theme: 'primary', - dataGridLabel: true - }, ...extend); - } - - static get builderInfo() { - return { - title: 'Button', - group: 'basic', - icon: 'stop', - documentation: '/userguide/form-building/form-components#button', - weight: 110, - schema: ButtonComponent.schema() - }; - } - - static savedValueTypes(schema) { - return getComponentSavedTypes(schema) || [componentValueTypes.boolean]; - } - - constructor(component, options, data) { - super(component, options, data); - this.filesUploading = []; - } - - get defaultSchema() { - return ButtonComponent.schema(); - } - - get inputInfo() { - const info = super.elementInfo(); - info.type = 'button'; - info.attr.type = (['submit', 'saveState'].includes(this.component.action)) ? 'submit' : 'button'; - this.component.theme = this.component.theme || 'default'; - info.attr.class = `btn btn-${this.component.theme}`; - if (this.component.size) { - info.attr.class += ` btn-${this.component.size}`; + static schema(...extend) { + return Input.schema( + { + type: 'button', + label: 'Submit', + key: 'submit', + size: 'md', + leftIcon: '', + rightIcon: '', + block: false, + action: 'submit', + persistent: false, + disableOnInvalid: false, + theme: 'primary', + dataGridLabel: true, + }, + ...extend, + ); } - if (this.component.block) { - info.attr.class += ' btn-block'; - } - if (this.component.customClass) { - info.attr.class += ` ${this.component.customClass}`; + + static get builderInfo() { + return { + title: 'Button', + group: 'basic', + icon: 'stop', + documentation: '/userguide/form-building/form-components#button', + weight: 110, + schema: ButtonComponent.schema(), + }; } - info.content = this.t(this.component.label, { _userInput: true }); - return info; - } - - get labelInfo() { - return { - hidden: true - }; - } - - set loading(loading) { - this.setLoading(this.refs.button, loading); - } - - get skipInEmail() { - return true; - } - - // No label needed for buttons. - createLabel() {} - - createInput(container) { - this.refs.button = super.createInput(container); - return this.refs.button; - } - - get emptyValue() { - return false; - } - - getValue() { - return this.dataValue; - } - - get clicked() { - return this.dataValue; - } - - get defaultValue() { - return false; - } - - get className() { - let className = super.className; - className += ` ${this.transform('class', 'form-group')}`; - return className; - } - - get oauthConfig() { - if (_.has(this, 'root.form.config.oauth') && this.component.oauthProvider) { - return this.root.form.config.oauth[this.component.oauthProvider]; + + static savedValueTypes(schema) { + return getComponentSavedTypes(schema) || [componentValueTypes.boolean]; } - // Legacy oauth location. - if (this.component.oauth) { - return this.component.oauth; + + constructor(component, options, data) { + super(component, options, data); + this.filesUploading = []; } - return false; - } - render() { - if (this.viewOnly || this.options.hideButtons) { - this._visible = false; + get defaultSchema() { + return ButtonComponent.schema(); } - return super.render(this.renderTemplate('button', { - component: this.component, - input: this.inputInfo, - })); - } - - attachButton() { - this.addShortcut(this.refs.button); - let onChange = null; - let onError = null; - if (this.component.action === 'submit') { - this.on('submitButton', () => { - this.disabled = true; - }, true); - this.on('cancelSubmit', () => { - this.disabled = false; - }, true); - this.on('submitDone', (message) => { - const resultMessage = _.isString(message) ? message : this.t('complete'); - this.loading = false; - this.disabled = false; - this.addClass(this.refs.button, 'btn-success submit-success'); - this.removeClass(this.refs.button, 'btn-danger submit-fail'); - this.addClass(this.refs.buttonMessageContainer, 'has-success'); - this.removeClass(this.refs.buttonMessageContainer, 'has-error'); - this.setContent(this.refs.buttonMessage, resultMessage); - }, true); - this.on('submitError', (message) => { - const resultMessage = _.isString(message) ? this.t(message) : this.t(this.errorMessage('submitError')); - this.loading = false; - this.disabled = false; - this.hasError = true; - this.removeClass(this.refs.button, 'btn-success submit-success'); - this.addClass(this.refs.button, 'btn-danger submit-fail'); - this.removeClass(this.refs.buttonMessageContainer, 'has-success'); - this.addClass(this.refs.buttonMessageContainer, 'has-error'); - this.setContent(this.refs.buttonMessage, resultMessage); - }, true); - - this.on('fileUploadingStart', (filePromise) => { - this.filesUploading.push(filePromise); - this.disabled = true; - this.setDisabled(this.refs.button, this.disabled); - }, true); - this.on('fileUploadingEnd', (filePromise) => { - const index = this.filesUploading.indexOf(filePromise); - if (index !== -1) { - this.filesUploading.splice(index, 1); + get inputInfo() { + const info = super.elementInfo(); + info.type = 'button'; + info.attr.type = ['submit', 'saveState'].includes(this.component.action) + ? 'submit' + : 'button'; + this.component.theme = this.component.theme || 'default'; + info.attr.class = `btn btn-${this.component.theme}`; + if (this.component.size) { + info.attr.class += ` btn-${this.component.size}`; } - this.disabled = this.shouldDisabled ? true : false; - this.setDisabled(this.refs.button, this.disabled); - }, true); - - onChange = (value, isValid) => { - this.removeClass(this.refs.button, 'btn-success submit-success'); - if (isValid) { - this.removeClass(this.refs.button, 'btn-danger submit-fail'); - if (this.hasError) { - this.hasError = false; - this.setContent(this.refs.buttonMessage, ''); - this.removeClass(this.refs.buttonMessageContainer, 'has-success'); - this.removeClass(this.refs.buttonMessageContainer, 'has-error'); - } + if (this.component.block) { + info.attr.class += ' btn-block'; + } + if (this.component.customClass) { + info.attr.class += ` ${this.component.customClass}`; } - }; - onError = () => { - this.hasError = true; - this.removeClass(this.refs.button, 'btn-success submit-success'); - this.addClass(this.refs.button, 'btn-danger submit-fail'); - this.removeClass(this.refs.buttonMessageContainer, 'has-success'); - this.addClass(this.refs.buttonMessageContainer, 'has-error'); - this.setContent(this.refs.buttonMessage, this.t(this.errorMessage('submitError'))); - }; + info.content = this.t(this.component.label, { _userInput: true }); + return info; } - if (this.component.action === 'url') { - this.on('requestButton', () => { - this.disabled = true; - }, true); - this.on('requestDone', () => { - this.loading = false; - this.disabled = false; - }, true); + get labelInfo() { + return { + hidden: true, + }; } - this.on('change', (value, flags) => { - let isValid = value.isValid; - const isSilent = flags && flags.silent; - //check root validity only if disableOnInvalid is set and when it is not possible to make submission because of validation errors - if (flags && flags.noValidate && (this.component.disableOnInvalid || this.hasError)) { - isValid = flags.rootValidity || (this.root ? this.root.checkValidity(this.root.data, null, null, true) : true); - flags.rootValidity = isValid; - } - this.isDisabledOnInvalid = this.component.disableOnInvalid && (isSilent || !isValid); - this.disabled = this.shouldDisabled; - this.setDisabled(this.refs.button, this.disabled); - - if (onChange) { - onChange(value, isValid); - } - }, true); - - this.on('error', () => { - this.loading = false; - this.disabled = false; - if (onError) { - onError(); - } - }, true); - - if (this.component.saveOnEnter) { - this.root.addEventListener(this.root.element, 'keyup', (event) => { - if (event.keyCode === 13) { - this.onClick.call(this, event); - } - }); + set loading(loading) { + this.setLoading(this.refs.button, loading); } - this.addEventListener(this.refs.button, 'click', this.onClick.bind(this)); - this.addEventListener(this.refs.buttonMessageContainer, 'click', () => { - if (this.refs.buttonMessageContainer.classList.contains('has-error')) { - if (this.root && this.root.alert) { - this.scrollIntoView(this.root.alert); - } - } - }); - - this.disabled = this.shouldDisabled; - this.setDisabled(this.refs.button, this.disabled); - - function getUrlParameter(name) { - name = name.replace(/[[]/, '\\[').replace(/[\]]/, '\\]'); - const regex = new RegExp(`[\\?&]${name}=([^&#]*)`); - const results = regex.exec(location.search); - if (!results) { - return results; - } - return decodeURIComponent(results[1].replace(/\+/g, ' ')); + + get skipInEmail() { + return true; } - // If this is an OpenID Provider initiated login, perform the click event immediately - if ((this.component.action === 'oauth') && this.oauthConfig && !this.oauthConfig.error) { - const iss = getUrlParameter('iss'); - if (iss && (this.oauthConfig.authURI.indexOf(iss) === 0)) { - this.openOauth(this.oauthConfig); - } + // No label needed for buttons. + createLabel() {} + + createInput(container) { + this.refs.button = super.createInput(container); + return this.refs.button; } - } - - get shouldDisabled() { - return super.shouldDisabled || !!this.filesUploading?.length || this.isDisabledOnInvalid; - } - - attach(element) { - this.loadRefs(element, { - button: 'single', - buttonMessageContainer: 'single', - buttonMessage: 'single' - }); - - const superAttach = super.attach(element); - this.attachButton(); - return superAttach; - } - /* eslint-enable max-statements */ - - detach(element) { - if (element && this.refs.button) { - this.removeShortcut(this.refs.button); + + get emptyValue() { + return false; } - super.detach(); - } - - onClick(event) { - this.triggerReCaptcha(); - // Don't click if disabled or in builder mode. - if (this.disabled || this.options.attachMode === 'builder') { - return; + + getValue() { + return this.dataValue; } - this.dataValue = true; - if (this.component.action !== 'submit' && this.component.showValidations) { - this.emit('checkValidity', this.data); + + get clicked() { + return this.dataValue; } - switch (this.component.action) { - case 'saveState': - case 'submit': - event.preventDefault(); - event.stopPropagation(); - this.loading = true; - this.emit('submitButton', { - state: this.component.state || 'submitted', - component: this.component, - instance: this - }); - break; - case 'event': - this.emit(this.interpolate(this.component.event), this.data); - this.events.emit(this.interpolate(this.component.event), this.data); - this.emit('customEvent', { - type: this.interpolate(this.component.event), - component: this.component, - data: this.data, - event: event - }); - break; - case 'custom': { - // Get the FormioForm at the root of this component's tree - const form = this.getRoot(); - - const flattened = {}; - const components = {}; - - eachComponent(form.components, (componentWrapper, path) => { - const component = componentWrapper.component || componentWrapper; - flattened[path] = component; - components[component.key] = component; - }, true); - - this.evaluate(this.component.custom, { - form, - flattened, - components - }); - this.triggerChange(); - break; - } - case 'url': - this.loading = true; - this.emit('requestButton', { - component: this.component, - instance: this - }); - this.emit('requestUrl', { - url: this.interpolate(this.component.url), - headers: this.component.headers - }); - break; - case 'reset': - this.emit('resetForm'); - break; - case 'delete': - this.emit('deleteSubmission'); - break; - case 'oauth': - if (this.root === this) { - console.warn('You must add the OAuth button to a form for it to function properly'); - return; + get defaultValue() { + return false; + } + + get className() { + let className = super.className; + className += ` ${this.transform('class', 'form-group')}`; + return className; + } + + get oauthConfig() { + if ( + _.has(this, 'root.form.config.oauth') && + this.component.oauthProvider + ) { + return this.root.form.config.oauth[this.component.oauthProvider]; + } + // Legacy oauth location. + if (this.component.oauth) { + return this.component.oauth; } + return false; + } - // Display Alert if OAuth config is missing - if (!this.oauthConfig) { - this.root.setAlert('danger', 'OAuth not configured. You must configure oauth for your project before it will work.'); - break; + render() { + if (this.viewOnly || this.options.hideButtons) { + this._visible = false; } + return super.render( + this.renderTemplate('button', { + component: this.component, + input: this.inputInfo, + }), + ); + } - // Display Alert if oAuth has an error is missing - if (this.oauthConfig.error) { - this.root.setAlert('danger', `The Following Error Has Occured ${this.oauthConfig.error}`); - break; + attachButton() { + this.addShortcut(this.refs.button); + let onChange = null; + let onError = null; + if (this.component.action === 'submit') { + this.on( + 'submitButton', + () => { + this.disabled = true; + }, + true, + ); + this.on( + 'cancelSubmit', + () => { + this.disabled = false; + }, + true, + ); + this.on( + 'submitDone', + (message) => { + const resultMessage = _.isString(message) + ? message + : this.t('complete'); + this.loading = false; + this.disabled = false; + this.addClass( + this.refs.button, + 'btn-success submit-success', + ); + this.removeClass( + this.refs.button, + 'btn-danger submit-fail', + ); + this.addClass( + this.refs.buttonMessageContainer, + 'has-success', + ); + this.removeClass( + this.refs.buttonMessageContainer, + 'has-error', + ); + this.setContent(this.refs.buttonMessage, resultMessage); + }, + true, + ); + this.on( + 'submitError', + (message) => { + const resultMessage = _.isString(message) + ? this.t(message) + : this.t(this.errorMessage('submitError')); + this.loading = false; + this.disabled = false; + this.hasError = true; + this.removeClass( + this.refs.button, + 'btn-success submit-success', + ); + this.addClass(this.refs.button, 'btn-danger submit-fail'); + this.removeClass( + this.refs.buttonMessageContainer, + 'has-success', + ); + this.addClass( + this.refs.buttonMessageContainer, + 'has-error', + ); + this.setContent(this.refs.buttonMessage, resultMessage); + }, + true, + ); + + this.on( + 'fileUploadingStart', + (filePromise) => { + this.filesUploading.push(filePromise); + this.disabled = true; + this.setDisabled(this.refs.button, this.disabled); + }, + true, + ); + + this.on( + 'fileUploadingEnd', + (filePromise) => { + const index = this.filesUploading.indexOf(filePromise); + if (index !== -1) { + this.filesUploading.splice(index, 1); + } + this.disabled = this.shouldDisabled ? true : false; + this.setDisabled(this.refs.button, this.disabled); + }, + true, + ); + + onChange = (value, isValid) => { + this.removeClass( + this.refs.button, + 'btn-success submit-success', + ); + if (isValid) { + this.removeClass( + this.refs.button, + 'btn-danger submit-fail', + ); + if (this.hasError) { + this.hasError = false; + this.setContent(this.refs.buttonMessage, ''); + this.removeClass( + this.refs.buttonMessageContainer, + 'has-success', + ); + this.removeClass( + this.refs.buttonMessageContainer, + 'has-error', + ); + } + } + }; + onError = () => { + this.hasError = true; + this.removeClass( + this.refs.button, + 'btn-success submit-success', + ); + this.addClass(this.refs.button, 'btn-danger submit-fail'); + this.removeClass( + this.refs.buttonMessageContainer, + 'has-success', + ); + this.addClass(this.refs.buttonMessageContainer, 'has-error'); + this.setContent( + this.refs.buttonMessage, + this.t(this.errorMessage('submitError')), + ); + }; } - this.openOauth(this.oauthConfig); + if (this.component.action === 'url') { + this.on( + 'requestButton', + () => { + this.disabled = true; + }, + true, + ); + this.on( + 'requestDone', + () => { + this.loading = false; + this.disabled = false; + }, + true, + ); + } - break; - } - } + this.on( + 'change', + (value, flags) => { + let isValid = value.isValid; + const isSilent = flags && flags.silent; + //check root validity only if disableOnInvalid is set and when it is not possible to make submission because of validation errors + if ( + flags && + flags.noValidate && + (this.component.disableOnInvalid || this.hasError) + ) { + isValid = + flags.rootValidity || + (this.root + ? this.root.checkValidity( + this.root.data, + null, + null, + true, + ) + : true); + flags.rootValidity = isValid; + } + this.isDisabledOnInvalid = + this.component.disableOnInvalid && (isSilent || !isValid); + this.disabled = this.shouldDisabled; + this.setDisabled(this.refs.button, this.disabled); + + if (onChange) { + onChange(value, isValid); + } + }, + true, + ); + + this.on( + 'error', + () => { + this.loading = false; + this.disabled = false; + if (onError) { + onError(); + } + }, + true, + ); + + if (this.component.saveOnEnter) { + this.root.addEventListener(this.root.element, 'keyup', (event) => { + if (event.keyCode === 13) { + this.onClick.call(this, event); + } + }); + } + this.addEventListener( + this.refs.button, + 'click', + this.onClick.bind(this), + ); + this.addEventListener(this.refs.buttonMessageContainer, 'click', () => { + if ( + this.refs.buttonMessageContainer.classList.contains('has-error') + ) { + if (this.root && this.root.alert) { + this.scrollIntoView(this.root.alert); + } + } + }); - openOauth(settings) { - if (!this.root.formio) { - console.warn('You must attach a Form API url to your form in order to use OAuth buttons.'); - return; - } + this.disabled = this.shouldDisabled; + this.setDisabled(this.refs.button, this.disabled); - /*eslint-disable camelcase */ - let params = { - response_type: 'code', - client_id: settings.clientId, - redirect_uri: (settings.redirectURI && this.interpolate(settings.redirectURI)) || window.location.origin || `${window.location.protocol}//${window.location.host}`, - scope: settings.scope - }; - if (settings.state) { - params.state = settings.state; + function getUrlParameter(name) { + name = name.replace(/[[]/, '\\[').replace(/[\]]/, '\\]'); + const regex = new RegExp(`[\\?&]${name}=([^&#]*)`); + const results = regex.exec(location.search); + if (!results) { + return results; + } + return decodeURIComponent(results[1].replace(/\+/g, ' ')); + } + + // If this is an OpenID Provider initiated login, perform the click event immediately + if ( + this.component.action === 'oauth' && + this.oauthConfig && + !this.oauthConfig.error + ) { + const iss = getUrlParameter('iss'); + if (iss && this.oauthConfig.authURI.indexOf(iss) === 0) { + this.openOauth(this.oauthConfig); + } + } } - else if (settings.code_challenge) { - params.code_challenge = settings.code_challenge; - params.code_challenge_method = 'S256'; + + get shouldDisabled() { + return ( + super.shouldDisabled || + !!this.filesUploading?.length || + this.isDisabledOnInvalid + ); } - /*eslint-enable camelcase */ + attach(element) { + this.loadRefs(element, { + button: 'single', + buttonMessageContainer: 'single', + buttonMessage: 'single', + }); - // Needs for the correct redirection URI for the OpenID - const originalRedirectUri = params.redirect_uri; + const superAttach = super.attach(element); + this.attachButton(); + return superAttach; + } + /* eslint-enable max-statements */ - // Make display optional. - if (settings.display) { - params.display = settings.display; + detach(element) { + if (element && this.refs.button) { + this.removeShortcut(this.refs.button); + } + super.detach(); } - params = Object.keys(params).map(key => { - return `${key}=${encodeURIComponent(params[key])}`; - }).join('&'); - - const separator = settings.authURI.indexOf('?') !== -1 ? '&' : '?'; - const url = `${settings.authURI}${separator}${params}`; - const popup = window.open(url, settings.provider, 'width=1020,height=618'); - - const interval = setInterval(() => { - try { - const popupHost = popup.location.host; - const currentHost = window.location.host; - if (popup && !popup.closed && popupHost === currentHost) { - popup.close(); - const params = popup.location.search.substr(1).split('&').reduce((params, param) => { - const split = param.split('='); - params[split[0]] = split[1]; - return params; - }, {}); - if (params.error) { - alert(params.error_description || params.error); - this.root.setAlert('danger', params.error_description || params.error); + onClick(event) { + this.triggerReCaptcha(); + // Don't click if disabled or in builder mode. + if (this.disabled || this.options.attachMode === 'builder') { return; - } - // TODO: check for error response here - if (settings.state !== params.state) { - this.root.setAlert('danger', 'OAuth state does not match. Please try logging in again.'); - return; - } - // Depending on where the settings came from, submit to either the submission endpoint (old) or oauth endpoint (new). - let requestPromise = Promise.resolve(); - - if (_.has(this, 'root.form.config.oauth') && this.root.form.config.oauth[this.component.oauthProvider]) { - params.provider = settings.provider; - params.redirectURI = originalRedirectUri; - - // Needs for the exclude oAuth Actions that not related to this button - params.triggeredBy = this.oauthComponentPath; - requestPromise = this.root.formio.makeRequest('oauth', `${this.root.formio.projectUrl}/oauth2`, 'POST', params); - } - else { - const submission = { data: {}, oauth: {} }; - submission.oauth[settings.provider] = params; - submission.oauth[settings.provider].redirectURI = originalRedirectUri; - if (settings.logoutURI) { - this.root.formio.oauthLogoutURI(settings.logoutURI); + } + this.dataValue = true; + if ( + this.component.action !== 'submit' && + this.component.showValidations + ) { + this.emit('checkValidity', this.data); + } + switch (this.component.action) { + case 'saveState': + case 'submit': + event.preventDefault(); + event.stopPropagation(); + this.loading = true; + this.emit('submitButton', { + state: this.component.state || 'submitted', + component: this.component, + instance: this, + }); + break; + case 'event': + this.emit(this.interpolate(this.component.event), this.data); + this.events.emit( + this.interpolate(this.component.event), + this.data, + ); + this.emit('customEvent', { + type: this.interpolate(this.component.event), + component: this.component, + data: this.data, + event: event, + }); + break; + case 'custom': { + // Get the FormioForm at the root of this component's tree + const form = this.getRoot(); + + const flattened = {}; + const components = {}; + + eachComponent( + form.components, + (componentWrapper, path) => { + const component = + componentWrapper.component || componentWrapper; + flattened[path] = component; + components[component.key] = component; + }, + true, + ); + + this.evaluate(this.component.custom, { + form, + flattened, + components, + }); + + this.triggerChange(); + break; } + case 'url': + this.loading = true; + this.emit('requestButton', { + component: this.component, + instance: this, + }); + this.emit('requestUrl', { + url: this.interpolate(this.component.url), + headers: this.component.headers, + }); + break; + case 'reset': + this.emit('resetForm'); + break; + case 'delete': + this.emit('deleteSubmission'); + break; + case 'oauth': + if (this.root === this) { + console.warn( + 'You must add the OAuth button to a form for it to function properly', + ); + return; + } + + // Display Alert if OAuth config is missing + if (!this.oauthConfig) { + this.root.setAlert( + 'danger', + 'OAuth not configured. You must configure oauth for your project before it will work.', + ); + break; + } + + // Display Alert if oAuth has an error is missing + if (this.oauthConfig.error) { + this.root.setAlert( + 'danger', + `The Following Error Has Occured ${this.oauthConfig.error}`, + ); + break; + } + + this.openOauth(this.oauthConfig); + + break; + } + } - // Needs for the exclude oAuth Actions that not related to this button - submission.oauth[settings.provider].triggeredBy = this.oauthComponentPath; - requestPromise = this.root.formio.saveSubmission(submission); - } - requestPromise.then((result) => { - this.root.onSubmit(result, true); - }) - .catch((err) => { - this.root.onSubmissionError(err); - }); + openOauth(settings) { + if (!this.root.formio) { + console.warn( + 'You must attach a Form API url to your form in order to use OAuth buttons.', + ); + return; } - } - catch (error) { - if (error.name !== 'SecurityError' && (error.name !== 'Error' || error.message !== 'Permission denied')) { - this.root.setAlert('danger', error.message || error); + + /*eslint-disable camelcase */ + let params = { + response_type: 'code', + client_id: settings.clientId, + redirect_uri: + (settings.redirectURI && + this.interpolate(settings.redirectURI)) || + window.location.origin || + `${window.location.protocol}//${window.location.host}`, + scope: settings.scope, + }; + if (settings.state) { + params.state = settings.state; + } else if (settings.code_challenge) { + params.code_challenge = settings.code_challenge; + params.code_challenge_method = 'S256'; + } + + /*eslint-enable camelcase */ + + // Needs for the correct redirection URI for the OpenID + const originalRedirectUri = params.redirect_uri; + + // Make display optional. + if (settings.display) { + params.display = settings.display; } - } - if (!popup || popup.closed || popup.closed === undefined) { - clearInterval(interval); - } - }, 100); - } - - get oauthComponentPath() { - const pathArray = getArrayFromComponentPath(this.path); - return _.chain(pathArray).filter(pathPart => !_.isNumber(pathPart)).join('.').value(); - } - - focus() { - if (this.refs.button) { - this.refs.button.focus(); + + params = Object.keys(params) + .map((key) => { + return `${key}=${encodeURIComponent(params[key])}`; + }) + .join('&'); + + const separator = settings.authURI.indexOf('?') !== -1 ? '&' : '?'; + const url = `${settings.authURI}${separator}${params}`; + const popup = window.open( + url, + settings.provider, + 'width=1020,height=618', + ); + + const interval = setInterval(() => { + try { + const popupHost = popup.location.host; + const currentHost = window.location.host; + if (popup && !popup.closed && popupHost === currentHost) { + popup.close(); + const params = popup.location.search + .substr(1) + .split('&') + .reduce((params, param) => { + const split = param.split('='); + params[split[0]] = split[1]; + return params; + }, {}); + if (params.error) { + alert(params.error_description || params.error); + this.root.setAlert( + 'danger', + params.error_description || params.error, + ); + return; + } + // TODO: check for error response here + if (settings.state !== params.state) { + this.root.setAlert( + 'danger', + 'OAuth state does not match. Please try logging in again.', + ); + return; + } + // Depending on where the settings came from, submit to either the submission endpoint (old) or oauth endpoint (new). + let requestPromise = Promise.resolve(); + + if ( + _.has(this, 'root.form.config.oauth') && + this.root.form.config.oauth[ + this.component.oauthProvider + ] + ) { + params.provider = settings.provider; + params.redirectURI = originalRedirectUri; + + // Needs for the exclude oAuth Actions that not related to this button + params.triggeredBy = this.oauthComponentPath; + requestPromise = this.root.formio.makeRequest( + 'oauth', + `${this.root.formio.projectUrl}/oauth2`, + 'POST', + params, + ); + } else { + const submission = { data: {}, oauth: {} }; + submission.oauth[settings.provider] = params; + submission.oauth[settings.provider].redirectURI = + originalRedirectUri; + if (settings.logoutURI) { + this.root.formio.oauthLogoutURI(settings.logoutURI); + } + + // Needs for the exclude oAuth Actions that not related to this button + submission.oauth[settings.provider].triggeredBy = + this.oauthComponentPath; + requestPromise = + this.root.formio.saveSubmission(submission); + } + requestPromise + .then((result) => { + this.root.onSubmit(result, true); + }) + .catch((err) => { + this.root.onSubmissionError(err); + }); + } + } catch (error) { + if ( + error.name !== 'SecurityError' && + (error.name !== 'Error' || + error.message !== 'Permission denied') + ) { + this.root.setAlert('danger', error.message || error); + } + } + if (!popup || popup.closed || popup.closed === undefined) { + clearInterval(interval); + } + }, 100); } - } - triggerReCaptcha() { - if (!this.root) { - return; + get oauthComponentPath() { + const pathArray = getArrayFromComponentPath(this.path); + return _.chain(pathArray) + .filter((pathPart) => !_.isNumber(pathPart)) + .join('.') + .value(); } - let recaptchaComponent; + focus() { + if (this.refs.button) { + this.refs.button.focus(); + } + } - this.root.everyComponent((component)=> { - if ( component.component.type === 'recaptcha' && - component.component.eventType === 'buttonClick' && - component.component.buttonKey === this.component.key) { - recaptchaComponent = component; + triggerReCaptcha() { + if (!this.root) { + return; } - }); - if (recaptchaComponent) { - recaptchaComponent.verify(`${this.component.key}Click`); + let recaptchaComponent; + + this.root.everyComponent((component) => { + if ( + component.component.type === 'recaptcha' && + component.component.eventType === 'buttonClick' && + component.component.buttonKey === this.component.key + ) { + recaptchaComponent = component; + } + }); + + if (recaptchaComponent) { + recaptchaComponent.verify(`${this.component.key}Click`); + } } - } } diff --git a/src/components/button/Button.unit.js b/src/components/button/Button.unit.js index 216fcb0ca6..407fb25d4c 100644 --- a/src/components/button/Button.unit.js +++ b/src/components/button/Button.unit.js @@ -5,438 +5,527 @@ import ButtonComponent from './Button'; import { Formio } from './../../Formio'; import sinon from 'sinon'; -import { - comp1, - comp2, - comp3 -} from './fixtures'; +import { comp1, comp2, comp3 } from './fixtures'; import Webform from '../../Webform'; import formWithResetValue from '../../../test/formtest/formWithResetValue'; -describe('Button Component', function() { - it('Should build a button component', function() { - return Harness.testCreate(ButtonComponent, comp1).then((component) => { - const buttons = Harness.testElements(component, 'button[type="submit"]', 1); - for (const button of buttons) { - assert.equal(button.name, `data[${comp1.key}]`); - assert.equal(button.innerHTML.trim(), comp1.label); - } - }); - }); - - it('POST to URL button should pass URL and headers', function(done) { - const formJson = { - 'type': 'form', - 'components': [{ - 'label': 'Some Field', - 'type': 'textfield', - 'input': true, - 'key': 'someField' - }, - { - 'label': 'POST to URL', - 'action': 'url', - 'url': 'someUrl', - 'headers': [{ - 'header': 'testHeader', - 'value': 'testValue' - }], - 'type': 'button', - 'input': true, - 'key': 'postToUrl' - } - ] - }; - const element = document.createElement('div'); - Formio.createForm(element, formJson) - .then(form => { - const spy = sinon.spy(Formio, 'makeStaticRequest'); - form.getComponent('postToUrl').refs.button.click(); - const passedUrl = spy.firstCall.args[0]; - const passedHeaders = spy.firstCall.lastArg.headers; - spy.restore(); - assert.deepEqual(passedHeaders, { - 'testHeader': 'testValue' +describe('Button Component', function () { + it('Should build a button component', function () { + return Harness.testCreate(ButtonComponent, comp1).then((component) => { + const buttons = Harness.testElements( + component, + 'button[type="submit"]', + 1, + ); + for (const button of buttons) { + assert.equal(button.name, `data[${comp1.key}]`); + assert.equal(button.innerHTML.trim(), comp1.label); + } }); - assert.equal(passedUrl, 'someUrl'); - done(); - }) - .catch(done); - }); - - it('Test on error', function(done) { - const element = document.createElement('div'); - Formio.createForm(element, { - components: [{ - type: 'textfield', - key: 'a', - label: 'A', - validate: { - required: true - } - }, - { - type: 'button', - action: 'submit', - key: 'submit', - disableOnInvalid: true, - input: true - } - ] - }).then(form => { - form.on('change', () => { - const button = form.getComponent('submit'); - assert(button.disabled, 'Button should be disabled'); - button.emit('submitError'); - setTimeout(() => { - console.log('Text Content: ', button.refs.buttonMessage.innerHTML); - assert.equal(button.refs.buttonMessage.textContent, 'Please check the form and correct all errors before submitting.'); - done(); - }, 100); - }); - form.submission = { - data: {} - }; - }).catch(done); - }); - - it('POST to URL button should perform URL interpolation', function(done) { - const formJson = { - 'type': 'form', - 'components': [{ - 'label': 'Some Field', - 'type': 'textfield', - 'input': true, - 'key': 'someField' - }, - { - 'label': 'URL', - 'type': 'textfield', - 'input': true, - 'key': 'url' - }, - { - 'label': 'POST to URL', - 'action': 'url', - 'url': '{{data.url}}/submission', - 'type': 'button', - 'input': true, - 'key': 'postToUrl' - } - ] - }; - const element = document.createElement('div'); - Formio.createForm(element, formJson) - .then(form => { - form.submission = { - data: { - url: 'someUrl' - } + }); + + it('POST to URL button should pass URL and headers', function (done) { + const formJson = { + type: 'form', + components: [ + { + label: 'Some Field', + type: 'textfield', + input: true, + key: 'someField', + }, + { + label: 'POST to URL', + action: 'url', + url: 'someUrl', + headers: [ + { + header: 'testHeader', + value: 'testValue', + }, + ], + type: 'button', + input: true, + key: 'postToUrl', + }, + ], }; - return form.submissionReady - .then(() => { - const spy = sinon.spy(Formio, 'makeStaticRequest'); - form.getComponent('postToUrl').refs.button.click(); - const passedUrl = spy.firstCall.args[0]; - spy.restore(); - assert.equal(passedUrl, 'someUrl/submission'); - done(); - }); - }) - .catch(done); - }); - - it('POST to URL button should perform headers interpolation', function(done) { - const formJson = { - 'type': 'form', - 'components': [{ - 'label': 'Some Field', - 'type': 'textfield', - 'input': true, - 'key': 'someField' - }, - { - 'label': 'Header', - 'type': 'textfield', - 'input': true, - 'key': 'header' - }, - { - 'label': 'POST to URL', - 'action': 'url', - 'url': 'someUrl', - 'headers': [{ - 'header': 'testHeader', - 'value': 'Value {{data.header}}' - }], - 'type': 'button', - 'input': true, - 'key': 'postToUrl' - } - ] - }; - const element = document.createElement('div'); - Formio.createForm(element, formJson) - .then(form => { - form.submission = { - data: { - someField: 'some value', - header: 'some header' - } + const element = document.createElement('div'); + Formio.createForm(element, formJson) + .then((form) => { + const spy = sinon.spy(Formio, 'makeStaticRequest'); + form.getComponent('postToUrl').refs.button.click(); + const passedUrl = spy.firstCall.args[0]; + const passedHeaders = spy.firstCall.lastArg.headers; + spy.restore(); + assert.deepEqual(passedHeaders, { + testHeader: 'testValue', + }); + assert.equal(passedUrl, 'someUrl'); + done(); + }) + .catch(done); + }); + + it('Test on error', function (done) { + const element = document.createElement('div'); + Formio.createForm(element, { + components: [ + { + type: 'textfield', + key: 'a', + label: 'A', + validate: { + required: true, + }, + }, + { + type: 'button', + action: 'submit', + key: 'submit', + disableOnInvalid: true, + input: true, + }, + ], + }) + .then((form) => { + form.on('change', () => { + const button = form.getComponent('submit'); + assert(button.disabled, 'Button should be disabled'); + button.emit('submitError'); + setTimeout(() => { + console.log( + 'Text Content: ', + button.refs.buttonMessage.innerHTML, + ); + assert.equal( + button.refs.buttonMessage.textContent, + 'Please check the form and correct all errors before submitting.', + ); + done(); + }, 100); + }); + form.submission = { + data: {}, + }; + }) + .catch(done); + }); + + it('POST to URL button should perform URL interpolation', function (done) { + const formJson = { + type: 'form', + components: [ + { + label: 'Some Field', + type: 'textfield', + input: true, + key: 'someField', + }, + { + label: 'URL', + type: 'textfield', + input: true, + key: 'url', + }, + { + label: 'POST to URL', + action: 'url', + url: '{{data.url}}/submission', + type: 'button', + input: true, + key: 'postToUrl', + }, + ], }; - return form.submissionReady - .then(() => { - const spy = sinon.spy(Formio, 'makeStaticRequest'); - form.getComponent('postToUrl').refs.button.click(); - const passedHeaders = spy.firstCall.lastArg.headers; - spy.restore(); - assert.deepEqual(passedHeaders, { - 'testHeader': 'Value some header' + const element = document.createElement('div'); + Formio.createForm(element, formJson) + .then((form) => { + form.submission = { + data: { + url: 'someUrl', + }, + }; + return form.submissionReady.then(() => { + const spy = sinon.spy(Formio, 'makeStaticRequest'); + form.getComponent('postToUrl').refs.button.click(); + const passedUrl = spy.firstCall.args[0]; + spy.restore(); + assert.equal(passedUrl, 'someUrl/submission'); + done(); + }); + }) + .catch(done); + }); + + it('POST to URL button should perform headers interpolation', function (done) { + const formJson = { + type: 'form', + components: [ + { + label: 'Some Field', + type: 'textfield', + input: true, + key: 'someField', + }, + { + label: 'Header', + type: 'textfield', + input: true, + key: 'header', + }, + { + label: 'POST to URL', + action: 'url', + url: 'someUrl', + headers: [ + { + header: 'testHeader', + value: 'Value {{data.header}}', + }, + ], + type: 'button', + input: true, + key: 'postToUrl', + }, + ], + }; + const element = document.createElement('div'); + Formio.createForm(element, formJson) + .then((form) => { + form.submission = { + data: { + someField: 'some value', + header: 'some header', + }, + }; + return form.submissionReady.then(() => { + const spy = sinon.spy(Formio, 'makeStaticRequest'); + form.getComponent('postToUrl').refs.button.click(); + const passedHeaders = spy.firstCall.lastArg.headers; + spy.restore(); + assert.deepEqual(passedHeaders, { + testHeader: 'Value some header', + }); + done(); + }); + }) + .catch(done); + }); + + it('Should not change color and show message if the error is silent', function (done) { + const formJson = { + type: 'form', + components: [ + { + label: 'Some Field', + type: 'textfield', + input: true, + key: 'someField', + }, + { + label: 'Submit', + action: 'submit', + type: 'button', + input: true, + key: 'submit', + }, + ], + }; + const element = document.createElement('div'); + Formio.createForm(element, formJson, { + hooks: { + beforeSubmit: function (submission, callback) { + callback( + { + message: 'Err', + component: submission.component, + silent: true, + }, + submission, + ); + }, + }, + }) + .then((form) => { + const button = form.getComponent('submit'); + button.emit('submitButton', { + state: button.component.state || 'submitted', + component: button.component, + instance: button, + }); + setTimeout(() => { + assert( + !button.refs.button.className.includes( + 'btn-danger submit-fail', + ), + ); + assert( + !button.refs.button.className.includes( + 'btn-success submit-success', + ), + ); + assert( + !button.refs.buttonMessageContainer.className.includes( + 'has-success', + ), + ); + assert( + !button.refs.buttonMessageContainer.className.includes( + 'has-error', + ), + ); + assert(button.refs.buttonMessage.innerHTML === ''); + done(); + }, 100); + }) + .catch(done); + }); + + it("Should reset values of all the form's components and update properties dependent on values", function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement); + + form.setForm(formWithResetValue) + .then(() => { + const select = form.getComponent(['showPanel']); + + select.setValue('yes'); + + setTimeout(() => { + const panel = form.getComponent(['panel']); + const textField = form.getComponent(['textField']); + const textArea = form.getComponent(['textArea']); + + assert.equal( + panel.visible, + true, + 'Panel should be visible', + ); + assert.equal( + textField.visible, + true, + 'TextFiled should be visible', + ); + assert.equal( + textArea.visible, + true, + 'TextArea should be visible', + ); + + const resetButton = form.getComponent(['reset']); + resetButton.emit('resetForm'); + + setTimeout(() => { + const panel = form.getComponent(['panel']); + const textField = form.getComponent(['textField']); + const textArea = form.getComponent(['textArea']); + + assert.equal( + panel.visible, + false, + 'Panel should NOT be visible', + ); + assert.equal( + textField.visible, + false, + 'TextFiled should NOT be visible', + ); + assert.equal( + textArea.visible, + false, + 'TextArea should NOT be visible', + ); + done(); + }, 300); + }, 300); + }) + .catch((err) => done(err)); + }); + + it('Should perform custom logic', function (done) { + const element = document.createElement('div'); + const form = new Webform(element); + const testForm = { + components: [ + { + type: 'number', + key: 'number', + label: 'Number', + }, + { + type: 'button', + key: 'custom', + label: 'Custom', + action: 'custom', + custom: "data['number'] = 5555", + }, + ], + }; + + form.setForm(testForm) + .then(() => { + const button = form.getComponent('custom'); + const changeEventTriggered = sinon.spy(button, 'triggerChange'); + button.refs.button.click(); + assert( + changeEventTriggered.calledOnce, + 'Click on custom button should trigger change event', + ); + form.on('change', () => { + const { data } = form.submission; + assert.deepEqual(data, { + number: 5555, + custom: true, + }); + done(); + }); + }) + .catch((err) => done(err)); + }); + + it('Should correctly set theme', function (done) { + const form = _.cloneDeep(comp2); + const element = document.createElement('div'); + + Formio.createForm(element, form) + .then((formObj) => { + const btns = formObj.components; + const theme = [ + 'warning', + 'danger', + 'success', + 'info', + 'secondary', + 'primary', + ]; + + _.each(btns, (btn, index) => { + const btnClass = `btn-${theme[index]}`; + const includeClass = + btn.refs.button.classList.contains(btnClass); + assert.equal( + includeClass, + true, + `Should set ${theme[index]} theme for button`, + ); + }); + + done(); + }) + .catch(done); + }); + + it('Should render block btn', function (done) { + const form = _.cloneDeep(comp2); + form.components = [ + { + label: 'Submit', + showValidations: false, + block: true, + tableView: false, + key: 'submit', + type: 'button', + input: true, + }, + ]; + + const element = document.createElement('div'); + + Formio.createForm(element, form) + .then((formObj) => { + const btn = formObj.components[0]; + const btnClass = 'btn-block'; + const includeClass = + btn.refs.button.classList.contains(btnClass); + + assert.equal( + includeClass, + true, + 'Should set btn-block class for button', + ); + + done(); + }) + .catch(done); + }); + + it('Test event, reset, post, save in state actions', function (done) { + const form = _.cloneDeep(comp3); + const element = document.createElement('div'); + + const originalMakeRequest = Formio.makeStaticRequest; + Formio.makeStaticRequest = function (url, method, data) { + assert.equal(url, 'https://test.com'); + assert.equal(method, 'POST'); + assert.deepEqual(data.data, { + event: false, + post: true, + reset: false, + saveInState: false, }); - done(); - }); - }) - .catch(done); - }); - - it('Should not change color and show message if the error is silent', function(done) { - const formJson = { - 'type': 'form', - 'components': [{ - 'label': 'Some Field', - 'type': 'textfield', - 'input': true, - 'key': 'someField' - }, - { - 'label': 'Submit', - 'action': 'submit', - 'type': 'button', - 'input': true, - 'key': 'submit' - } - ] - }; - const element = document.createElement('div'); - Formio.createForm(element, formJson, { - hooks: { - beforeSubmit: function(submission, callback) { - callback({ - message: 'Err', - component: submission.component, - silent: true, - }, submission); - } - } - }) - .then(form => { - const button = form.getComponent('submit'); - button.emit('submitButton', { - state: button.component.state || 'submitted', - component: button.component, - instance: button - }); - setTimeout(() => { - assert(!button.refs.button.className.includes('btn-danger submit-fail')); - assert(!button.refs.button.className.includes('btn-success submit-success')); - assert(!button.refs.buttonMessageContainer.className.includes('has-success')); - assert(!button.refs.buttonMessageContainer.className.includes('has-error')); - assert(button.refs.buttonMessage.innerHTML === ''); - done(); - }, 100); - }) - .catch(done); - }); - - it('Should reset values of all the form\'s components and update properties dependent on values', function(done) { - const formElement = document.createElement('div'); - const form = new Webform(formElement); - - form.setForm(formWithResetValue).then(() => { - const select = form.getComponent(['showPanel']); - - select.setValue('yes'); - - setTimeout(() => { - const panel = form.getComponent(['panel']); - const textField = form.getComponent(['textField']); - const textArea = form.getComponent(['textArea']); - - assert.equal(panel.visible, true, 'Panel should be visible'); - assert.equal(textField.visible, true, 'TextFiled should be visible'); - assert.equal(textArea.visible, true, 'TextArea should be visible'); - - const resetButton = form.getComponent(['reset']); - resetButton.emit('resetForm'); - - setTimeout(() => { - const panel = form.getComponent(['panel']); - const textField = form.getComponent(['textField']); - const textArea = form.getComponent(['textArea']); - - assert.equal(panel.visible, false, 'Panel should NOT be visible'); - assert.equal(textField.visible, false, 'TextFiled should NOT be visible'); - assert.equal(textArea.visible, false, 'TextArea should NOT be visible'); - done(); - }, 300); - }, 300); - }).catch((err) => done(err)); - }); - - it('Should perform custom logic', function(done) { - const element = document.createElement('div'); - const form = new Webform(element); - const testForm = { - components: [{ - type: 'number', - key: 'number', - label: 'Number' - }, - { - type: 'button', - key: 'custom', - label: 'Custom', - action: 'custom', - custom: 'data[\'number\'] = 5555' - } - ] - }; - - form.setForm(testForm) - .then(() => { - const button = form.getComponent('custom'); - const changeEventTriggered = sinon.spy(button, 'triggerChange'); - button.refs.button.click(); - assert(changeEventTriggered.calledOnce, 'Click on custom button should trigger change event'); - form.on('change', () => { - const { - data - } = form.submission; - assert.deepEqual(data, { - number: 5555, - custom: true - }); - done(); - }); - }) - .catch((err) => done(err)); - }); - - it('Should correctly set theme', function(done) { - const form = _.cloneDeep(comp2); - const element = document.createElement('div'); - - Formio.createForm(element, form).then(formObj => { - const btns = formObj.components; - const theme = ['warning', 'danger', 'success', 'info', 'secondary', 'primary']; - - _.each(btns, (btn, index) => { - const btnClass = `btn-${theme[index]}`; - const includeClass = btn.refs.button.classList.contains(btnClass); - assert.equal(includeClass, true, `Should set ${theme[index]} theme for button`); - }); - - done(); - }).catch(done); - }); - - it('Should render block btn', function(done) { - const form = _.cloneDeep(comp2); - form.components = [{ - label: 'Submit', - showValidations: false, - block: true, - tableView: false, - key: 'submit', - type: 'button', - input: true - }]; - - const element = document.createElement('div'); - - Formio.createForm(element, form).then(formObj => { - const btn = formObj.components[0]; - const btnClass = 'btn-block'; - const includeClass = btn.refs.button.classList.contains(btnClass); - - assert.equal(includeClass, true, 'Should set btn-block class for button'); - - done(); - }).catch(done); - }); - - it('Test event, reset, post, save in state actions', function(done) { - const form = _.cloneDeep(comp3); - const element = document.createElement('div'); - - const originalMakeRequest = Formio.makeStaticRequest; - Formio.makeStaticRequest = function(url, method, data) { - assert.equal(url, 'https://test.com'); - assert.equal(method, 'POST'); - assert.deepEqual(data.data, { - event: false, - post: true, - reset: false, - saveInState: false - }); - - return new Promise(resolve => { - resolve({ - ...data - }); - }); - }; - - Formio.createForm(element, form).then(form => { - const formio = new Formio('http://test.localhost/test', {}); - - formio.makeRequest = (type, url, method, data) => { - assert.equal(data.state, 'testState'); - assert.equal(method.toUpperCase(), 'POST'); - - return new Promise(resolve => resolve({ - ...data - })); - }; - - form.formio = formio; - - const click = (btnComp) => { - const elem = btnComp.refs.button; - const clickEvent = new Event('click'); - elem.dispatchEvent(clickEvent); - }; - - const saveInStateBtn = form.getComponent('saveInState'); - click(saveInStateBtn); - - setTimeout(() => { - const eventBtn = form.getComponent('event'); - click(eventBtn); - - setTimeout(() => { - const numberComp = form.getComponent('number'); - assert.equal(numberComp.dataValue, 2); - assert.equal(numberComp.getValue(), 2); - - const resetBtn = form.getComponent('reset'); - click(resetBtn); - - setTimeout(() => { - const numberComp = form.getComponent('number'); - assert.equal(numberComp.dataValue, null); - assert.equal(numberComp.getValue(), null); - - const postBtn = form.getComponent('post'); - click(postBtn); - - setTimeout(() => { - Formio.makeStaticRequest = originalMakeRequest; - done(); - }, 300); - }, 300); - }, 300); - }, 300); - }).catch(done); - }); + + return new Promise((resolve) => { + resolve({ + ...data, + }); + }); + }; + + Formio.createForm(element, form) + .then((form) => { + const formio = new Formio('http://test.localhost/test', {}); + + formio.makeRequest = (type, url, method, data) => { + assert.equal(data.state, 'testState'); + assert.equal(method.toUpperCase(), 'POST'); + + return new Promise((resolve) => + resolve({ + ...data, + }), + ); + }; + + form.formio = formio; + + const click = (btnComp) => { + const elem = btnComp.refs.button; + const clickEvent = new Event('click'); + elem.dispatchEvent(clickEvent); + }; + + const saveInStateBtn = form.getComponent('saveInState'); + click(saveInStateBtn); + + setTimeout(() => { + const eventBtn = form.getComponent('event'); + click(eventBtn); + + setTimeout(() => { + const numberComp = form.getComponent('number'); + assert.equal(numberComp.dataValue, 2); + assert.equal(numberComp.getValue(), 2); + + const resetBtn = form.getComponent('reset'); + click(resetBtn); + + setTimeout(() => { + const numberComp = form.getComponent('number'); + assert.equal(numberComp.dataValue, null); + assert.equal(numberComp.getValue(), null); + + const postBtn = form.getComponent('post'); + click(postBtn); + + setTimeout(() => { + Formio.makeStaticRequest = originalMakeRequest; + done(); + }, 300); + }, 300); + }, 300); + }, 300); + }) + .catch(done); + }); }); diff --git a/src/components/button/editForm/Button.edit.display.js b/src/components/button/editForm/Button.edit.display.js index 0ffcc6825b..52c255fc84 100644 --- a/src/components/button/editForm/Button.edit.display.js +++ b/src/components/button/editForm/Button.edit.display.js @@ -2,250 +2,255 @@ import BuilderUtils from '../../../utils/builder'; import _ from 'lodash'; export default [ - { - key: 'labelPosition', - ignore: true, - }, - { - key: 'placeholder', - ignore: true, - }, - { - key: 'hideLabel', - customConditional(context) { - return context.instance.options?.flags?.inDataGrid; - }, - }, - { - key: 'dataGridLabel', - ignore: true, - }, - { - type: 'select', - key: 'action', - label: 'Action', - input: true, - dataSrc: 'values', - weight: 110, - tooltip: 'This is the action to be performed by this button.', - data: { - values: [ - { label: 'Submit', value: 'submit' }, - { label: 'Save in state', value: 'saveState' }, - { label: 'Event', value: 'event' }, - { label: 'Custom', value: 'custom' }, - { label: 'Reset', value: 'reset' }, - { label: 'OAuth', value: 'oauth' }, - { label: 'POST to URL', value: 'url' }, - ], - }, - }, - { - type: 'select', - key: 'oauthProvider', - label: 'OAuth Provider', - input: true, - dataSrc: 'values', - weight: 111, - tooltip: 'The oauth provider to use to log in (8.x server only).', - data: { - values: [ - { label: 'OpenID', value: 'openid' }, - { label: 'Github', value: 'github' }, - { label: 'Google', value: 'google' }, - ], - }, - conditional: { - json: { '===': [{ var: 'data.action' }, 'oauth'] }, - }, - }, - { - type: 'textfield', - label: 'Save in state', - key: 'state', - weight: 112, - tooltip: 'The state you wish to save the submission under when this button is pressed. Example "draft" would save the submission in Draft Mode.', - placeholder: 'submitted', - input: true, - conditional: { - json: { '===': [{ var: 'data.action' }, 'saveState'] }, - }, - }, - { - type: 'checkbox', - input: true, - inputType: 'checkbox', - key: 'saveOnEnter', - label: 'Save On Enter', - weight: 113, - tooltip: 'Use the Enter key to submit form.', - conditional: { - json: { '===': [{ var: 'data.action' }, 'submit'] }, - }, - }, - { - type: 'checkbox', - input: true, - inputType: 'checkbox', - key: 'showValidations', - label: 'Show Validations', - weight: 115, - tooltip: 'When the button is pressed, show any validation errors on the form.', - conditional: { - json: { '!==': [{ var: 'data.action' }, 'submit'] }, - }, - }, - { - type: 'textfield', - label: 'Button Event', - key: 'event', - input: true, - weight: 120, - tooltip: 'The event to fire when the button is clicked.', - conditional: { - json: { '===': [{ var: 'data.action' }, 'event'] }, - }, - }, - { - type: 'textfield', - inputType: 'url', - key: 'url', - input: true, - weight: 120, - label: 'Button URL', - tooltip: 'The URL where the submission will be sent.', - placeholder: 'https://example.form.io', - conditional: { - json: { '===': [{ var: 'data.action' }, 'url'] }, - }, - }, - { - type: 'datagrid', - key: 'headers', - input: true, - weight: 130, - label: 'Headers', - addAnother: 'Add Header', - tooltip: 'Headers Properties and Values for your request', - components: [ - { - key: 'header', - label: 'Header', + { + key: 'labelPosition', + ignore: true, + }, + { + key: 'placeholder', + ignore: true, + }, + { + key: 'hideLabel', + customConditional(context) { + return context.instance.options?.flags?.inDataGrid; + }, + }, + { + key: 'dataGridLabel', + ignore: true, + }, + { + type: 'select', + key: 'action', + label: 'Action', + input: true, + dataSrc: 'values', + weight: 110, + tooltip: 'This is the action to be performed by this button.', + data: { + values: [ + { label: 'Submit', value: 'submit' }, + { label: 'Save in state', value: 'saveState' }, + { label: 'Event', value: 'event' }, + { label: 'Custom', value: 'custom' }, + { label: 'Reset', value: 'reset' }, + { label: 'OAuth', value: 'oauth' }, + { label: 'POST to URL', value: 'url' }, + ], + }, + }, + { + type: 'select', + key: 'oauthProvider', + label: 'OAuth Provider', input: true, + dataSrc: 'values', + weight: 111, + tooltip: 'The oauth provider to use to log in (8.x server only).', + data: { + values: [ + { label: 'OpenID', value: 'openid' }, + { label: 'Github', value: 'github' }, + { label: 'Google', value: 'google' }, + ], + }, + conditional: { + json: { '===': [{ var: 'data.action' }, 'oauth'] }, + }, + }, + { type: 'textfield', - }, - { - key: 'value', - label: 'Value', + label: 'Save in state', + key: 'state', + weight: 112, + tooltip: + 'The state you wish to save the submission under when this button is pressed. Example "draft" would save the submission in Draft Mode.', + placeholder: 'submitted', input: true, + conditional: { + json: { '===': [{ var: 'data.action' }, 'saveState'] }, + }, + }, + { + type: 'checkbox', + input: true, + inputType: 'checkbox', + key: 'saveOnEnter', + label: 'Save On Enter', + weight: 113, + tooltip: 'Use the Enter key to submit form.', + conditional: { + json: { '===': [{ var: 'data.action' }, 'submit'] }, + }, + }, + { + type: 'checkbox', + input: true, + inputType: 'checkbox', + key: 'showValidations', + label: 'Show Validations', + weight: 115, + tooltip: + 'When the button is pressed, show any validation errors on the form.', + conditional: { + json: { '!==': [{ var: 'data.action' }, 'submit'] }, + }, + }, + { + type: 'textfield', + label: 'Button Event', + key: 'event', + input: true, + weight: 120, + tooltip: 'The event to fire when the button is clicked.', + conditional: { + json: { '===': [{ var: 'data.action' }, 'event'] }, + }, + }, + { type: 'textfield', - } - ], - conditional: { - json: { '===': [{ var: 'data.action' }, 'url'] }, - }, - }, - { - type: 'textarea', - key: 'custom', - label: 'Button Custom Logic', - tooltip: 'The custom logic to evaluate when the button is clicked.', - rows: 5, - editor: 'ace', - input: true, - weight: 120, - placeholder: "data['mykey'] = data['anotherKey'];", - conditional: { - json: { '===': [{ var: 'data.action' }, 'custom'] }, - }, - }, - { - type: 'select', - key: 'theme', - label: 'Theme', - input: true, - tooltip: 'The color theme of this button.', - dataSrc: 'values', - weight: 140, - data: { - values: [ - { label: 'Primary', value: 'primary' }, - { label: 'Secondary', value: 'secondary' }, - { label: 'Info', value: 'info' }, - { label: 'Success', value: 'success' }, - { label: 'Danger', value: 'danger' }, - { label: 'Warning', value: 'warning' }, - ], - }, - }, - { - type: 'select', - key: 'size', - label: 'Size', - input: true, - tooltip: 'The size of this button.', - dataSrc: 'values', - weight: 150, - data: { - values: [ - { label: 'Small', value: 'sm' }, - { label: 'Medium', value: 'md' }, - { label: 'Large', value: 'lg' }, - ], - }, - }, - { - type: 'textfield', - key: 'leftIcon', - label: 'Left Icon', - input: true, - placeholder: 'Enter icon classes', - tooltip: "This is the full icon class string to show the icon. Example: 'bi bi-plus'", - weight: 160, - }, - { - type: 'textfield', - key: 'rightIcon', - label: 'Right Icon', - input: true, - placeholder: 'Enter icon classes', - tooltip: "This is the full icon class string to show the icon. Example: 'bi bi-plus'", - weight: 170, - }, - { - type: 'select', - input: true, - weight: 180, - label: 'Shortcut', - key: 'shortcut', - tooltip: 'Shortcut for this component.', - dataSrc: 'custom', - valueProperty: 'value', - customDefaultValue: () => '', - template: '{{ item.label }}', - data: { - custom(context) { - return BuilderUtils.getAvailableShortcuts( - _.get(context, 'instance.options.editForm', {}), - _.get(context, 'instance.options.editComponent', {}) - ); - }, - }, - }, - { - type: 'checkbox', - key: 'block', - label: 'Block Button', - input: true, - weight: 155, - tooltip: 'This control should span the full width of the bounding container.', - }, - { - type: 'checkbox', - key: 'disableOnInvalid', - label: 'Disable on Form Invalid', - tooltip: 'This will disable this field if the form is invalid.', - input: true, - weight: 620, - }, + inputType: 'url', + key: 'url', + input: true, + weight: 120, + label: 'Button URL', + tooltip: 'The URL where the submission will be sent.', + placeholder: 'https://example.form.io', + conditional: { + json: { '===': [{ var: 'data.action' }, 'url'] }, + }, + }, + { + type: 'datagrid', + key: 'headers', + input: true, + weight: 130, + label: 'Headers', + addAnother: 'Add Header', + tooltip: 'Headers Properties and Values for your request', + components: [ + { + key: 'header', + label: 'Header', + input: true, + type: 'textfield', + }, + { + key: 'value', + label: 'Value', + input: true, + type: 'textfield', + }, + ], + conditional: { + json: { '===': [{ var: 'data.action' }, 'url'] }, + }, + }, + { + type: 'textarea', + key: 'custom', + label: 'Button Custom Logic', + tooltip: 'The custom logic to evaluate when the button is clicked.', + rows: 5, + editor: 'ace', + input: true, + weight: 120, + placeholder: "data['mykey'] = data['anotherKey'];", + conditional: { + json: { '===': [{ var: 'data.action' }, 'custom'] }, + }, + }, + { + type: 'select', + key: 'theme', + label: 'Theme', + input: true, + tooltip: 'The color theme of this button.', + dataSrc: 'values', + weight: 140, + data: { + values: [ + { label: 'Primary', value: 'primary' }, + { label: 'Secondary', value: 'secondary' }, + { label: 'Info', value: 'info' }, + { label: 'Success', value: 'success' }, + { label: 'Danger', value: 'danger' }, + { label: 'Warning', value: 'warning' }, + ], + }, + }, + { + type: 'select', + key: 'size', + label: 'Size', + input: true, + tooltip: 'The size of this button.', + dataSrc: 'values', + weight: 150, + data: { + values: [ + { label: 'Small', value: 'sm' }, + { label: 'Medium', value: 'md' }, + { label: 'Large', value: 'lg' }, + ], + }, + }, + { + type: 'textfield', + key: 'leftIcon', + label: 'Left Icon', + input: true, + placeholder: 'Enter icon classes', + tooltip: + "This is the full icon class string to show the icon. Example: 'bi bi-plus'", + weight: 160, + }, + { + type: 'textfield', + key: 'rightIcon', + label: 'Right Icon', + input: true, + placeholder: 'Enter icon classes', + tooltip: + "This is the full icon class string to show the icon. Example: 'bi bi-plus'", + weight: 170, + }, + { + type: 'select', + input: true, + weight: 180, + label: 'Shortcut', + key: 'shortcut', + tooltip: 'Shortcut for this component.', + dataSrc: 'custom', + valueProperty: 'value', + customDefaultValue: () => '', + template: '{{ item.label }}', + data: { + custom(context) { + return BuilderUtils.getAvailableShortcuts( + _.get(context, 'instance.options.editForm', {}), + _.get(context, 'instance.options.editComponent', {}), + ); + }, + }, + }, + { + type: 'checkbox', + key: 'block', + label: 'Block Button', + input: true, + weight: 155, + tooltip: + 'This control should span the full width of the bounding container.', + }, + { + type: 'checkbox', + key: 'disableOnInvalid', + label: 'Disable on Form Invalid', + tooltip: 'This will disable this field if the form is invalid.', + input: true, + weight: 620, + }, ]; diff --git a/src/components/button/fixtures/comp1.js b/src/components/button/fixtures/comp1.js index c4de6a9f46..c4b258bc9a 100644 --- a/src/components/button/fixtures/comp1.js +++ b/src/components/button/fixtures/comp1.js @@ -1,14 +1,14 @@ export default { - 'type': 'button', - 'theme': 'primary', - 'disableOnInvalid': false, - 'action': 'submit', - 'block': false, - 'rightIcon': '', - 'leftIcon': '', - 'size': 'md', - 'key': 'submit', - 'tableView': false, - 'label': 'Submit', - 'input': true + type: 'button', + theme: 'primary', + disableOnInvalid: false, + action: 'submit', + block: false, + rightIcon: '', + leftIcon: '', + size: 'md', + key: 'submit', + tableView: false, + label: 'Submit', + input: true, }; diff --git a/src/components/button/fixtures/comp2.js b/src/components/button/fixtures/comp2.js index f5f69f716b..269acb93f8 100644 --- a/src/components/button/fixtures/comp2.js +++ b/src/components/button/fixtures/comp2.js @@ -1,55 +1,62 @@ export default { - type: 'form', - components: [ - { - label: 'Submit', - showValidations: false, - theme: 'warning', - tableView: false, - key: 'submit5', - type: 'button', - input: true - }, - { - label: 'Submit', - showValidations: false, - theme: 'danger', - tableView: false, - key: 'submit4', - type: 'button', - input: true - }, - { - label: 'Submit', - showValidations: false, - theme: 'success', - tableView: false, - key: 'submit3', - type: 'button', - input: true - }, - { - label: 'Submit', - showValidations: false, - theme: 'info', - tableView: false, - key: 'submit2', - type: 'button', - input: true - }, - { - label: 'Submit', - showValidations: false, - theme: 'secondary', - tableView: false, - key: 'submit1', - type: 'button', - input: true - }, - { label: 'Submit', showValidations: false, tableView: false, key: 'submit', type: 'button', input: true } - ], - title: 'test checkbox', - display: 'form', - name: 'testCheckbox', - path: 'testcheckbox', + type: 'form', + components: [ + { + label: 'Submit', + showValidations: false, + theme: 'warning', + tableView: false, + key: 'submit5', + type: 'button', + input: true, + }, + { + label: 'Submit', + showValidations: false, + theme: 'danger', + tableView: false, + key: 'submit4', + type: 'button', + input: true, + }, + { + label: 'Submit', + showValidations: false, + theme: 'success', + tableView: false, + key: 'submit3', + type: 'button', + input: true, + }, + { + label: 'Submit', + showValidations: false, + theme: 'info', + tableView: false, + key: 'submit2', + type: 'button', + input: true, + }, + { + label: 'Submit', + showValidations: false, + theme: 'secondary', + tableView: false, + key: 'submit1', + type: 'button', + input: true, + }, + { + label: 'Submit', + showValidations: false, + tableView: false, + key: 'submit', + type: 'button', + input: true, + }, + ], + title: 'test checkbox', + display: 'form', + name: 'testCheckbox', + path: 'testcheckbox', }; diff --git a/src/components/button/fixtures/comp3.js b/src/components/button/fixtures/comp3.js index 6079c364dc..40fa2340cd 100644 --- a/src/components/button/fixtures/comp3.js +++ b/src/components/button/fixtures/comp3.js @@ -1,82 +1,82 @@ export default { - type: 'form', - components: [ - { - label: 'Number', - mask: false, - spellcheck: true, - tableView: false, - delimiter: false, - requireDecimal: false, - inputFormat: 'plain', - key: 'number', - logic: [ - { - name: 'show two', - trigger: { - type: 'event', - event: 'showTwo' - }, - actions: [ - { - name: 'simple action', - type: 'value', - value: 'value = 2;' - } - ] - } - ], - type: 'number', - input: true - }, - { - label: 'Save in state', - action: 'saveState', - showValidations: false, - tableView: false, - key: 'saveInState', - type: 'button', - state: 'testState', - input: true - }, - { - label: 'Event', - action: 'event', - showValidations: false, - tableView: false, - key: 'event', - type: 'button', - event: 'showTwo', - input: true - }, - { - label: 'Reset', - action: 'reset', - showValidations: false, - tableView: false, - key: 'reset', - type: 'button', - input: true - }, - { - label: 'Post', - action: 'url', - showValidations: false, - tableView: false, - key: 'post', - type: 'button', - url: 'https://test.com', - headers: [ - { - header: '', - value: '' - } - ], - input: true - } - ], - title: 'test', - display: 'form', - name: 'test', - path: 'test' + type: 'form', + components: [ + { + label: 'Number', + mask: false, + spellcheck: true, + tableView: false, + delimiter: false, + requireDecimal: false, + inputFormat: 'plain', + key: 'number', + logic: [ + { + name: 'show two', + trigger: { + type: 'event', + event: 'showTwo', + }, + actions: [ + { + name: 'simple action', + type: 'value', + value: 'value = 2;', + }, + ], + }, + ], + type: 'number', + input: true, + }, + { + label: 'Save in state', + action: 'saveState', + showValidations: false, + tableView: false, + key: 'saveInState', + type: 'button', + state: 'testState', + input: true, + }, + { + label: 'Event', + action: 'event', + showValidations: false, + tableView: false, + key: 'event', + type: 'button', + event: 'showTwo', + input: true, + }, + { + label: 'Reset', + action: 'reset', + showValidations: false, + tableView: false, + key: 'reset', + type: 'button', + input: true, + }, + { + label: 'Post', + action: 'url', + showValidations: false, + tableView: false, + key: 'post', + type: 'button', + url: 'https://test.com', + headers: [ + { + header: '', + value: '', + }, + ], + input: true, + }, + ], + title: 'test', + display: 'form', + name: 'test', + path: 'test', }; diff --git a/src/components/button/fixtures/values.js b/src/components/button/fixtures/values.js index e59550a17c..7b10bb7417 100644 --- a/src/components/button/fixtures/values.js +++ b/src/components/button/fixtures/values.js @@ -1,4 +1 @@ -export default [ - true, - false, -]; +export default [true, false]; diff --git a/src/components/checkbox/Checkbox.form.js b/src/components/checkbox/Checkbox.form.js index 2eca29b217..acff917668 100644 --- a/src/components/checkbox/Checkbox.form.js +++ b/src/components/checkbox/Checkbox.form.js @@ -3,19 +3,22 @@ import CheckboxEditData from './editForm/Checkbox.edit.data'; import CheckboxEditDisplay from './editForm/Checkbox.edit.display'; import CheckboxEditValidation from './editForm/Checkbox.edit.validation'; -export default function(...extend) { - return Components.baseEditForm([ - { - key: 'data', - components: CheckboxEditData - }, - { - key: 'display', - components: CheckboxEditDisplay - }, - { - key: 'validation', - components: CheckboxEditValidation - }, - ], ...extend); +export default function (...extend) { + return Components.baseEditForm( + [ + { + key: 'data', + components: CheckboxEditData, + }, + { + key: 'display', + components: CheckboxEditDisplay, + }, + { + key: 'validation', + components: CheckboxEditValidation, + }, + ], + ...extend, + ); } diff --git a/src/components/checkbox/Checkbox.js b/src/components/checkbox/Checkbox.js index 457a770938..bc7f484b3e 100644 --- a/src/components/checkbox/Checkbox.js +++ b/src/components/checkbox/Checkbox.js @@ -3,249 +3,272 @@ import { componentValueTypes, getComponentSavedTypes } from '../../utils/utils'; import Field from '../_classes/field/Field'; export default class CheckBoxComponent extends Field { - static schema(...extend) { - return Field.schema({ - type: 'checkbox', - inputType: 'checkbox', - label: 'Checkbox', - key: 'checkbox', - dataGridLabel: true, - labelPosition: 'right', - value: '', - name: '' - }, ...extend); - } - - static get builderInfo() { - return { - title: 'Checkbox', - group: 'basic', - icon: 'check-square', - documentation: '/userguide/form-building/form-components#check-box', - weight: 50, - schema: CheckBoxComponent.schema() - }; - } - - static get serverConditionSettings() { - return CheckBoxComponent.conditionOperatorsSettings; - } - - static get conditionOperatorsSettings() { - return { - ...super.conditionOperatorsSettings, - operators: ['isEqual'], - valueComponent() { + static schema(...extend) { + return Field.schema( + { + type: 'checkbox', + inputType: 'checkbox', + label: 'Checkbox', + key: 'checkbox', + dataGridLabel: true, + labelPosition: 'right', + value: '', + name: '', + }, + ...extend, + ); + } + + static get builderInfo() { + return { + title: 'Checkbox', + group: 'basic', + icon: 'check-square', + documentation: '/userguide/form-building/form-components#check-box', + weight: 50, + schema: CheckBoxComponent.schema(), + }; + } + + static get serverConditionSettings() { + return CheckBoxComponent.conditionOperatorsSettings; + } + + static get conditionOperatorsSettings() { + return { + ...super.conditionOperatorsSettings, + operators: ['isEqual'], + valueComponent() { + return { + valueType: 'boolean', + data: { + values: [ + { label: 'Checked', value: 'true' }, + { label: 'Not Checked', value: 'false' }, + ], + }, + type: 'select', + }; + }, + }; + } + + static savedValueTypes(schema) { + schema = schema || {}; + const types = getComponentSavedTypes(schema); + + if (_.isArray(types)) { + return types; + } + + if (schema.inputType === 'radio') { + return [componentValueTypes.string]; + } + + return [componentValueTypes.boolean]; + } + + get defaultSchema() { + return CheckBoxComponent.schema(); + } + + get labelClass() { + let className = ''; + if ( + this.isInputComponent && + !this.options.inputsOnly && + this.component.validate && + this.component.validate.required + ) { + className += ' field-required'; + } + return `${className}`; + } + + get hasSetValue() { + return this.hasValue(); + } + + get inputInfo() { + const info = super.elementInfo(); + info.type = 'input'; + info.changeEvent = 'click'; + info.attr.type = this.component.inputType || 'checkbox'; + info.attr.class = 'form-check-input'; + if (this.component.name) { + info.attr.name = `data[${this.component.name}]`; + } + info.attr.value = this.component.value ? this.component.value : 0; + info.label = this.t(this.component.label, { _userInput: true }); + info.labelClass = this.labelClass; + return info; + } + + get labelInfo() { return { - valueType: 'boolean', - data: { - values: [ - { label: 'Checked', value: 'true' }, - { label: 'Not Checked', value: 'false' }, - ] - }, - type: 'select' + hidden: true, }; - } - }; - } - - static savedValueTypes(schema) { - schema = schema || {}; - const types = getComponentSavedTypes(schema); - - if (_.isArray(types)) { - return types; - } - - if (schema.inputType === 'radio') { - return [componentValueTypes.string]; - } - - return [componentValueTypes.boolean]; - } - - get defaultSchema() { - return CheckBoxComponent.schema(); - } - - get labelClass() { - let className = ''; - if (this.isInputComponent - && !this.options.inputsOnly - && this.component.validate - && this.component.validate.required) { - className += ' field-required'; - } - return `${className}`; - } - - get hasSetValue() { - return this.hasValue(); - } - - get inputInfo() { - const info = super.elementInfo(); - info.type = 'input'; - info.changeEvent = 'click'; - info.attr.type = this.component.inputType || 'checkbox'; - info.attr.class = 'form-check-input'; - if (this.component.name) { - info.attr.name = `data[${this.component.name}]`; - } - info.attr.value = this.component.value ? this.component.value : 0; - info.label = this.t(this.component.label, { _userInput: true }); - info.labelClass = this.labelClass; - return info; - } - - get labelInfo() { - return { - hidden: true - }; - } - - render() { - return super.render(this.renderTemplate('checkbox', { - input: this.inputInfo, - checked: this.checked, - tooltip: this.interpolate(this.t(this.component.tooltip) || '', { _userInput: true }).replace(/(?:\r\n|\r|\n)/g, '
    ') - })); - } - - attach(element) { - this.loadRefs(element, { input: 'multiple' }); - this.input = this.refs.input[0]; - if (this.refs.input) { - this.addEventListener(this.input, this.inputInfo.changeEvent, () => this.updateValue(null, { - modified: true - })); - this.addShortcut(this.input); - } - return super.attach(element); - } - - detach(element) { - if (element && this.input) { - this.removeShortcut(this.input); - } - super.detach(); - } - - get emptyValue() { - return this.component.inputType === 'radio' ? null : false; - } - - isEmpty(value = this.dataValue) { - return super.isEmpty(value) || value === false; - } - - get key() { - return this.component.name ? this.component.name : super.key; - } - - getValueAt(index) { - if (this.component.name) { - return this.refs.input[index].checked ? this.component.value : ''; - } - return !!this.refs.input[index].checked; - } - - getValue() { - const value = super.getValue(); - if (this.component.name) { - return value ? this.setCheckedState(value) : this.setCheckedState(this.dataValue); - } - else { - return (value === '') ? this.dataValue : !!value; - } - } - - get checked() { - if (this.component.name) { - return (this.dataValue === this.component.value); - } - return !!this.dataValue; - } - - setCheckedState(value) { - if (!this.input) { - return; - } - if (this.component.name) { - this.input.value = (value === this.component.value) ? this.component.value : 0; - this.input.checked = (value === this.component.value) ? 1 : 0; - } - else if (value === 'on') { - this.input.value = 1; - this.input.checked = 1; - } - else if (value === 'off') { - this.input.value = 0; - this.input.checked = 0; - } - else if (value) { - this.input.value = 1; - this.input.checked = 1; - } - else { - this.input.value = 0; - this.input.checked = 0; - } - if (this.input.checked) { - this.input.setAttribute('checked', true); - } - else { - this.input.removeAttribute('checked'); - } - return value; - } - - setValue(value, flags = {}) { - if ( - this.setCheckedState(value) !== undefined || - (!this.input && value !== undefined && (this.visible || this.conditionallyVisible() || !this.component.clearOnHide)) - ) { - const changed = this.updateValue(value, flags); - if (this.isHtmlRenderMode() && flags && flags.fromSubmission && changed) { - this.redraw(); - } - return changed; } - return false; - } - getValueAsString(value) { - const { name: componentName, value: componentValue } = this.component; - const hasValue = componentName ? _.isEqual(value, componentValue) : value; - if (_.isUndefined(value) && this.inDataTable) { - return ''; + render() { + return super.render( + this.renderTemplate('checkbox', { + input: this.inputInfo, + checked: this.checked, + tooltip: this.interpolate( + this.t(this.component.tooltip) || '', + { _userInput: true }, + ).replace(/(?:\r\n|\r|\n)/g, '
    '), + }), + ); + } + + attach(element) { + this.loadRefs(element, { input: 'multiple' }); + this.input = this.refs.input[0]; + if (this.refs.input) { + this.addEventListener(this.input, this.inputInfo.changeEvent, () => + this.updateValue(null, { + modified: true, + }), + ); + this.addShortcut(this.input); + } + return super.attach(element); + } + + detach(element) { + if (element && this.input) { + this.removeShortcut(this.input); + } + super.detach(); } - return this.t(hasValue ? 'Yes' : 'No'); - } + get emptyValue() { + return this.component.inputType === 'radio' ? null : false; + } + + isEmpty(value = this.dataValue) { + return super.isEmpty(value) || value === false; + } - updateValue(value, flags) { - // If this is a radio and is alredy checked, uncheck it. - if (this.component.name && flags.modified && (this.dataValue === this.component.value)) { - this.input.checked = 0; - this.input.value = 0; - this.dataValue = ''; - this.updateOnChange(flags, true); + get key() { + return this.component.name ? this.component.name : super.key; } - const changed = super.updateValue(value, flags); + getValueAt(index) { + if (this.component.name) { + return this.refs.input[index].checked ? this.component.value : ''; + } + return !!this.refs.input[index].checked; + } + + getValue() { + const value = super.getValue(); + if (this.component.name) { + return value + ? this.setCheckedState(value) + : this.setCheckedState(this.dataValue); + } else { + return value === '' ? this.dataValue : !!value; + } + } - // Update attributes of the input element - if (changed && this.input) { - if (this.input.checked) { - this.input.setAttribute('checked', 'true'); - } - else { - this.input.removeAttribute('checked'); - } + get checked() { + if (this.component.name) { + return this.dataValue === this.component.value; + } + return !!this.dataValue; } - return changed; - } + setCheckedState(value) { + if (!this.input) { + return; + } + if (this.component.name) { + this.input.value = + value === this.component.value ? this.component.value : 0; + this.input.checked = value === this.component.value ? 1 : 0; + } else if (value === 'on') { + this.input.value = 1; + this.input.checked = 1; + } else if (value === 'off') { + this.input.value = 0; + this.input.checked = 0; + } else if (value) { + this.input.value = 1; + this.input.checked = 1; + } else { + this.input.value = 0; + this.input.checked = 0; + } + if (this.input.checked) { + this.input.setAttribute('checked', true); + } else { + this.input.removeAttribute('checked'); + } + return value; + } + + setValue(value, flags = {}) { + if ( + this.setCheckedState(value) !== undefined || + (!this.input && + value !== undefined && + (this.visible || + this.conditionallyVisible() || + !this.component.clearOnHide)) + ) { + const changed = this.updateValue(value, flags); + if ( + this.isHtmlRenderMode() && + flags && + flags.fromSubmission && + changed + ) { + this.redraw(); + } + return changed; + } + return false; + } + + getValueAsString(value) { + const { name: componentName, value: componentValue } = this.component; + const hasValue = componentName + ? _.isEqual(value, componentValue) + : value; + if (_.isUndefined(value) && this.inDataTable) { + return ''; + } + + return this.t(hasValue ? 'Yes' : 'No'); + } + + updateValue(value, flags) { + // If this is a radio and is alredy checked, uncheck it. + if ( + this.component.name && + flags.modified && + this.dataValue === this.component.value + ) { + this.input.checked = 0; + this.input.value = 0; + this.dataValue = ''; + this.updateOnChange(flags, true); + } + + const changed = super.updateValue(value, flags); + + // Update attributes of the input element + if (changed && this.input) { + if (this.input.checked) { + this.input.setAttribute('checked', 'true'); + } else { + this.input.removeAttribute('checked'); + } + } + + return changed; + } } diff --git a/src/components/checkbox/Checkbox.unit.js b/src/components/checkbox/Checkbox.unit.js index c9c81b5613..797b8e57e3 100644 --- a/src/components/checkbox/Checkbox.unit.js +++ b/src/components/checkbox/Checkbox.unit.js @@ -5,90 +5,125 @@ import Harness from '../../../test/harness'; import { Formio } from './../../Formio'; import CheckBoxComponent from './Checkbox'; -import { - comp1, - customDefaultComponent, - comp2, - comp3, - comp4 -} from './fixtures'; +import { comp1, customDefaultComponent, comp2, comp3, comp4 } from './fixtures'; -describe('Checkbox Component', function() { - it('Should build a checkbox component', function() { - return Harness.testCreate(CheckBoxComponent, comp1).then((component) => { - const inputs = Harness.testElements(component, 'input[type="checkbox"]', 1); - for (let i=0; i < inputs.length; i++) { - assert(inputs[i].getAttribute('class').indexOf('form-check-input') !== -1, 'No form-check-input class'); - assert.equal(inputs[i].name, `data[${comp1.key}]`); - } - Harness.testElements(component, 'span', 1); +describe('Checkbox Component', function () { + it('Should build a checkbox component', function () { + return Harness.testCreate(CheckBoxComponent, comp1).then( + (component) => { + const inputs = Harness.testElements( + component, + 'input[type="checkbox"]', + 1, + ); + for (let i = 0; i < inputs.length; i++) { + assert( + inputs[i] + .getAttribute('class') + .indexOf('form-check-input') !== -1, + 'No form-check-input class', + ); + assert.equal(inputs[i].name, `data[${comp1.key}]`); + } + Harness.testElements(component, 'span', 1); + }, + ); }); - }); - it('Span should have correct text label', function() { - return Harness.testCreate(CheckBoxComponent, comp1).then((component) => { - const checkboxes = component.element.querySelectorAll('input[ref="input"]'); - assert.equal(checkboxes.length, 1); - const componentClass = checkboxes[0].getAttribute('class'); - assert(componentClass.indexOf('form-check-input') !== -1, 'No form-check-input class.'); - const labels = component.element.querySelectorAll('label'); - assert.equal(labels.length, 1); - assert(labels[0].getAttribute('class').indexOf('form-check-label') !== -1, 'No form-check-label class'); - const spans = labels[0].querySelectorAll('span'); - assert.equal(spans.length, 1); - assert.equal(spans[0].innerHTML, 'Check me'); + it('Span should have correct text label', function () { + return Harness.testCreate(CheckBoxComponent, comp1).then( + (component) => { + const checkboxes = + component.element.querySelectorAll('input[ref="input"]'); + assert.equal(checkboxes.length, 1); + const componentClass = checkboxes[0].getAttribute('class'); + assert( + componentClass.indexOf('form-check-input') !== -1, + 'No form-check-input class.', + ); + const labels = component.element.querySelectorAll('label'); + assert.equal(labels.length, 1); + assert( + labels[0] + .getAttribute('class') + .indexOf('form-check-label') !== -1, + 'No form-check-label class', + ); + const spans = labels[0].querySelectorAll('span'); + assert.equal(spans.length, 1); + assert.equal(spans[0].innerHTML, 'Check me'); + }, + ); }); - }); - it('Should be able to set and get data', function() { - return Harness.testCreate(CheckBoxComponent, comp1).then((component) => { - Harness.testSetGet(component, 1); - Harness.testSetGet(component, 0); + it('Should be able to set and get data', function () { + return Harness.testCreate(CheckBoxComponent, comp1).then( + (component) => { + Harness.testSetGet(component, 1); + Harness.testSetGet(component, 0); + }, + ); }); - }); - it('Should be able to set custom default value', function() { - return Harness.testCreate(CheckBoxComponent, customDefaultComponent).then((component) => { - assert.equal(component.dataValue, true); - }); - }); + it('Should be able to set custom default value', function () { + return Harness.testCreate( + CheckBoxComponent, + customDefaultComponent, + ).then((component) => { + assert.equal(component.dataValue, true); + }); + }); - it('Should be able to unselect a checkbox component with the radio input type', function() { - return Harness.testCreate(CheckBoxComponent, comp2).then((component) => { - const input = Harness.testElements(component, 'input[type="radio"]', 1)[0]; - Harness.clickElement(component, input); - assert.equal(input.checked, true); - Harness.clickElement(component, input); - assert.equal(input.checked, false); + it('Should be able to unselect a checkbox component with the radio input type', function () { + return Harness.testCreate(CheckBoxComponent, comp2).then( + (component) => { + const input = Harness.testElements( + component, + 'input[type="radio"]', + 1, + )[0]; + Harness.clickElement(component, input); + assert.equal(input.checked, true); + Harness.clickElement(component, input); + assert.equal(input.checked, false); + }, + ); }); - }); - it('Should render red asterisk for preview template of the modal required checkbox ', function(done) { - Harness.testCreate(CheckBoxComponent, comp3).then((component) => { - const label = component.element.querySelector('.control-label'); - assert(label.className.includes('field-required')); - done(); - }).catch(done); - }); + it('Should render red asterisk for preview template of the modal required checkbox ', function (done) { + Harness.testCreate(CheckBoxComponent, comp3) + .then((component) => { + const label = component.element.querySelector('.control-label'); + assert(label.className.includes('field-required')); + done(); + }) + .catch(done); + }); - it('Should hide component with conditional logic when checkbox component with the radio input type is unchecked', function(done) { - const form = _.cloneDeep(comp4); - const element = document.createElement('div'); + it('Should hide component with conditional logic when checkbox component with the radio input type is unchecked', function (done) { + const form = _.cloneDeep(comp4); + const element = document.createElement('div'); - Formio.createForm(element, form).then(form => { - const radioCheckbox = form.getComponent('p1'); - const contentComp = form.getComponent('p1Content'); - assert.equal(contentComp.visible, false); - const radio = Harness.testElements(radioCheckbox, 'input[type="radio"]', 1)[0]; - Harness.clickElement(radioCheckbox, radio); - setTimeout(() => { - assert.equal(contentComp.visible, true); - Harness.clickElement(radioCheckbox, radio); - setTimeout(() => { - assert.equal(contentComp.visible, false); - done(); - }, 300); - }, 300); - }).catch((err) => done(err)); - }); + Formio.createForm(element, form) + .then((form) => { + const radioCheckbox = form.getComponent('p1'); + const contentComp = form.getComponent('p1Content'); + assert.equal(contentComp.visible, false); + const radio = Harness.testElements( + radioCheckbox, + 'input[type="radio"]', + 1, + )[0]; + Harness.clickElement(radioCheckbox, radio); + setTimeout(() => { + assert.equal(contentComp.visible, true); + Harness.clickElement(radioCheckbox, radio); + setTimeout(() => { + assert.equal(contentComp.visible, false); + done(); + }, 300); + }, 300); + }) + .catch((err) => done(err)); + }); }); diff --git a/src/components/checkbox/editForm/Checkbox.edit.data.js b/src/components/checkbox/editForm/Checkbox.edit.data.js index 37a32a08fe..86bb5e76be 100644 --- a/src/components/checkbox/editForm/Checkbox.edit.data.js +++ b/src/components/checkbox/editForm/Checkbox.edit.data.js @@ -1,6 +1,6 @@ export default [ - { - key: 'multiple', - ignore: true - }, + { + key: 'multiple', + ignore: true, + }, ]; diff --git a/src/components/checkbox/editForm/Checkbox.edit.display.js b/src/components/checkbox/editForm/Checkbox.edit.display.js index f814964c37..6cf0c6fae7 100644 --- a/src/components/checkbox/editForm/Checkbox.edit.display.js +++ b/src/components/checkbox/editForm/Checkbox.edit.display.js @@ -2,77 +2,77 @@ import BuilderUtils from '../../../utils/builder'; import _ from 'lodash'; export default [ - { - key: 'labelPosition', - ignore: true, - }, - { - key: 'labelWidth', - ignore: true, - }, - { - key: 'labelMargin', - ignore: true - }, - { - key: 'placeholder', - ignore: true, - }, - { - type: 'select', - input: true, - weight: 350, - label: 'Shortcut', - key: 'shortcut', - tooltip: 'Shortcut for this component.', - dataSrc: 'custom', - valueProperty: 'value', - customDefaultValue: () => '', - template: '{{ item.label }}', - data: { - custom(context) { - return BuilderUtils.getAvailableShortcuts( - _.get(context, 'instance.options.editForm', {}), - _.get(context, 'instance.options.editComponent', {}) - ); - }, + { + key: 'labelPosition', + ignore: true, }, - }, - { - type: 'select', - input: true, - key: 'inputType', - label: 'Input Type', - tooltip: 'This is the input type used for this checkbox.', - dataSrc: 'values', - weight: 410, - data: { - values: [ - { label: 'Checkbox', value: 'checkbox' }, - { label: 'Radio', value: 'radio' }, - ], + { + key: 'labelWidth', + ignore: true, }, - }, - { - type: 'textfield', - input: true, - key: 'name', - label: 'Radio Key', - tooltip: 'The key used to trigger the radio button toggle.', - weight: 420, - conditional: { - json: { '===': [{ var: 'data.inputType' }, 'radio'] }, + { + key: 'labelMargin', + ignore: true, }, - }, - { - type: 'textfield', - input: true, - label: 'Radio Value', - key: 'value', - tooltip: 'The value used with this radio button.', - weight: 430, - conditional: { - json: { '===': [{ var: 'data.inputType' }, 'radio'] }, + { + key: 'placeholder', + ignore: true, + }, + { + type: 'select', + input: true, + weight: 350, + label: 'Shortcut', + key: 'shortcut', + tooltip: 'Shortcut for this component.', + dataSrc: 'custom', + valueProperty: 'value', + customDefaultValue: () => '', + template: '{{ item.label }}', + data: { + custom(context) { + return BuilderUtils.getAvailableShortcuts( + _.get(context, 'instance.options.editForm', {}), + _.get(context, 'instance.options.editComponent', {}), + ); + }, + }, + }, + { + type: 'select', + input: true, + key: 'inputType', + label: 'Input Type', + tooltip: 'This is the input type used for this checkbox.', + dataSrc: 'values', + weight: 410, + data: { + values: [ + { label: 'Checkbox', value: 'checkbox' }, + { label: 'Radio', value: 'radio' }, + ], + }, + }, + { + type: 'textfield', + input: true, + key: 'name', + label: 'Radio Key', + tooltip: 'The key used to trigger the radio button toggle.', + weight: 420, + conditional: { + json: { '===': [{ var: 'data.inputType' }, 'radio'] }, + }, + }, + { + type: 'textfield', + input: true, + label: 'Radio Value', + key: 'value', + tooltip: 'The value used with this radio button.', + weight: 430, + conditional: { + json: { '===': [{ var: 'data.inputType' }, 'radio'] }, + }, }, - }, ]; diff --git a/src/components/checkbox/editForm/Checkbox.edit.validation.js b/src/components/checkbox/editForm/Checkbox.edit.validation.js index 5e1241b518..eea3569d98 100644 --- a/src/components/checkbox/editForm/Checkbox.edit.validation.js +++ b/src/components/checkbox/editForm/Checkbox.edit.validation.js @@ -1,10 +1,10 @@ export default [ - { - key: 'validateOn', - ignore: true - }, - { - key: 'unique', - ignore: true - }, + { + key: 'validateOn', + ignore: true, + }, + { + key: 'unique', + ignore: true, + }, ]; diff --git a/src/components/checkbox/fixtures/comp1.js b/src/components/checkbox/fixtures/comp1.js index 596bd8e0bb..db0878ba8b 100644 --- a/src/components/checkbox/fixtures/comp1.js +++ b/src/components/checkbox/fixtures/comp1.js @@ -1,24 +1,22 @@ export default { - 'conditional': { - 'eq': '', - 'when': null, - 'show': '' - }, - 'tags': [ - - ], - 'type': 'checkbox', - 'validate': { - 'required': false - }, - 'persistent': true, - 'protected': false, - 'defaultValue': false, - 'key': 'checkme', - 'dataGridLabel': true, - 'label': 'Check me', - 'hideLabel': false, - 'tableView': true, - 'inputType': 'checkbox', - 'input': true + conditional: { + eq: '', + when: null, + show: '', + }, + tags: [], + type: 'checkbox', + validate: { + required: false, + }, + persistent: true, + protected: false, + defaultValue: false, + key: 'checkme', + dataGridLabel: true, + label: 'Check me', + hideLabel: false, + tableView: true, + inputType: 'checkbox', + input: true, }; diff --git a/src/components/checkbox/fixtures/comp2.js b/src/components/checkbox/fixtures/comp2.js index ad10fc4f73..2b01f5c36d 100644 --- a/src/components/checkbox/fixtures/comp2.js +++ b/src/components/checkbox/fixtures/comp2.js @@ -1,11 +1,11 @@ export default { - label: 'Checkbox - Radio', - inputType: 'radio', - tableView: false, - defaultValue: false, - key: 'checkboxRadio', - type: 'checkbox', - name: 'radio', - value: 'true', - input: true, + label: 'Checkbox - Radio', + inputType: 'radio', + tableView: false, + defaultValue: false, + key: 'checkboxRadio', + type: 'checkbox', + name: 'radio', + value: 'true', + input: true, }; diff --git a/src/components/checkbox/fixtures/comp3.js b/src/components/checkbox/fixtures/comp3.js index 9fc0e289c8..35df67dbe6 100644 --- a/src/components/checkbox/fixtures/comp3.js +++ b/src/components/checkbox/fixtures/comp3.js @@ -1,14 +1,14 @@ export default { - label: 'Checkbox', - fancyButton: false, - promptOnDeselect: false, - tableView: false, - modalEdit: true, - validate: { - required: true - }, - key: 'checkbox', - type: 'checkbox', - input: true, - defaultValue: false + label: 'Checkbox', + fancyButton: false, + promptOnDeselect: false, + tableView: false, + modalEdit: true, + validate: { + required: true, + }, + key: 'checkbox', + type: 'checkbox', + input: true, + defaultValue: false, }; diff --git a/src/components/checkbox/fixtures/comp4.js b/src/components/checkbox/fixtures/comp4.js index 8b1dc8e892..89e8b4bd00 100644 --- a/src/components/checkbox/fixtures/comp4.js +++ b/src/components/checkbox/fixtures/comp4.js @@ -4,34 +4,34 @@ export default { path: '6009test', type: 'form', display: 'form', - components:[ + components: [ { label: 'P1', - inputType:'radio', - tableView:false, + inputType: 'radio', + tableView: false, key: 'p1', - type:'checkbox', - name:'priorities', - value:'p1', - input:true + type: 'checkbox', + name: 'priorities', + value: 'p1', + input: true, }, { - html:'

    Content 1

    ', - label:'P1 Content', - refreshOnChange:false, - key:'p1Content', - type:'content', - input:false, - tableView:false, - customConditional:'show = data.priorities == "p1"' + html: '

    Content 1

    ', + label: 'P1 Content', + refreshOnChange: false, + key: 'p1Content', + type: 'content', + input: false, + tableView: false, + customConditional: 'show = data.priorities == "p1"', }, { - type:'button', - label:'Submit', - key:'submit', - disableOnInvalid:true, - input:true, - tableView:false - } - ] + type: 'button', + label: 'Submit', + key: 'submit', + disableOnInvalid: true, + input: true, + tableView: false, + }, + ], }; diff --git a/src/components/checkbox/fixtures/customDefaultComponent.js b/src/components/checkbox/fixtures/customDefaultComponent.js index fd0dc2bfb6..49af7f7bd8 100644 --- a/src/components/checkbox/fixtures/customDefaultComponent.js +++ b/src/components/checkbox/fixtures/customDefaultComponent.js @@ -1,25 +1,23 @@ export default { - 'conditional': { - 'eq': '', - 'when': null, - 'show': '' - }, - 'tags': [ - - ], - 'customDefaultValue': 'value = true;', - 'type': 'checkbox', - 'validate': { - 'required': false - }, - 'persistent': true, - 'protected': false, - 'defaultValue': false, - 'key': 'checkme', - 'dataGridLabel': true, - 'label': 'Check me', - 'hideLabel': false, - 'tableView': true, - 'inputType': 'checkbox', - 'input': true + conditional: { + eq: '', + when: null, + show: '', + }, + tags: [], + customDefaultValue: 'value = true;', + type: 'checkbox', + validate: { + required: false, + }, + persistent: true, + protected: false, + defaultValue: false, + key: 'checkme', + dataGridLabel: true, + label: 'Check me', + hideLabel: false, + tableView: true, + inputType: 'checkbox', + input: true, }; diff --git a/src/components/checkbox/fixtures/values.js b/src/components/checkbox/fixtures/values.js index e34ef929c6..7b10bb7417 100644 --- a/src/components/checkbox/fixtures/values.js +++ b/src/components/checkbox/fixtures/values.js @@ -1,5 +1 @@ -export default [ - true, - false, -]; - +export default [true, false]; diff --git a/src/components/columns/Columns.form.js b/src/components/columns/Columns.form.js index 76bac20a96..ac8772bcc9 100644 --- a/src/components/columns/Columns.form.js +++ b/src/components/columns/Columns.form.js @@ -2,11 +2,14 @@ import nestedComponentForm from '../_classes/nested/NestedComponent.form'; import ColumnsEditDisplay from './editForm/Columns.edit.display'; -export default function(...extend) { - return nestedComponentForm([ - { - key: 'display', - components: ColumnsEditDisplay - }, - ], ...extend); +export default function (...extend) { + return nestedComponentForm( + [ + { + key: 'display', + components: ColumnsEditDisplay, + }, + ], + ...extend, + ); } diff --git a/src/components/columns/Columns.js b/src/components/columns/Columns.js index c18ab5da6b..f77e439678 100644 --- a/src/components/columns/Columns.js +++ b/src/components/columns/Columns.js @@ -2,179 +2,214 @@ import _ from 'lodash'; import NestedComponent from '../_classes/nested/NestedComponent'; export default class ColumnsComponent extends NestedComponent { - static schema(...extend) { - return NestedComponent.schema({ - label: 'Columns', - key: 'columns', - type: 'columns', - columns: [ - { components: [], width: 6, offset: 0, push: 0, pull: 0, size: 'md' }, - { components: [], width: 6, offset: 0, push: 0, pull: 0, size: 'md' } - ], - clearOnHide: false, - input: false, - tableView: false, - persistent: false, - autoAdjust: false - }, ...extend); - } - - static get builderInfo() { - return { - title: 'Columns', - icon: 'columns', - group: 'layout', - documentation: '/userguide/form-building/layout-components#columns', - showPreview: false, - weight: 10, - schema: ColumnsComponent.schema() - }; - } - - static savedValueTypes() { - return []; - } - - constructor(component, options, data) { - super(component, options, data); - this.rows = []; - } - - get schema() { - const schema = _.omit(super.schema, ['components']); - schema.columns?.map((column, colIndex) => { - column.components.map((comp, compIndex) => { - const clonedComp = _.clone(comp); - clonedComp.internal = true; - const component = this.createComponent(clonedComp); - delete component.component.internal; - schema.columns[colIndex].components[compIndex] = component.schema; - }); - }); - return schema; - } - - get defaultSchema() { - return ColumnsComponent.schema(); - } - - get className() { - return `row ${super.className}`; - } - - get columnKey() { - return `column-${this.id}`; - } - - init() { - super.init(); - this.columns = []; - _.each(this.component.columns, (column, index) => { - this.columns[index] = []; - if (!column.size) { - column.size = 'md'; - } - column.currentWidth = this.options.condensedMode ? this.gridSize : column.width || 0; - // Ensure there is a components array. - if (!Array.isArray(column.components)) { - column.components = []; - } - _.each(column.components, (comp) => { - const component = this.createComponent(comp); - component.column = index; - this.columns[index].push(component); - }); - }); - if (this.component.autoAdjust && this.options.display !== 'pdf') { - this.justify(); - } - this.rows = this.groupByRow(); - } - - labelIsHidden() { - return true; - } - - render() { - return super.render(this.renderTemplate('columns', { - columnKey: this.columnKey, - columnComponents: this.columns.map(column => this.renderComponents(column)) - })); - } - - justifyColumn(items, index) { - const toAdjust = _.every(items, item => !item.visible); - const column = this.component.columns[index]; - const width = (toAdjust && items.length) ? 0 : column.width; - const shouldRedraw = !_.isEqual(width, column.currentWidth); - - column.currentWidth = width; - - return shouldRedraw; - } - - justify() { - return this.columns.reduce((redraw, items, index) => this.justifyColumn(items, index) || redraw, false); - } - - attach(element) { - this.loadRefs(element, { [this.columnKey]: 'multiple' }); - const superAttach = super.attach(element); - if (this.refs[this.columnKey]) { - this.refs[this.columnKey].forEach((column, index) => - this.attachComponents(column, this.columns[index], this.component.columns[index].components) - ); - } - return superAttach; - } - - get gridSize() { - return 12; - } - - /** - * Group columns in rows. - * @return {Array.} - */ - groupByRow() { - const initVal = { stack: [], rows: [] }; - const width = x => x.component.width; - const result = _.reduce(this.components, (acc, next) => { - const stack = [...acc.stack, next]; - if (_.sumBy(stack, width) <= this.gridSize) { - acc.stack = stack; - return acc; - } - else { - acc.rows = [...acc.rows, acc.stack]; - acc.stack = [next]; - return acc; - } - }, initVal); - - return _.concat(result.rows, [result.stack]); - } - - checkData(data, flags, row, components) { - const isValid = super.checkData(data, flags, row, components); - - if (this.component.autoAdjust && this.options.display !== 'pdf') { - const redraw = this.justify(); - - if (redraw) { - this.redraw(); - } - } - - return isValid; - } - - detach(all) { - super.detach(all); - } - - destroy(all = false) { - super.destroy(all); - this.columns = []; - } + static schema(...extend) { + return NestedComponent.schema( + { + label: 'Columns', + key: 'columns', + type: 'columns', + columns: [ + { + components: [], + width: 6, + offset: 0, + push: 0, + pull: 0, + size: 'md', + }, + { + components: [], + width: 6, + offset: 0, + push: 0, + pull: 0, + size: 'md', + }, + ], + clearOnHide: false, + input: false, + tableView: false, + persistent: false, + autoAdjust: false, + }, + ...extend, + ); + } + + static get builderInfo() { + return { + title: 'Columns', + icon: 'columns', + group: 'layout', + documentation: '/userguide/form-building/layout-components#columns', + showPreview: false, + weight: 10, + schema: ColumnsComponent.schema(), + }; + } + + static savedValueTypes() { + return []; + } + + constructor(component, options, data) { + super(component, options, data); + this.rows = []; + } + + get schema() { + const schema = _.omit(super.schema, ['components']); + schema.columns?.map((column, colIndex) => { + column.components.map((comp, compIndex) => { + const clonedComp = _.clone(comp); + clonedComp.internal = true; + const component = this.createComponent(clonedComp); + delete component.component.internal; + schema.columns[colIndex].components[compIndex] = + component.schema; + }); + }); + return schema; + } + + get defaultSchema() { + return ColumnsComponent.schema(); + } + + get className() { + return `row ${super.className}`; + } + + get columnKey() { + return `column-${this.id}`; + } + + init() { + super.init(); + this.columns = []; + _.each(this.component.columns, (column, index) => { + this.columns[index] = []; + if (!column.size) { + column.size = 'md'; + } + column.currentWidth = this.options.condensedMode + ? this.gridSize + : column.width || 0; + // Ensure there is a components array. + if (!Array.isArray(column.components)) { + column.components = []; + } + _.each(column.components, (comp) => { + const component = this.createComponent(comp); + component.column = index; + this.columns[index].push(component); + }); + }); + if (this.component.autoAdjust && this.options.display !== 'pdf') { + this.justify(); + } + this.rows = this.groupByRow(); + } + + labelIsHidden() { + return true; + } + + render() { + return super.render( + this.renderTemplate('columns', { + columnKey: this.columnKey, + columnComponents: this.columns.map((column) => + this.renderComponents(column), + ), + }), + ); + } + + justifyColumn(items, index) { + const toAdjust = _.every(items, (item) => !item.visible); + const column = this.component.columns[index]; + const width = toAdjust && items.length ? 0 : column.width; + const shouldRedraw = !_.isEqual(width, column.currentWidth); + + column.currentWidth = width; + + return shouldRedraw; + } + + justify() { + return this.columns.reduce( + (redraw, items, index) => + this.justifyColumn(items, index) || redraw, + false, + ); + } + + attach(element) { + this.loadRefs(element, { [this.columnKey]: 'multiple' }); + const superAttach = super.attach(element); + if (this.refs[this.columnKey]) { + this.refs[this.columnKey].forEach((column, index) => + this.attachComponents( + column, + this.columns[index], + this.component.columns[index].components, + ), + ); + } + return superAttach; + } + + get gridSize() { + return 12; + } + + /** + * Group columns in rows. + * @return {Array.} + */ + groupByRow() { + const initVal = { stack: [], rows: [] }; + const width = (x) => x.component.width; + const result = _.reduce( + this.components, + (acc, next) => { + const stack = [...acc.stack, next]; + if (_.sumBy(stack, width) <= this.gridSize) { + acc.stack = stack; + return acc; + } else { + acc.rows = [...acc.rows, acc.stack]; + acc.stack = [next]; + return acc; + } + }, + initVal, + ); + + return _.concat(result.rows, [result.stack]); + } + + checkData(data, flags, row, components) { + const isValid = super.checkData(data, flags, row, components); + + if (this.component.autoAdjust && this.options.display !== 'pdf') { + const redraw = this.justify(); + + if (redraw) { + this.redraw(); + } + } + + return isValid; + } + + detach(all) { + super.detach(all); + } + + destroy(all = false) { + super.destroy(all); + this.columns = []; + } } diff --git a/src/components/columns/Columns.unit.js b/src/components/columns/Columns.unit.js index d5c1d5e9dd..5d9f27f8ce 100644 --- a/src/components/columns/Columns.unit.js +++ b/src/components/columns/Columns.unit.js @@ -1,95 +1,136 @@ import Harness from '../../../test/harness'; import ColumnsComponent from './Columns'; import assert from 'power-assert'; -import { - comp1, - comp2, - comp3 -} from './fixtures'; +import { comp1, comp2, comp3 } from './fixtures'; -describe('Columns Component', function() { - it('Should build a columns component', function() { - return Harness.testCreate(ColumnsComponent, comp1).then((component) => { - Harness.testElements(component, 'input[type="text"]', 2); +describe('Columns Component', function () { + it('Should build a columns component', function () { + return Harness.testCreate(ColumnsComponent, comp1).then((component) => { + Harness.testElements(component, 'input[type="text"]', 2); + }); }); - }); - it('Should be auto-adjusting when auto adjust is set to true', function(done) { - Harness.testCreate(ColumnsComponent, comp2) - .then((component) => { - const columns = component.component.columns; - assert.equal(columns.every(col => col.currentWidth === 2), true); - const oddIndexes = [0, 2, 4]; // 0 column has 2 textfields - oddIndexes.forEach(i => columns[i].components[0].hidden = true); // we're setting hidden for odd columns - // initially all components aren't hidden and have default width = 2 - component.rebuild(); // rebuild component to check conditions + it('Should be auto-adjusting when auto adjust is set to true', function (done) { + Harness.testCreate(ColumnsComponent, comp2) + .then((component) => { + const columns = component.component.columns; + assert.equal( + columns.every((col) => col.currentWidth === 2), + true, + ); + const oddIndexes = [0, 2, 4]; // 0 column has 2 textfields + oddIndexes.forEach( + (i) => (columns[i].components[0].hidden = true), + ); // we're setting hidden for odd columns + // initially all components aren't hidden and have default width = 2 + component.rebuild(); // rebuild component to check conditions - oddIndexes.forEach(i => { - if (i === 0) { - assert.equal(columns[i].currentWidth, 2, `column[${i}] should have width = 2`); // it has at least a component as visible - } - else { - assert.equal(columns[i].currentWidth, 0, `column[${i}] should have width = 0`); // columns which has no visible components should have currentWidth as 0 - } - }); + oddIndexes.forEach((i) => { + if (i === 0) { + assert.equal( + columns[i].currentWidth, + 2, + `column[${i}] should have width = 2`, + ); // it has at least a component as visible + } else { + assert.equal( + columns[i].currentWidth, + 0, + `column[${i}] should have width = 0`, + ); // columns which has no visible components should have currentWidth as 0 + } + }); - oddIndexes.forEach(i => columns[i].components[0].hidden = false); // we're setting visible for odd columns again - component.rebuild(); // rebuild component to check conditions - assert.equal(columns.every(col => col.currentWidth === 2), true, 'all columns should have width = 2'); // ensure we have initial width - }) - .then(done) - .catch(done); - }); + oddIndexes.forEach( + (i) => (columns[i].components[0].hidden = false), + ); // we're setting visible for odd columns again + component.rebuild(); // rebuild component to check conditions + assert.equal( + columns.every((col) => col.currentWidth === 2), + true, + 'all columns should have width = 2', + ); // ensure we have initial width + }) + .then(done) + .catch(done); + }); - it('Should clear fields in modal window after confirming to clear data in dialog window', function(done) { - Harness.testCreate(ColumnsComponent, comp3).then((component) => { - const hiddenModalWindow = component.element.querySelector('.component-rendering-hidden'); - assert.equal(!!hiddenModalWindow, true); + it('Should clear fields in modal window after confirming to clear data in dialog window', function (done) { + Harness.testCreate(ColumnsComponent, comp3) + .then((component) => { + const hiddenModalWindow = component.element.querySelector( + '.component-rendering-hidden', + ); + assert.equal(!!hiddenModalWindow, true); - const clickEvent = new Event('click'); - const openModalElement = component.refs.openModal; - //open modal edit window - openModalElement.dispatchEvent(clickEvent); + const clickEvent = new Event('click'); + const openModalElement = component.refs.openModal; + //open modal edit window + openModalElement.dispatchEvent(clickEvent); - setTimeout(() => { - assert.equal(!!component.element.querySelector('.component-rendering-hidden'), false); + setTimeout(() => { + assert.equal( + !!component.element.querySelector( + '.component-rendering-hidden', + ), + false, + ); - const inputEvent = new Event('input'); - const columnsInputField = component.element.querySelector('[name="data[textField]"]'); + const inputEvent = new Event('input'); + const columnsInputField = component.element.querySelector( + '[name="data[textField]"]', + ); - columnsInputField.value = 'alexy@form.io'; - columnsInputField.dispatchEvent(inputEvent); + columnsInputField.value = 'alexy@form.io'; + columnsInputField.dispatchEvent(inputEvent); - setTimeout(() => { - assert.equal(component.element.querySelector('[name="data[textField]"]').value, 'alexy@form.io'); + setTimeout(() => { + assert.equal( + component.element.querySelector( + '[name="data[textField]"]', + ).value, + 'alexy@form.io', + ); - const clickEvent = new Event('click'); - const modalOverlay = component.refs.modalOverlay; - //click outside modal edit window - modalOverlay.dispatchEvent(clickEvent); + const clickEvent = new Event('click'); + const modalOverlay = component.refs.modalOverlay; + //click outside modal edit window + modalOverlay.dispatchEvent(clickEvent); - setTimeout(() => { - assert.equal(!!component.componentModal.dialog, true); + setTimeout(() => { + assert.equal( + !!component.componentModal.dialog, + true, + ); - const clickEvent = new Event('click'); - const btnForCleaningValues = document.querySelector('[ref="dialogYesButton"]'); - //click on 'yes, delete it' button inside alert window - btnForCleaningValues.dispatchEvent(clickEvent); + const clickEvent = new Event('click'); + const btnForCleaningValues = document.querySelector( + '[ref="dialogYesButton"]', + ); + //click on 'yes, delete it' button inside alert window + btnForCleaningValues.dispatchEvent(clickEvent); - setTimeout(() => { - const clickEvent = new Event('click'); - const openModalElement = component.refs.openModal; - //open edit modal window again - openModalElement.dispatchEvent(clickEvent); + setTimeout(() => { + const clickEvent = new Event('click'); + const openModalElement = + component.refs.openModal; + //open edit modal window again + openModalElement.dispatchEvent(clickEvent); - setTimeout(() => { - assert.equal(component.element.querySelector('[name="data[textField]"]').value, ''); - done(); - }, 100); - }, 100); - }, 100); - }, 100); - }, 100); - }).catch(done); - }); + setTimeout(() => { + assert.equal( + component.element.querySelector( + '[name="data[textField]"]', + ).value, + '', + ); + done(); + }, 100); + }, 100); + }, 100); + }, 100); + }, 100); + }) + .catch(done); + }); }); diff --git a/src/components/columns/editForm/Columns.edit.display.js b/src/components/columns/editForm/Columns.edit.display.js index 1c6afdccfd..c6f35171fa 100644 --- a/src/components/columns/editForm/Columns.edit.display.js +++ b/src/components/columns/editForm/Columns.edit.display.js @@ -1,102 +1,104 @@ export default [ - { - key: 'labelPosition', - ignore: true - }, - { - key: 'placeholder', - ignore: true - }, - { - key: 'description', - ignore: true - }, - { - key: 'tooltip', - ignore: true - }, - { - key: 'autofocus', - ignore: true - }, - { - key: 'tabindex', - ignore: true - }, - { - key: 'disabled', - ignore: true - }, - { - key: 'tableView', - ignore: true - }, - { - key: 'hideLabel', - ignore: true - }, - { - weight: 0, - type: 'textfield', - input: true, - key: 'label', - label: 'Label', - placeholder: 'Field Label', - tooltip: 'The label for this field.', - validate: { - required: true + { + key: 'labelPosition', + ignore: true, }, - autofocus: true, - overrideEditForm: true - }, - { - weight: 150, - type: 'datagrid', - input: true, - key: 'columns', - label: 'Column Properties', - addAnother: 'Add Column', - tooltip: 'The size and width settings for each column. One row is equal to 12. (e.g., a row with two columns spanning the entire page should be 6 and 6)', - reorder: true, - components: [ - { - type: 'hidden', - key: 'components', - defaultValue: [] - }, - { - type: 'select', - key: 'size', - defaultValue: 'md', - label: 'Size', - data: { - values: [ - { label: 'xs', value: 'xs' }, - { label: 'sm', value: 'sm' }, - { label: 'md', value: 'md' }, - { label: 'lg', value: 'lg' }, - { label: 'xl', value: 'xl' }, - ], + { + key: 'placeholder', + ignore: true, + }, + { + key: 'description', + ignore: true, + }, + { + key: 'tooltip', + ignore: true, + }, + { + key: 'autofocus', + ignore: true, + }, + { + key: 'tabindex', + ignore: true, + }, + { + key: 'disabled', + ignore: true, + }, + { + key: 'tableView', + ignore: true, + }, + { + key: 'hideLabel', + ignore: true, + }, + { + weight: 0, + type: 'textfield', + input: true, + key: 'label', + label: 'Label', + placeholder: 'Field Label', + tooltip: 'The label for this field.', + validate: { + required: true, }, - }, - { - type: 'number', - key: 'width', - defaultValue: 6, - label: 'Width' - } - ] - }, - { - weight: 160, - type: 'checkbox', - label: 'Auto adjust columns', - tooltip: 'Will automatically adjust columns based on if nested components are hidden.', - key: 'autoAdjust', - input: true - }, - { - key: 'hideLabel', - ignore: true - }, + autofocus: true, + overrideEditForm: true, + }, + { + weight: 150, + type: 'datagrid', + input: true, + key: 'columns', + label: 'Column Properties', + addAnother: 'Add Column', + tooltip: + 'The size and width settings for each column. One row is equal to 12. (e.g., a row with two columns spanning the entire page should be 6 and 6)', + reorder: true, + components: [ + { + type: 'hidden', + key: 'components', + defaultValue: [], + }, + { + type: 'select', + key: 'size', + defaultValue: 'md', + label: 'Size', + data: { + values: [ + { label: 'xs', value: 'xs' }, + { label: 'sm', value: 'sm' }, + { label: 'md', value: 'md' }, + { label: 'lg', value: 'lg' }, + { label: 'xl', value: 'xl' }, + ], + }, + }, + { + type: 'number', + key: 'width', + defaultValue: 6, + label: 'Width', + }, + ], + }, + { + weight: 160, + type: 'checkbox', + label: 'Auto adjust columns', + tooltip: + 'Will automatically adjust columns based on if nested components are hidden.', + key: 'autoAdjust', + input: true, + }, + { + key: 'hideLabel', + ignore: true, + }, ]; diff --git a/src/components/columns/fixtures/comp1.js b/src/components/columns/fixtures/comp1.js index 8d7c10bd84..7084c36596 100644 --- a/src/components/columns/fixtures/comp1.js +++ b/src/components/columns/fixtures/comp1.js @@ -1,89 +1,83 @@ export default { - 'conditional': { - 'eq': '', - 'when': null, - 'show': '' - }, - 'tags': [ - - ], - 'type': 'columns', - 'columns': [ - { - 'components': [ - { - 'tags': [ - - ], - 'type': 'textfield', - 'conditional': { - 'eq': '', - 'when': null, - 'show': '' - }, - 'validate': { - 'customPrivate': false, - 'custom': '', - 'pattern': '', - 'maxLength': '', - 'minLength': '', - 'required': false - }, - 'persistent': true, - 'unique': false, - 'protected': false, - 'defaultValue': '', - 'multiple': false, - 'suffix': '', - 'prefix': '', - 'placeholder': '', - 'key': 'firstName', - 'label': 'First Name', - 'inputMask': '', - 'inputType': 'text', - 'tableView': true, - 'input': true - } - ] + conditional: { + eq: '', + when: null, + show: '', }, - { - 'components': [ + tags: [], + type: 'columns', + columns: [ + { + components: [ + { + tags: [], + type: 'textfield', + conditional: { + eq: '', + when: null, + show: '', + }, + validate: { + customPrivate: false, + custom: '', + pattern: '', + maxLength: '', + minLength: '', + required: false, + }, + persistent: true, + unique: false, + protected: false, + defaultValue: '', + multiple: false, + suffix: '', + prefix: '', + placeholder: '', + key: 'firstName', + label: 'First Name', + inputMask: '', + inputType: 'text', + tableView: true, + input: true, + }, + ], + }, { - 'tags': [ - - ], - 'type': 'textfield', - 'conditional': { - 'eq': '', - 'when': null, - 'show': '' - }, - 'validate': { - 'customPrivate': false, - 'custom': '', - 'pattern': '', - 'maxLength': '', - 'minLength': '', - 'required': false - }, - 'persistent': true, - 'unique': false, - 'protected': false, - 'defaultValue': '', - 'multiple': false, - 'suffix': '', - 'prefix': '', - 'placeholder': '', - 'key': 'lastName', - 'label': 'Last Name', - 'inputMask': '', - 'inputType': 'text', - 'tableView': true, - 'input': true - } - ] - } - ], - 'key': 'columns1', - 'input': false + components: [ + { + tags: [], + type: 'textfield', + conditional: { + eq: '', + when: null, + show: '', + }, + validate: { + customPrivate: false, + custom: '', + pattern: '', + maxLength: '', + minLength: '', + required: false, + }, + persistent: true, + unique: false, + protected: false, + defaultValue: '', + multiple: false, + suffix: '', + prefix: '', + placeholder: '', + key: 'lastName', + label: 'Last Name', + inputMask: '', + inputType: 'text', + tableView: true, + input: true, + }, + ], + }, + ], + key: 'columns1', + input: false, }; diff --git a/src/components/columns/fixtures/comp2.js b/src/components/columns/fixtures/comp2.js index c1fa1fb983..e6a63ce553 100644 --- a/src/components/columns/fixtures/comp2.js +++ b/src/components/columns/fixtures/comp2.js @@ -1,125 +1,125 @@ export default { - label: 'Columns', - columns: [ - // First Column - { - components: [ + label: 'Columns', + columns: [ + // First Column { - label: 'Text Field', - tableView: true, - key: 'textField1', - type: 'textfield', - input: true + components: [ + { + label: 'Text Field', + tableView: true, + key: 'textField1', + type: 'textfield', + input: true, + }, + { + label: 'Text Field', + tableView: true, + key: 'textField2', + type: 'textfield', + input: true, + }, + ], + width: 2, + offset: 0, + push: 0, + pull: 0, + size: 'md', + currentWidth: 2, }, + // Second Column with hidden components { - label: 'Text Field', - tableView: true, - key: 'textField2', - type: 'textfield', - input: true + components: [ + { + label: 'Text Field', + tableView: true, + key: 'textField3', + type: 'textfield', + input: true, + }, + ], + width: 2, + offset: 0, + push: 0, + pull: 0, + size: 'md', + currentWidth: 2, }, - ], - width: 2, - offset: 0, - push: 0, - pull: 0, - size: 'md', - currentWidth: 2, - }, - // Second Column with hidden components - { - components: [ + // Third Column { - label: 'Text Field', - tableView: true, - key: 'textField3', - type: 'textfield', - input: true + components: [ + { + label: 'Text Field', + tableView: true, + key: 'textField4', + type: 'textfield', + input: true, + }, + ], + size: 'md', + width: 2, + offset: 0, + push: 0, + pull: 0, + currentWidth: 2, }, - ], - width: 2, - offset: 0, - push: 0, - pull: 0, - size: 'md', - currentWidth: 2, - }, - // Third Column - { - components: [ + // Fourth Column { - label: 'Text Field', - tableView: true, - key: 'textField4', - type: 'textfield', - input: true + components: [ + { + label: 'Text Field', + tableView: true, + key: 'textField5', + type: 'textfield', + input: true, + }, + ], + size: 'md', + width: 2, + offset: 0, + push: 0, + pull: 0, + currentWidth: 2, }, - ], - size: 'md', - width: 2, - offset: 0, - push: 0, - pull: 0, - currentWidth: 2, - }, - // Fourth Column - { - components: [ + // Fifth Column { - label: 'Text Field', - tableView: true, - key: 'textField5', - type: 'textfield', - input: true + components: [ + { + label: 'Text Field', + tableView: true, + key: 'textField6', + type: 'textfield', + input: true, + }, + ], + size: 'md', + width: 2, + offset: 0, + push: 0, + pull: 0, + currentWidth: 2, }, - ], - size: 'md', - width: 2, - offset: 0, - push: 0, - pull: 0, - currentWidth: 2, - }, - // Fifth Column - { - components: [ + // Sixth Column { - label: 'Text Field', - tableView: true, - key: 'textField6', - type: 'textfield', - input: true + components: [ + { + label: 'Text Field', + tableView: true, + key: 'textField7', + type: 'textfield', + input: true, + }, + ], + size: 'md', + width: 2, + offset: 0, + push: 0, + pull: 0, + currentWidth: 2, }, - ], - size: 'md', - width: 2, - offset: 0, - push: 0, - pull: 0, - currentWidth: 2, - }, - // Sixth Column - { - components: [ - { - label: 'Text Field', - tableView: true, - key: 'textField7', - type: 'textfield', - input: true - }, - ], - size: 'md', - width: 2, - offset: 0, - push: 0, - pull: 0, - currentWidth: 2, - }, - ], - autoAdjust: true, - key: 'columns1', - type: 'columns', - input: false, - tableView: false, + ], + autoAdjust: true, + key: 'columns1', + type: 'columns', + input: false, + tableView: false, }; diff --git a/src/components/columns/fixtures/comp3.js b/src/components/columns/fixtures/comp3.js index 5ad3d7f498..26f53a1987 100644 --- a/src/components/columns/fixtures/comp3.js +++ b/src/components/columns/fixtures/comp3.js @@ -1,51 +1,51 @@ export default { - label: 'Columns', - columns: [ - { - components: [ + label: 'Columns', + columns: [ { - label: 'Text Field', - tableView: true, - key: 'textField', - type: 'textfield', - input: true, - hideOnChildrenHidden: false, + components: [ + { + label: 'Text Field', + tableView: true, + key: 'textField', + type: 'textfield', + input: true, + hideOnChildrenHidden: false, + }, + ], + width: 6, + offset: 0, + push: 0, + pull: 0, + size: 'md', + currentWidth: 6, }, - ], - width: 6, - offset: 0, - push: 0, - pull: 0, - size: 'md', - currentWidth: 6, - }, - { - components: [ { - label: 'Number', - mask: false, - tableView: false, - delimiter: false, - requireDecimal: false, - inputFormat: 'plain', - truncateMultipleSpaces: false, - key: 'number', - type: 'number', - input: true, - hideOnChildrenHidden: false, + components: [ + { + label: 'Number', + mask: false, + tableView: false, + delimiter: false, + requireDecimal: false, + inputFormat: 'plain', + truncateMultipleSpaces: false, + key: 'number', + type: 'number', + input: true, + hideOnChildrenHidden: false, + }, + ], + width: 6, + offset: 0, + push: 0, + pull: 0, + size: 'md', + currentWidth: 6, }, - ], - width: 6, - offset: 0, - push: 0, - pull: 0, - size: 'md', - currentWidth: 6, - }, - ], - modalEdit: true, - key: 'columns', - type: 'columns', - input: false, - tableView: false, + ], + modalEdit: true, + key: 'columns', + type: 'columns', + input: false, + tableView: false, }; diff --git a/src/components/container/Container.form.js b/src/components/container/Container.form.js index 75755ed07f..92147b0421 100644 --- a/src/components/container/Container.form.js +++ b/src/components/container/Container.form.js @@ -2,15 +2,18 @@ import Components from '../Components'; import ContainerEditDisplay from './editForm/Container.edit.display'; import ContainerEditData from './editForm/Container.edit.data'; -export default function(...extend) { - return Components.baseEditForm([ - { - key: 'display', - components: ContainerEditDisplay - }, - { - key: 'data', - components: ContainerEditData - }, - ], ...extend); +export default function (...extend) { + return Components.baseEditForm( + [ + { + key: 'display', + components: ContainerEditDisplay, + }, + { + key: 'data', + components: ContainerEditData, + }, + ], + ...extend, + ); } diff --git a/src/components/container/Container.js b/src/components/container/Container.js index 3c49b2e3f7..79b8ff22de 100644 --- a/src/components/container/Container.js +++ b/src/components/container/Container.js @@ -1,91 +1,117 @@ import _ from 'lodash'; -import { componentValueTypes, getComponentSavedTypes, getFocusableElements } from '../../utils/utils'; +import { + componentValueTypes, + getComponentSavedTypes, + getFocusableElements, +} from '../../utils/utils'; import Component from '../_classes/component/Component'; import Field from '../_classes/field/Field'; import NestedDataComponent from '../_classes/nesteddata/NestedDataComponent'; export default class ContainerComponent extends NestedDataComponent { - static schema(...extend) { - return NestedDataComponent.schema({ - label: 'Container', - type: 'container', - key: 'container', - clearOnHide: true, - input: true, - tree: true, - hideLabel: true, - components: [] - }, ...extend); - } + static schema(...extend) { + return NestedDataComponent.schema( + { + label: 'Container', + type: 'container', + key: 'container', + clearOnHide: true, + input: true, + tree: true, + hideLabel: true, + components: [], + }, + ...extend, + ); + } - static get builderInfo() { - return { - title: 'Container', - icon: 'folder-open', - group: 'data', - documentation: '/userguide/form-building/data-components#container', - showPreview: false, - weight: 10, - schema: ContainerComponent.schema() - }; - } + static get builderInfo() { + return { + title: 'Container', + icon: 'folder-open', + group: 'data', + documentation: '/userguide/form-building/data-components#container', + showPreview: false, + weight: 10, + schema: ContainerComponent.schema(), + }; + } - constructor(...args) { - super(...args); - this.type = 'container'; - } + constructor(...args) { + super(...args); + this.type = 'container'; + } - static savedValueTypes(schema) { - return getComponentSavedTypes(schema) || [componentValueTypes.object]; - } + static savedValueTypes(schema) { + return getComponentSavedTypes(schema) || [componentValueTypes.object]; + } - addComponents(data, options) { - return super.addComponents(this.dataValue, options); - } + addComponents(data, options) { + return super.addComponents(this.dataValue, options); + } - get defaultSchema() { - return ContainerComponent.schema(); - } + get defaultSchema() { + return ContainerComponent.schema(); + } - get emptyValue() { - return {}; - } + get emptyValue() { + return {}; + } - get templateName() { - return 'container'; - } + get templateName() { + return 'container'; + } - componentContext() { - return this.dataValue; - } + componentContext() { + return this.dataValue; + } - checkData(data, flags, row, components) { - data = data || this.rootValue; - flags = flags || {}; - row = row || this.data; - components = components && _.isArray(components) ? components : this.getComponents(); + checkData(data, flags, row, components) { + data = data || this.rootValue; + flags = flags || {}; + row = row || this.data; + components = + components && _.isArray(components) + ? components + : this.getComponents(); - return components.reduce((valid, comp) => { - return comp.checkData(data, flags, this.dataValue) && valid; - }, Component.prototype.checkData.call(this, data, flags, row)); - } + return components.reduce( + (valid, comp) => { + return comp.checkData(data, flags, this.dataValue) && valid; + }, + Component.prototype.checkData.call(this, data, flags, row), + ); + } - checkChildComponentsValidity(data, dirty, row, silentCheck, isParentValid) { - return super.checkChildComponentsValidity(data, dirty, this.dataValue, silentCheck, isParentValid); - } + checkChildComponentsValidity(data, dirty, row, silentCheck, isParentValid) { + return super.checkChildComponentsValidity( + data, + dirty, + this.dataValue, + silentCheck, + isParentValid, + ); + } - focus() { - const focusableElements = getFocusableElements(this.element); - if (focusableElements && focusableElements[0]) { - focusableElements[0].focus(); - } - } + focus() { + const focusableElements = getFocusableElements(this.element); + if (focusableElements && focusableElements[0]) { + focusableElements[0].focus(); + } + } - checkConditions(data, flags, row) { - // check conditions of parent component first, because it may influence on visibility of it's children - const check = Field.prototype.checkConditions.call(this, data, flags, row); - this.getComponents().forEach(comp => comp.checkConditions(data, flags, this.dataValue)); - return check; - } + checkConditions(data, flags, row) { + // check conditions of parent component first, because it may influence on visibility of it's children + const check = Field.prototype.checkConditions.call( + this, + data, + flags, + row, + ); + this.getComponents().forEach((comp) => + comp.checkConditions(data, flags, this.dataValue), + ); + return check; + } } diff --git a/src/components/container/Container.unit.js b/src/components/container/Container.unit.js index 3831d3a45f..d749ae5f42 100644 --- a/src/components/container/Container.unit.js +++ b/src/components/container/Container.unit.js @@ -3,98 +3,122 @@ import _ from 'lodash'; import Harness from '../../../test/harness'; import ContainerComponent from './Container'; -import { - comp1, - comp2, - comp3, - comp4, -} from './fixtures'; +import { comp1, comp2, comp3, comp4 } from './fixtures'; import { Formio } from '../../Formio'; -describe('Container Component', function() { - it('Should build a container component', function() { - return Harness.testCreate(ContainerComponent, comp1).then((component) => { - const inputs = Harness.testElements(component, 'input[type="text"]', 2); - for (let i=0; i < inputs.length; i++) { - assert.equal(inputs[i].name, `data[${comp1.key}][${comp1.components[i].key}]`); - } +describe('Container Component', function () { + it('Should build a container component', function () { + return Harness.testCreate(ContainerComponent, comp1).then( + (component) => { + const inputs = Harness.testElements( + component, + 'input[type="text"]', + 2, + ); + for (let i = 0; i < inputs.length; i++) { + assert.equal( + inputs[i].name, + `data[${comp1.key}][${comp1.components[i].key}]`, + ); + } + }, + ); }); - }); - it('Should be able to set and get data', function() { - return Harness.testCreate(ContainerComponent, comp1).then((component) => { - const inputs = Harness.testElements(component, 'input[type="text"]', 2); - Harness.testSetGet(component, { - firstName: 'Joe', - lastName: 'Smith' - }); - assert.equal(inputs[0].value, 'Joe'); - assert.equal(inputs[1].value, 'Smith'); + it('Should be able to set and get data', function () { + return Harness.testCreate(ContainerComponent, comp1).then( + (component) => { + const inputs = Harness.testElements( + component, + 'input[type="text"]', + 2, + ); + Harness.testSetGet(component, { + firstName: 'Joe', + lastName: 'Smith', + }); + assert.equal(inputs[0].value, 'Joe'); + assert.equal(inputs[1].value, 'Smith'); + }, + ); }); - }); - it('Should set the dataValue, but after it sets the value of its nested components', function() { - return Harness.testCreate(ContainerComponent, comp2).then((component) => { - const editGrid = component.getComponent('children'); - const setValue = editGrid.setValue; - editGrid.setValue = function(...args) { - const changed = setValue.call(editGrid, ...args); - assert(changed, 'The edit grid must have changed'); - return changed; - }; - component.setValue({ - children: [ - { - name: 'Joe' - }, - { - name: 'Sally' - } - ] - }); + it('Should set the dataValue, but after it sets the value of its nested components', function () { + return Harness.testCreate(ContainerComponent, comp2).then( + (component) => { + const editGrid = component.getComponent('children'); + const setValue = editGrid.setValue; + editGrid.setValue = function (...args) { + const changed = setValue.call(editGrid, ...args); + assert(changed, 'The edit grid must have changed'); + return changed; + }; + component.setValue({ + children: [ + { + name: 'Joe', + }, + { + name: 'Sally', + }, + ], + }); + }, + ); }); - }); - it('Should render form with a submission in a draft-state without validation errors', function(done) { - const form = _.cloneDeep(comp3); - const element = document.createElement('div'); + it('Should render form with a submission in a draft-state without validation errors', function (done) { + const form = _.cloneDeep(comp3); + const element = document.createElement('div'); - Formio.createForm(element, form).then(form => { - form.submission = { - data: { - 'container': { - 'textField': 'a', - } - } - }; + Formio.createForm(element, form) + .then((form) => { + form.submission = { + data: { + container: { + textField: 'a', + }, + }, + }; - setTimeout(() => { - const textField = form.getComponent(['textField']); - const container = form.getComponent(['container']); - assert.equal(textField.errors.length, 0); - assert.equal(container.errors.length, 0); - done(); - }, 100); - }).catch(done); - }); + setTimeout(() => { + const textField = form.getComponent(['textField']); + const container = form.getComponent(['container']); + assert.equal(textField.errors.length, 0); + assert.equal(container.errors.length, 0); + done(); + }, 100); + }) + .catch(done); + }); - it('Should not set the default value when clearOnHide during the server-side validation', function(done) { - const form = _.cloneDeep(comp4); - const element = document.createElement('div'); + it('Should not set the default value when clearOnHide during the server-side validation', function (done) { + const form = _.cloneDeep(comp4); + const element = document.createElement('div'); - Formio.createForm(element, form, { server: true, noDefaults: true }).then(form => { - form.setValue({ data: { checkbox: false } }, { - sanitize: true, - }, true); + Formio.createForm(element, form, { server: true, noDefaults: true }) + .then((form) => { + form.setValue( + { data: { checkbox: false } }, + { + sanitize: true, + }, + true, + ); - form.checkConditions(); - form.clearOnHide(); + form.checkConditions(); + form.clearOnHide(); - setTimeout(() => { - assert.deepEqual(form._data, { checkbox: false }, 'Should not add Container\'s key'); - done(); - }, 200); - }).catch(done); - }); + setTimeout(() => { + assert.deepEqual( + form._data, + { checkbox: false }, + "Should not add Container's key", + ); + done(); + }, 200); + }) + .catch(done); + }); }); diff --git a/src/components/container/editForm/Container.edit.data.js b/src/components/container/editForm/Container.edit.data.js index 9aa6fd8d21..ec460417e5 100644 --- a/src/components/container/editForm/Container.edit.data.js +++ b/src/components/container/editForm/Container.edit.data.js @@ -1,14 +1,14 @@ export default [ - { - key: 'multiple', - ignore: true - }, - { - key: 'allowCalculateOverride', - ignore: true - }, - { - key: 'defaultValue', - ignore: true - }, + { + key: 'multiple', + ignore: true, + }, + { + key: 'allowCalculateOverride', + ignore: true, + }, + { + key: 'defaultValue', + ignore: true, + }, ]; diff --git a/src/components/container/editForm/Container.edit.display.js b/src/components/container/editForm/Container.edit.display.js index e3656fe2c2..b015131159 100644 --- a/src/components/container/editForm/Container.edit.display.js +++ b/src/components/container/editForm/Container.edit.display.js @@ -1,18 +1,18 @@ export default [ - { - key: 'placeholder', - ignore: true - }, - { - key: 'description', - ignore: true - }, - { - key: 'autofocus', - ignore: true - }, - { - key: 'tabindex', - ignore: true - }, + { + key: 'placeholder', + ignore: true, + }, + { + key: 'description', + ignore: true, + }, + { + key: 'autofocus', + ignore: true, + }, + { + key: 'tabindex', + ignore: true, + }, ]; diff --git a/src/components/container/fixtures/comp1.js b/src/components/container/fixtures/comp1.js index a399724511..fcc0e1af7d 100644 --- a/src/components/container/fixtures/comp1.js +++ b/src/components/container/fixtures/comp1.js @@ -1,84 +1,80 @@ export default { - 'input': true, - 'tree': true, - 'components': [ - { - 'input': true, - 'tableView': true, - 'inputType': 'text', - 'inputMask': '', - 'label': 'First Name', - 'key': 'firstName', - 'placeholder': '', - 'prefix': '', - 'suffix': '', - 'multiple': false, - 'defaultValue': '', - 'protected': false, - 'unique': false, - 'persistent': true, - 'validate': { - 'required': false, - 'minLength': '', - 'maxLength': '', - 'pattern': '', - 'custom': '', - 'customPrivate': false - }, - 'conditional': { - 'show': '', - 'when': null, - 'eq': '' - }, - 'type': 'textfield', - 'tags': [ - - ] + input: true, + tree: true, + components: [ + { + input: true, + tableView: true, + inputType: 'text', + inputMask: '', + label: 'First Name', + key: 'firstName', + placeholder: '', + prefix: '', + suffix: '', + multiple: false, + defaultValue: '', + protected: false, + unique: false, + persistent: true, + validate: { + required: false, + minLength: '', + maxLength: '', + pattern: '', + custom: '', + customPrivate: false, + }, + conditional: { + show: '', + when: null, + eq: '', + }, + type: 'textfield', + tags: [], + }, + { + input: true, + tableView: true, + inputType: 'text', + inputMask: '', + label: 'Last Name', + key: 'lastName', + placeholder: '', + prefix: '', + suffix: '', + multiple: false, + defaultValue: '', + protected: false, + unique: false, + persistent: true, + validate: { + required: false, + minLength: '', + maxLength: '', + pattern: '', + custom: '', + customPrivate: false, + }, + conditional: { + show: '', + when: null, + eq: '', + }, + type: 'textfield', + tags: [], + }, + ], + tableView: true, + label: 'User', + key: 'user', + protected: false, + persistent: true, + type: 'container', + tags: [], + conditional: { + show: '', + when: null, + eq: '', }, - { - 'input': true, - 'tableView': true, - 'inputType': 'text', - 'inputMask': '', - 'label': 'Last Name', - 'key': 'lastName', - 'placeholder': '', - 'prefix': '', - 'suffix': '', - 'multiple': false, - 'defaultValue': '', - 'protected': false, - 'unique': false, - 'persistent': true, - 'validate': { - 'required': false, - 'minLength': '', - 'maxLength': '', - 'pattern': '', - 'custom': '', - 'customPrivate': false - }, - 'conditional': { - 'show': '', - 'when': null, - 'eq': '' - }, - 'type': 'textfield', - 'tags': [ - - ] - } - ], - 'tableView': true, - 'label': 'User', - 'key': 'user', - 'protected': false, - 'persistent': true, - 'type': 'container', - 'tags': [], - 'conditional': { - 'show': '', - 'when': null, - 'eq': '' - } }; diff --git a/src/components/container/fixtures/comp2.js b/src/components/container/fixtures/comp2.js index 211478a049..9094e4adf8 100644 --- a/src/components/container/fixtures/comp2.js +++ b/src/components/container/fixtures/comp2.js @@ -1,32 +1,32 @@ export default { - 'input': true, - 'tree': true, - 'components': [ - { - type: 'editgrid', - label: 'Edit Grid', - key: 'children', - input: true, - components: [ + input: true, + tree: true, + components: [ { - type: 'textfield', - key: 'name', - label: 'Name', - input: true - } - ] - } - ], - 'tableView': true, - 'label': 'User', - 'key': 'user', - 'protected': false, - 'persistent': true, - 'type': 'container', - 'tags': [], - 'conditional': { - 'show': '', - 'when': null, - 'eq': '' - } + type: 'editgrid', + label: 'Edit Grid', + key: 'children', + input: true, + components: [ + { + type: 'textfield', + key: 'name', + label: 'Name', + input: true, + }, + ], + }, + ], + tableView: true, + label: 'User', + key: 'user', + protected: false, + persistent: true, + type: 'container', + tags: [], + conditional: { + show: '', + when: null, + eq: '', + }, }; diff --git a/src/components/container/fixtures/comp3.js b/src/components/container/fixtures/comp3.js index f97cb5025c..812174cd48 100644 --- a/src/components/container/fixtures/comp3.js +++ b/src/components/container/fixtures/comp3.js @@ -1,55 +1,55 @@ export default { - type: 'form', - components: [ - { - label: 'Text Field', - tableView: true, - validate: { required: true, minLength: 2 }, - key: 'textField', - type: 'textfield', - input: true, - }, - { - label: 'Container', - tableView: false, - validate: { required: true }, - key: 'container', - type: 'container', - input: true, - components: [ + type: 'form', + components: [ { - label: 'Text Field', - tableView: true, - validate: { required: true, minLength: 2 }, - key: 'textField', - type: 'textfield', - input: true, + label: 'Text Field', + tableView: true, + validate: { required: true, minLength: 2 }, + key: 'textField', + type: 'textfield', + input: true, }, - ], - }, - { - label: 'Submit as Draft', - action: 'saveState', - showValidations: false, - theme: 'secondary', - tableView: false, - key: 'submitAsDraft', - type: 'button', - state: 'draft', - input: true, - }, - { - label: 'Submit', - showValidations: false, - tableView: false, - key: 'submit', - type: 'button', - input: true, - saveOnEnter: false, - }, - ], - display: 'form', - name: 'fio37151', - path: 'fio37151', - project: '6103a71934405111ce3f64fa', + { + label: 'Container', + tableView: false, + validate: { required: true }, + key: 'container', + type: 'container', + input: true, + components: [ + { + label: 'Text Field', + tableView: true, + validate: { required: true, minLength: 2 }, + key: 'textField', + type: 'textfield', + input: true, + }, + ], + }, + { + label: 'Submit as Draft', + action: 'saveState', + showValidations: false, + theme: 'secondary', + tableView: false, + key: 'submitAsDraft', + type: 'button', + state: 'draft', + input: true, + }, + { + label: 'Submit', + showValidations: false, + tableView: false, + key: 'submit', + type: 'button', + input: true, + saveOnEnter: false, + }, + ], + display: 'form', + name: 'fio37151', + path: 'fio37151', + project: '6103a71934405111ce3f64fa', }; diff --git a/src/components/container/fixtures/comp4.js b/src/components/container/fixtures/comp4.js index c48b94e359..54ffe3f0b9 100644 --- a/src/components/container/fixtures/comp4.js +++ b/src/components/container/fixtures/comp4.js @@ -1,43 +1,43 @@ export default { - type: 'form', - display: 'form', - components: [ - { - label: 'Checkbox', - tableView: false, - key: 'checkbox', - type: 'checkbox', - input: true, - }, - { - label: 'Container', - tableView: false, - key: 'container', - conditional: { - show: true, - when: 'checkbox', - eq: 'true', - }, - type: 'container', - input: true, - components: [ + type: 'form', + display: 'form', + components: [ { - label: 'Text Field', - applyMaskOn: 'change', - tableView: true, - key: 'textField', - type: 'textfield', - input: true, + label: 'Checkbox', + tableView: false, + key: 'checkbox', + type: 'checkbox', + input: true, }, - ], - }, - { - type: 'button', - label: 'Submit', - key: 'submit', - disableOnInvalid: true, - input: true, - tableView: false, - }, - ], + { + label: 'Container', + tableView: false, + key: 'container', + conditional: { + show: true, + when: 'checkbox', + eq: 'true', + }, + type: 'container', + input: true, + components: [ + { + label: 'Text Field', + applyMaskOn: 'change', + tableView: true, + key: 'textField', + type: 'textfield', + input: true, + }, + ], + }, + { + type: 'button', + label: 'Submit', + key: 'submit', + disableOnInvalid: true, + input: true, + tableView: false, + }, + ], }; diff --git a/src/components/content/Content.form.js b/src/components/content/Content.form.js index b81c82cd24..a087890b69 100644 --- a/src/components/content/Content.form.js +++ b/src/components/content/Content.form.js @@ -2,37 +2,42 @@ import Components from '../Components'; import ContentEditDisplay from './editForm/Content.edit.display'; import ContentEditLogic from './editForm/Content.edit.logic'; -export default function(...extend) { - const editForm = Components.baseEditForm([ - { - key: 'display', - components: ContentEditDisplay, - }, - { - key: 'data', - ignore: true, - }, - { - key: 'validation', - ignore: true, - }, - { - key: 'logic', - components: ContentEditLogic, - }, - ], ...extend); - // Add content as full width above the settings. - editForm.components = [{ - weight: 0, - type: 'textarea', - editor: 'ckeditor', - label: 'Content', - hideLabel: true, - input: true, - key: 'html', - as: 'html', - rows: 3, - tooltip: 'The HTML template for the result data items.', - }].concat(editForm.components); - return editForm; +export default function (...extend) { + const editForm = Components.baseEditForm( + [ + { + key: 'display', + components: ContentEditDisplay, + }, + { + key: 'data', + ignore: true, + }, + { + key: 'validation', + ignore: true, + }, + { + key: 'logic', + components: ContentEditLogic, + }, + ], + ...extend, + ); + // Add content as full width above the settings. + editForm.components = [ + { + weight: 0, + type: 'textarea', + editor: 'ckeditor', + label: 'Content', + hideLabel: true, + input: true, + key: 'html', + as: 'html', + rows: 3, + tooltip: 'The HTML template for the result data items.', + }, + ].concat(editForm.components); + return editForm; } diff --git a/src/components/content/Content.js b/src/components/content/Content.js index 8ca7273481..4d1f2ab5a5 100644 --- a/src/components/content/Content.js +++ b/src/components/content/Content.js @@ -2,80 +2,91 @@ import Component from '../_classes/component/Component'; import _ from 'lodash'; export default class ContentComponent extends Component { - static schema(...extend) { - return Component.schema({ - label: 'Content', - type: 'content', - key: 'content', - input: false, - html: '' - }, ...extend); - } + static schema(...extend) { + return Component.schema( + { + label: 'Content', + type: 'content', + key: 'content', + input: false, + html: '', + }, + ...extend, + ); + } - static get builderInfo() { - return { - title: 'Content', - group: 'layout', - icon: 'html5', - preview: false, - showPreview: false, - documentation: '/userguide/form-building/layout-components#content', - weight: 5, - schema: ContentComponent.schema() - }; - } + static get builderInfo() { + return { + title: 'Content', + group: 'layout', + icon: 'html5', + preview: false, + showPreview: false, + documentation: '/userguide/form-building/layout-components#content', + weight: 5, + schema: ContentComponent.schema(), + }; + } - static savedValueTypes() { - return []; - } + static savedValueTypes() { + return []; + } - get defaultSchema() { - return ContentComponent.schema(); - } + get defaultSchema() { + return ContentComponent.schema(); + } - get content() { - if (this.builderMode) { - return this.component.html || 'Content'; + get content() { + if (this.builderMode) { + return this.component.html || 'Content'; + } + const submission = _.get(this.root, 'submission', {}); + return this.component.html + ? this.interpolate(this.component.html, { + metadata: submission.metadata || {}, + submission: submission, + data: this.rootValue, + row: this.data, + }) + : ''; } - const submission = _.get(this.root, 'submission', {}); - return this.component.html ? this.interpolate(this.component.html, { - metadata: submission.metadata || {}, - submission: submission, - data: this.rootValue, - row: this.data - }) : ''; - } - render() { - return super.render(this.renderTemplate('html', { - tag: 'div', - attrs: [], - content: this.content, - })); - } + render() { + return super.render( + this.renderTemplate('html', { + tag: 'div', + attrs: [], + content: this.content, + }), + ); + } - get dataReady() { - return this.root?.submissionReady || Promise.resolve(); - } + get dataReady() { + return this.root?.submissionReady || Promise.resolve(); + } - attach(element) { - this.loadRefs(element, { html: 'single' }); - this.dataReady.then(() => { - if (this.refs.html) { - this.setContent(this.refs.html, this.content); - } - }); - if (this.component.refreshOnChange) { - this.on('change', () => { - if (this.refs.html) { - this.setContent(this.refs.html, this.content); + attach(element) { + this.loadRefs(element, { html: 'single' }); + this.dataReady.then(() => { + if (this.refs.html) { + this.setContent(this.refs.html, this.content); + } + }); + if (this.component.refreshOnChange) { + this.on( + 'change', + () => { + if (this.refs.html) { + this.setContent(this.refs.html, this.content); + } + }, + true, + ); } - }, true); + return super.attach(element); } - return super.attach(element); - } - get emptyValue() { - return ''; - } + get emptyValue() { + return ''; + } } diff --git a/src/components/content/Content.unit.js b/src/components/content/Content.unit.js index 3e4d7d2007..9a16f25729 100644 --- a/src/components/content/Content.unit.js +++ b/src/components/content/Content.unit.js @@ -4,48 +4,52 @@ import Harness from '../../../test/harness'; import ContentComponent from './Content'; import { Formio } from '../../Formio'; -import { - comp1 -} from './fixtures'; +import { comp1 } from './fixtures'; -describe('Content Component', function() { - it('Should build a content component', function() { - return Harness.testCreate(ContentComponent, comp1).then((component) => { - const html = component.element.querySelector('[ref="html"]'); - assert.equal(html.innerHTML.trim(), comp1.html.trim()); +describe('Content Component', function () { + it('Should build a content component', function () { + return Harness.testCreate(ContentComponent, comp1).then((component) => { + const html = component.element.querySelector('[ref="html"]'); + assert.equal(html.innerHTML.trim(), comp1.html.trim()); + }); }); - }); - it('Should update after submission set', function(done) { - const formJson = { - components: [{ - html: '

    {{submission.data.textField}}

    ', - label: 'Content', - refreshOnChange: false, - key: 'content', - type: 'content', - }, { - label: 'Text Field', - tableView: true, - key: 'textField', - type: 'textfield', - input: true - },] - }; - const element = document.createElement('div'); - Formio.createForm(element, formJson) - .then(form => { - form.submission = { - data: { - textField: 'textField' - } + it('Should update after submission set', function (done) { + const formJson = { + components: [ + { + html: '

    {{submission.data.textField}}

    ', + label: 'Content', + refreshOnChange: false, + key: 'content', + type: 'content', + }, + { + label: 'Text Field', + tableView: true, + key: 'textField', + type: 'textfield', + input: true, + }, + ], }; - const content = form.getComponent('content'); - form.dataReady.then(() => { - assert.equal(content.refs.html.innerHTML, '

    textField

    '); - done(); - }); - }) - .catch(done); - }); + const element = document.createElement('div'); + Formio.createForm(element, formJson) + .then((form) => { + form.submission = { + data: { + textField: 'textField', + }, + }; + const content = form.getComponent('content'); + form.dataReady.then(() => { + assert.equal( + content.refs.html.innerHTML, + '

    textField

    ', + ); + done(); + }); + }) + .catch(done); + }); }); diff --git a/src/components/content/editForm/Content.edit.display.js b/src/components/content/editForm/Content.edit.display.js index 9cc26a818e..fc61f1705f 100644 --- a/src/components/content/editForm/Content.edit.display.js +++ b/src/components/content/editForm/Content.edit.display.js @@ -1,46 +1,46 @@ export default [ - { - key: 'labelPosition', - ignore: true - }, - { - key: 'placeholder', - ignore: true - }, - { - key: 'description', - ignore: true - }, - { - key: 'tooltip', - ignore: true - }, - { - key: 'hideLabel', - ignore: true - }, - { - key: 'autofocus', - ignore: true - }, - { - key: 'disabled', - ignore: true - }, - { - key: 'tabindex', - ignore: true - }, - { - key: 'tableView', - ignore: true - }, - { - weight: 700, - type: 'checkbox', - label: 'Refresh On Change', - tooltip: 'Rerender the field whenever a value on the form changes.', - key: 'refreshOnChange', - input: true - }, + { + key: 'labelPosition', + ignore: true, + }, + { + key: 'placeholder', + ignore: true, + }, + { + key: 'description', + ignore: true, + }, + { + key: 'tooltip', + ignore: true, + }, + { + key: 'hideLabel', + ignore: true, + }, + { + key: 'autofocus', + ignore: true, + }, + { + key: 'disabled', + ignore: true, + }, + { + key: 'tabindex', + ignore: true, + }, + { + key: 'tableView', + ignore: true, + }, + { + weight: 700, + type: 'checkbox', + label: 'Refresh On Change', + tooltip: 'Rerender the field whenever a value on the form changes.', + key: 'refreshOnChange', + input: true, + }, ]; diff --git a/src/components/content/editForm/Content.edit.logic.js b/src/components/content/editForm/Content.edit.logic.js index 72bdaa5a3b..cd655ab64b 100644 --- a/src/components/content/editForm/Content.edit.logic.js +++ b/src/components/content/editForm/Content.edit.logic.js @@ -1,98 +1,105 @@ export default [ - { - key: 'logic', - components: [ - { - key: 'actions', + { + key: 'logic', components: [ - { - key: 'actionPanel', - components: [ - { - data: { - json: [ + { + key: 'actions', + components: [ { - label: 'Hidden', - value: 'hidden', - type: 'boolean', + key: 'actionPanel', + components: [ + { + data: { + json: [ + { + label: 'Hidden', + value: 'hidden', + type: 'boolean', + }, + { + label: 'Required', + value: 'validate.required', + type: 'boolean', + }, + { + label: 'Disabled', + value: 'disabled', + type: 'boolean', + }, + { + label: 'Label', + value: 'label', + type: 'string', + }, + { + label: 'Title', + value: 'title', + type: 'string', + }, + { + label: 'Tooltip', + value: 'tooltip', + type: 'string', + }, + { + label: 'Description', + value: 'description', + type: 'string', + }, + { + label: 'Placeholder', + value: 'placeholder', + type: 'string', + }, + { + label: 'CSS Class', + value: 'className', + type: 'string', + }, + { + label: 'Container Custom Class', + value: 'customClass', + type: 'string', + }, + { + label: 'Content', + value: 'html', + type: 'string', + component: 'content', + }, + ], + }, + key: 'property', + }, + { + type: 'textarea', + editor: 'ace', + rows: 10, + as: 'html', + label: 'Content', + tooltip: 'The content of this HTML element.', + defaultValue: '
    Content
    ', + key: 'content', + weight: 30, + input: true, + customConditional(context) { + return ( + context.row.type === 'property' && + Object.prototype.hasOwnProperty.call( + context.row, + 'property', + ) && + context.row.property.type === + 'string' && + context.row.property.component === + 'content' + ); + }, + }, + ], }, - { - label: 'Required', - value: 'validate.required', - type: 'boolean', - }, - { - label: 'Disabled', - value: 'disabled', - type: 'boolean', - }, - { - label: 'Label', - value: 'label', - type: 'string', - }, - { - label: 'Title', - value: 'title', - type: 'string', - }, - { - label: 'Tooltip', - value: 'tooltip', - type: 'string', - }, - { - label: 'Description', - value: 'description', - type: 'string', - }, - { - label: 'Placeholder', - value: 'placeholder', - type: 'string', - }, - { - label: 'CSS Class', - value: 'className', - type: 'string', - }, - { - label: 'Container Custom Class', - value: 'customClass', - type: 'string', - }, - { - label: 'Content', - value: 'html', - type: 'string', - component: 'content', - }, - ], - }, - key: 'property', - }, - { - type: 'textarea', - editor: 'ace', - rows: 10, - as: 'html', - label: 'Content', - tooltip: 'The content of this HTML element.', - defaultValue: '
    Content
    ', - key: 'content', - weight: 30, - input: true, - customConditional(context) { - return (context.row.type === 'property' && - Object.prototype.hasOwnProperty.call(context.row, 'property') && - context.row.property.type === 'string' && - context.row.property.component === 'content'); - }, - }, - ], - }, + ], + }, ], - }, - ], - }, + }, ]; diff --git a/src/components/content/fixtures/comp1.js b/src/components/content/fixtures/comp1.js index 0d6665b468..56c9089b1f 100644 --- a/src/components/content/fixtures/comp1.js +++ b/src/components/content/fixtures/comp1.js @@ -1,14 +1,12 @@ export default { - 'conditional': { - 'eq': '', - 'when': null, - 'show': '' - }, - 'tags': [ - - ], - 'type': 'content', - 'html': '

    This is a test

    \n', - 'input': false, - 'key': 'content1' + conditional: { + eq: '', + when: null, + show: '', + }, + tags: [], + type: 'content', + html: '

    This is a test

    \n', + input: false, + key: 'content1', }; diff --git a/src/components/currency/Currency.form.js b/src/components/currency/Currency.form.js index 2bfc648ab5..a3e2257889 100644 --- a/src/components/currency/Currency.form.js +++ b/src/components/currency/Currency.form.js @@ -1,40 +1,43 @@ import baseEditForm from '../textfield/TextField.form'; import CurrencyEditDisplay from './editForm/Currency.edit.display'; import CurrencyEditData from './editForm/Currency.edit.data'; -export default function(...extend) { - return baseEditForm([ - { - key: 'display', - components: CurrencyEditDisplay - }, - { - key: 'data', - components: CurrencyEditData - }, - { - key: 'validation', - components: [ - { - key: 'validate.minLength', - ignore: true, - }, - { - key: 'validate.maxLength', - ignore: true, - }, - { - key: 'validate.minWords', - ignore: true, - }, - { - key: 'validate.maxWords', - ignore: true, - }, - { - key: 'validate.pattern', - ignore: true, - }, - ] - }, - ], ...extend); +export default function (...extend) { + return baseEditForm( + [ + { + key: 'display', + components: CurrencyEditDisplay, + }, + { + key: 'data', + components: CurrencyEditData, + }, + { + key: 'validation', + components: [ + { + key: 'validate.minLength', + ignore: true, + }, + { + key: 'validate.maxLength', + ignore: true, + }, + { + key: 'validate.minWords', + ignore: true, + }, + { + key: 'validate.maxWords', + ignore: true, + }, + { + key: 'validate.pattern', + ignore: true, + }, + ], + }, + ], + ...extend, + ); } diff --git a/src/components/currency/Currency.js b/src/components/currency/Currency.js index fd8e7c31ab..3814eec32b 100644 --- a/src/components/currency/Currency.js +++ b/src/components/currency/Currency.js @@ -5,195 +5,237 @@ import { getCurrencyAffixes } from '../../utils/utils'; import NumberComponent from '../number/Number'; export default class CurrencyComponent extends NumberComponent { - static schema(...extend) { - return NumberComponent.schema({ - type: 'currency', - label: 'Currency', - key: 'currency' - }, ...extend); - } - - static get builderInfo() { - return { - title: 'Currency', - group: 'advanced', - icon: 'usd', - documentation: '/userguide/form-building/advanced-components#currency', - weight: 70, - schema: CurrencyComponent.schema() - }; - } - - constructor(component, options, data) { - // Currency should default to have a delimiter unless otherwise specified. - if (component && !Object.prototype.hasOwnProperty.call(component, 'delimiter')) { - component.delimiter = true; + static schema(...extend) { + return NumberComponent.schema( + { + type: 'currency', + label: 'Currency', + key: 'currency', + }, + ...extend, + ); } - super(component, options, data); - } - - /** - * Creates the number mask for currency numbers. - * - * @return {*} - */ - createNumberMask() { - const decimalLimit = _.get(this.component, 'decimalLimit', 2); - const affixes = getCurrencyAffixes({ - currency: this.component.currency, - decimalLimit: decimalLimit, - decimalSeparator: this.decimalSeparator, - lang: this.options.language - }); - this.currencyPrefix = this.options.prefix || affixes.prefix; - this.currencySuffix = this.options.suffix || affixes.suffix; - return createNumberMask({ - prefix: this.currencyPrefix, - suffix: this.currencySuffix, - thousandsSeparatorSymbol: _.get(this.component, 'thousandsSeparator', this.delimiter), - decimalSymbol: _.get(this.component, 'decimalSymbol', this.decimalSeparator), - decimalLimit: decimalLimit, - allowNegative: _.get(this.component, 'allowNegative', true), - allowDecimal: this.isDecimalAllowed(), - }); - } - - isDecimalAllowed() { - return _.get(this.component, 'allowDecimal', true); - } - - setInputMask(input) { - const affixes = getCurrencyAffixes({ - currency: this.component.currency, - decimalSeparator: this.decimalSeparator, - lang: this.options.language, - }); - let numberPattern = `${affixes.prefix}[0-9`; - numberPattern += this.decimalSeparator || ''; - numberPattern += this.delimiter || ''; - numberPattern += ']*'; - input.setAttribute('pattern', numberPattern); - input.mask = maskInput({ - inputElement: input, - mask: this.numberMask || '', - pipe: (conformedValue) => { - if (conformedValue === '$0._') { - // Delay to allow mask to update first. - setTimeout(() => { - const caretPosition = input.value.length - 1; - input.setSelectionRange(caretPosition, caretPosition); - }); + + static get builderInfo() { + return { + title: 'Currency', + group: 'advanced', + icon: 'usd', + documentation: + '/userguide/form-building/advanced-components#currency', + weight: 70, + schema: CurrencyComponent.schema(), + }; + } + + constructor(component, options, data) { + // Currency should default to have a delimiter unless otherwise specified. + if ( + component && + !Object.prototype.hasOwnProperty.call(component, 'delimiter') + ) { + component.delimiter = true; } - return conformedValue; - }, - shadowRoot: this.root ? this.root.shadowRoot : null - }); - } - - get defaultSchema() { - return CurrencyComponent.schema(); - } - - parseNumber(value) { - return super.parseNumber(this.stripPrefixSuffix(value)); - } - - parseValue(value) { - return super.parseValue(this.stripPrefixSuffix(value)); - } - - addZerosAndFormatValue(value) { - if (!value && value !== 0) return; - - const decimalLimit = _.get(this.component, 'decimalLimit', 2); - - let integerPart; - let decimalPart = ''; - let decimalPartNumbers = []; - const negativeValueSymbol = '-'; - const hasPrefix = this.currencyPrefix ? value.includes(this.currencyPrefix) : false; - const hasSuffix = this.currencySuffix ? value.includes(this.currencySuffix) : false; - const isNegative = value.includes(negativeValueSymbol) || false; - - value = this.stripPrefixSuffix(isNegative ? value.replace(negativeValueSymbol,'') : value); - - if (value.includes(this.decimalSeparator)) { - [integerPart, decimalPart] = value.split(this.decimalSeparator); - decimalPartNumbers =[...decimalPart.split('')] ; + super(component, options, data); } - else { - integerPart = value; + + /** + * Creates the number mask for currency numbers. + * + * @return {*} + */ + createNumberMask() { + const decimalLimit = _.get(this.component, 'decimalLimit', 2); + const affixes = getCurrencyAffixes({ + currency: this.component.currency, + decimalLimit: decimalLimit, + decimalSeparator: this.decimalSeparator, + lang: this.options.language, + }); + this.currencyPrefix = this.options.prefix || affixes.prefix; + this.currencySuffix = this.options.suffix || affixes.suffix; + return createNumberMask({ + prefix: this.currencyPrefix, + suffix: this.currencySuffix, + thousandsSeparatorSymbol: _.get( + this.component, + 'thousandsSeparator', + this.delimiter, + ), + decimalSymbol: _.get( + this.component, + 'decimalSymbol', + this.decimalSeparator, + ), + decimalLimit: decimalLimit, + allowNegative: _.get(this.component, 'allowNegative', true), + allowDecimal: this.isDecimalAllowed(), + }); } - if (decimalPart.length < decimalLimit) { - while (decimalPartNumbers.length < decimalLimit) { - decimalPartNumbers.push('0'); - } + isDecimalAllowed() { + return _.get(this.component, 'allowDecimal', true); } - const formattedValue = `${isNegative ? negativeValueSymbol:''}${hasPrefix ? this.currencyPrefix : ''}${integerPart}${this.decimalSeparator}${decimalPartNumbers.join('')}${hasSuffix ? this.currencySuffix : ''}`; + setInputMask(input) { + const affixes = getCurrencyAffixes({ + currency: this.component.currency, + decimalSeparator: this.decimalSeparator, + lang: this.options.language, + }); + let numberPattern = `${affixes.prefix}[0-9`; + numberPattern += this.decimalSeparator || ''; + numberPattern += this.delimiter || ''; + numberPattern += ']*'; + input.setAttribute('pattern', numberPattern); + input.mask = maskInput({ + inputElement: input, + mask: this.numberMask || '', + pipe: (conformedValue) => { + if (conformedValue === '$0._') { + // Delay to allow mask to update first. + setTimeout(() => { + const caretPosition = input.value.length - 1; + input.setSelectionRange(caretPosition, caretPosition); + }); + } + return conformedValue; + }, + shadowRoot: this.root ? this.root.shadowRoot : null, + }); + } - return super.formatValue(formattedValue); - } + get defaultSchema() { + return CurrencyComponent.schema(); + } - getValueAsString(value, options) { - const stringValue = super.getValueAsString(value, options); + parseNumber(value) { + return super.parseNumber(this.stripPrefixSuffix(value)); + } - // eslint-disable-next-line eqeqeq - if (value || value == '0') { - if (Array.isArray(value)) { - return value.map((val) => this.addZerosAndFormatValue(super.getValueAsString(val, options))).join(', '); - } - return this.addZerosAndFormatValue(stringValue); + parseValue(value) { + return super.parseValue(this.stripPrefixSuffix(value)); } - return stringValue; - } + addZerosAndFormatValue(value) { + if (!value && value !== 0) return; + + const decimalLimit = _.get(this.component, 'decimalLimit', 2); + + let integerPart; + let decimalPart = ''; + let decimalPartNumbers = []; + const negativeValueSymbol = '-'; + const hasPrefix = this.currencyPrefix + ? value.includes(this.currencyPrefix) + : false; + const hasSuffix = this.currencySuffix + ? value.includes(this.currencySuffix) + : false; + const isNegative = value.includes(negativeValueSymbol) || false; + + value = this.stripPrefixSuffix( + isNegative ? value.replace(negativeValueSymbol, '') : value, + ); + + if (value.includes(this.decimalSeparator)) { + [integerPart, decimalPart] = value.split(this.decimalSeparator); + decimalPartNumbers = [...decimalPart.split('')]; + } else { + integerPart = value; + } - formatValue(value) { - if (value || value === '0') { - return this.addZerosAndFormatValue(value); - } + if (decimalPart.length < decimalLimit) { + while (decimalPartNumbers.length < decimalLimit) { + decimalPartNumbers.push('0'); + } + } - return super.formatValue(value); - } + const formattedValue = `${isNegative ? negativeValueSymbol : ''}${ + hasPrefix ? this.currencyPrefix : '' + }${integerPart}${this.decimalSeparator}${decimalPartNumbers.join('')}${ + hasSuffix ? this.currencySuffix : '' + }`; - stripPrefixSuffix(value) { - if (typeof value === 'string') { - try { - const hasPrefix = this.currencyPrefix ? value.includes(this.currencyPrefix) : false; - const hasSuffix = this.currencySuffix ? value.includes(this.currencySuffix) : false; - const hasDelimiter = value.includes(this.delimiter); - const hasDecimalSeparator = value.includes(this.decimalSeparator); + return super.formatValue(formattedValue); + } - if (this.currencyPrefix) { - value = value.replace(this.currencyPrefix, ''); + getValueAsString(value, options) { + const stringValue = super.getValueAsString(value, options); + + // eslint-disable-next-line eqeqeq + if (value || value == '0') { + if (Array.isArray(value)) { + return value + .map((val) => + this.addZerosAndFormatValue( + super.getValueAsString(val, options), + ), + ) + .join(', '); + } + return this.addZerosAndFormatValue(stringValue); } - if (this.currencySuffix) { - value = value.replace(this.currencySuffix, ''); + + return stringValue; + } + + formatValue(value) { + if (value || value === '0') { + return this.addZerosAndFormatValue(value); } - //when we enter $ in the field using dashboard, it contains '_' that is NaN - if ((hasPrefix || hasSuffix) && !hasDelimiter && !hasDecimalSeparator && (Number.isNaN(+value) || !value)) { - value ='0'; + + return super.formatValue(value); + } + + stripPrefixSuffix(value) { + if (typeof value === 'string') { + try { + const hasPrefix = this.currencyPrefix + ? value.includes(this.currencyPrefix) + : false; + const hasSuffix = this.currencySuffix + ? value.includes(this.currencySuffix) + : false; + const hasDelimiter = value.includes(this.delimiter); + const hasDecimalSeparator = value.includes( + this.decimalSeparator, + ); + + if (this.currencyPrefix) { + value = value.replace(this.currencyPrefix, ''); + } + if (this.currencySuffix) { + value = value.replace(this.currencySuffix, ''); + } + //when we enter $ in the field using dashboard, it contains '_' that is NaN + if ( + (hasPrefix || hasSuffix) && + !hasDelimiter && + !hasDecimalSeparator && + (Number.isNaN(+value) || !value) + ) { + value = '0'; + } + } catch (err) { + // If value doesn't have a replace method, continue on as before. + } } - } - catch (err) { - // If value doesn't have a replace method, continue on as before. - } + return value; + } + + addFocusBlurEvents(element) { + super.addFocusBlurEvents(element); + + this.addEventListener(element, 'focus', () => { + if (element.defaultValue === element.value) { + element.setSelectionRange(0, element.defaultValue.length); + } + }); + this.addEventListener(element, 'blur', () => { + element.value = this.getValueAsString( + this.addZerosAndFormatValue(this.parseValue(element.value)), + ); + }); } - return value; - } - - addFocusBlurEvents(element) { - super.addFocusBlurEvents(element); - - this.addEventListener(element, 'focus', () => { - if (element.defaultValue === element.value) { - element.setSelectionRange(0, element.defaultValue.length); - } - }); - this.addEventListener(element, 'blur', () => { - element.value = this.getValueAsString(this.addZerosAndFormatValue(this.parseValue(element.value))); - }); - } } diff --git a/src/components/currency/Currency.unit.js b/src/components/currency/Currency.unit.js index 863d03e24c..146c6b6655 100644 --- a/src/components/currency/Currency.unit.js +++ b/src/components/currency/Currency.unit.js @@ -1,329 +1,492 @@ import Harness from '../../../test/harness'; import CurrencyComponent from './Currency'; import assert from 'power-assert'; -import { - comp1, - comp2, - comp3, - comp4, -} from './fixtures'; - -describe('Currency Component', function() { - before(function(done) { - // Need to polyfill some Intl.locale support, since node doesn't include it in standard builds - require('../../../test/numberFormatPolyfill'); - - done(); - }); - - it('Should build a currency component', function() { - return Harness.testCreate(CurrencyComponent, comp1).then((component) => { - Harness.testElements(component, 'input[type="text"]', 1); - }); - }); - - it('Should place a caret between the period and the underline.', function(done) { - Harness.testCreate(CurrencyComponent, comp3, { language: 'en-US' }) - .then((component) => { - const inputEvent = new Event('input'); - const currencyElement = component.element.querySelector('[name="data[currency]"]'); - - currencyElement.value = 42; - currencyElement.dispatchEvent(inputEvent); - assert.equal(currencyElement.value, '$42'); - - currencyElement.value = '.'; - currencyElement.dispatchEvent(inputEvent); - setTimeout(() => { - assert.equal(currencyElement.selectionStart, 3); - done(); - }, 200); - }); - }); - - it('Should format value on blur for USA locale', function(done) { - Harness.testCreate(CurrencyComponent, comp1, { language: 'en-US' }).then((component) => { - component.root = { - onChange: ()=>{}, - triggerChange: ()=>{}, - }; - - const blurEvent = new Event('blur'); - const inputEvent = new Event('input'); - const valueElement = component.element.querySelector('[name="data[money]"]'); - - valueElement.value = 22222222; - valueElement.dispatchEvent(inputEvent); - valueElement.dispatchEvent(blurEvent); - assert.equal(valueElement.value, '$22,222,222.00'); - - valueElement.value = 22222222.2; - valueElement.dispatchEvent(inputEvent); - valueElement.dispatchEvent(blurEvent); - assert.equal(valueElement.value, '$22,222,222.20'); - - valueElement.value = 22222; - valueElement.dispatchEvent(inputEvent); - valueElement.dispatchEvent(blurEvent); - assert.equal(valueElement.value, '$22,222.00'); - - valueElement.value = 222; - valueElement.dispatchEvent(inputEvent); - valueElement.dispatchEvent(blurEvent); - assert.equal(valueElement.value, '$222.00'); - - valueElement.value = 2; - valueElement.dispatchEvent(inputEvent); - valueElement.dispatchEvent(blurEvent); - assert.equal(valueElement.value, '$2.00'); - - done(); - }); - }); - - it('Should format value on blur for French locale', function(done) { - Harness.testCreate(CurrencyComponent, comp1, { language: 'fr' }).then((component) => { - component.root = { - onChange: ()=>{}, - triggerChange: ()=>{}, - }; - - const blurEvent = new Event('blur'); - const inputEvent = new Event('input'); - const valueElement = component.element.querySelector('[name="data[money]"]'); - - valueElement.value = 22222222; - valueElement.dispatchEvent(inputEvent); - valueElement.dispatchEvent(blurEvent); - assert.deepEqual(valueElement.value, '22 222 222,00 $US'); - - valueElement.value = '22222222,2'; - valueElement.dispatchEvent(inputEvent); - valueElement.dispatchEvent(blurEvent); - assert.deepEqual(valueElement.value, '22 222 222,20 $US'); - - valueElement.value = 22222; - valueElement.dispatchEvent(inputEvent); - valueElement.dispatchEvent(blurEvent); - assert.deepEqual(valueElement.value, '22 222,00 $US'); - - valueElement.value = 222; - valueElement.dispatchEvent(inputEvent); - valueElement.dispatchEvent(blurEvent); - assert.deepEqual(valueElement.value, '222,00 $US'); - - valueElement.value = 2; - valueElement.dispatchEvent(inputEvent); - valueElement.dispatchEvent(blurEvent); - assert.deepEqual(valueElement.value, '2,00 $US'); - - done(); +import { comp1, comp2, comp3, comp4 } from './fixtures'; + +describe('Currency Component', function () { + before(function (done) { + // Need to polyfill some Intl.locale support, since node doesn't include it in standard builds + require('../../../test/numberFormatPolyfill'); + + done(); }); - }); - it('Should not change entered value on blur if multiple value is set', function(done) { - Harness.testCreate(CurrencyComponent, comp2).then((component) => { - component.root = { - onChange: ()=>{}, - triggerChange: ()=>{}, - }; - const blurEvent = new Event('blur'); - const clickEvent = new Event('click'); - const addBtn = component.refs.addButton[0]; + it('Should build a currency component', function () { + return Harness.testCreate(CurrencyComponent, comp1).then( + (component) => { + Harness.testElements(component, 'input[type="text"]', 1); + }, + ); + }); - addBtn.dispatchEvent(clickEvent); + it('Should place a caret between the period and the underline.', function (done) { + Harness.testCreate(CurrencyComponent, comp3, { + language: 'en-US', + }).then((component) => { + const inputEvent = new Event('input'); + const currencyElement = component.element.querySelector( + '[name="data[currency]"]', + ); + + currencyElement.value = 42; + currencyElement.dispatchEvent(inputEvent); + assert.equal(currencyElement.value, '$42'); + + currencyElement.value = '.'; + currencyElement.dispatchEvent(inputEvent); + setTimeout(() => { + assert.equal(currencyElement.selectionStart, 3); + done(); + }, 200); + }); + }); - const firstValueElement = component.element.querySelectorAll('[name="data[currency]"]')[0]; - const secondValueElement = component.element.querySelectorAll('[name="data[currency]"]')[1]; + it('Should format value on blur for USA locale', function (done) { + Harness.testCreate(CurrencyComponent, comp1, { + language: 'en-US', + }).then((component) => { + component.root = { + onChange: () => {}, + triggerChange: () => {}, + }; + + const blurEvent = new Event('blur'); + const inputEvent = new Event('input'); + const valueElement = component.element.querySelector( + '[name="data[money]"]', + ); + + valueElement.value = 22222222; + valueElement.dispatchEvent(inputEvent); + valueElement.dispatchEvent(blurEvent); + assert.equal(valueElement.value, '$22,222,222.00'); + + valueElement.value = 22222222.2; + valueElement.dispatchEvent(inputEvent); + valueElement.dispatchEvent(blurEvent); + assert.equal(valueElement.value, '$22,222,222.20'); + + valueElement.value = 22222; + valueElement.dispatchEvent(inputEvent); + valueElement.dispatchEvent(blurEvent); + assert.equal(valueElement.value, '$22,222.00'); + + valueElement.value = 222; + valueElement.dispatchEvent(inputEvent); + valueElement.dispatchEvent(blurEvent); + assert.equal(valueElement.value, '$222.00'); + + valueElement.value = 2; + valueElement.dispatchEvent(inputEvent); + valueElement.dispatchEvent(blurEvent); + assert.equal(valueElement.value, '$2.00'); + + done(); + }); + }); - component.setValue([111,222]); + it('Should format value on blur for French locale', function (done) { + Harness.testCreate(CurrencyComponent, comp1, { language: 'fr' }).then( + (component) => { + component.root = { + onChange: () => {}, + triggerChange: () => {}, + }; + + const blurEvent = new Event('blur'); + const inputEvent = new Event('input'); + const valueElement = component.element.querySelector( + '[name="data[money]"]', + ); + + valueElement.value = 22222222; + valueElement.dispatchEvent(inputEvent); + valueElement.dispatchEvent(blurEvent); + assert.deepEqual(valueElement.value, '22 222 222,00 $US'); + + valueElement.value = '22222222,2'; + valueElement.dispatchEvent(inputEvent); + valueElement.dispatchEvent(blurEvent); + assert.deepEqual(valueElement.value, '22 222 222,20 $US'); + + valueElement.value = 22222; + valueElement.dispatchEvent(inputEvent); + valueElement.dispatchEvent(blurEvent); + assert.deepEqual(valueElement.value, '22 222,00 $US'); + + valueElement.value = 222; + valueElement.dispatchEvent(inputEvent); + valueElement.dispatchEvent(blurEvent); + assert.deepEqual(valueElement.value, '222,00 $US'); + + valueElement.value = 2; + valueElement.dispatchEvent(inputEvent); + valueElement.dispatchEvent(blurEvent); + assert.deepEqual(valueElement.value, '2,00 $US'); + + done(); + }, + ); + }); - firstValueElement.dispatchEvent(blurEvent); - secondValueElement.dispatchEvent(blurEvent); + it('Should not change entered value on blur if multiple value is set', function (done) { + Harness.testCreate(CurrencyComponent, comp2).then((component) => { + component.root = { + onChange: () => {}, + triggerChange: () => {}, + }; + const blurEvent = new Event('blur'); + const clickEvent = new Event('click'); + const addBtn = component.refs.addButton[0]; + + addBtn.dispatchEvent(clickEvent); + + const firstValueElement = component.element.querySelectorAll( + '[name="data[currency]"]', + )[0]; + const secondValueElement = component.element.querySelectorAll( + '[name="data[currency]"]', + )[1]; + + component.setValue([111, 222]); + + firstValueElement.dispatchEvent(blurEvent); + secondValueElement.dispatchEvent(blurEvent); + + assert.equal(component.dataValue[0], component.getValue()[0]); + assert.equal(component.dataValue[1], component.getValue()[1]); + done(); + }); + }); - assert.equal(component.dataValue[0], component.getValue()[0]); - assert.equal(component.dataValue[1], component.getValue()[1]); - done(); + it('Should format currency submissions for table view for French locale', function () { + return Harness.testCreate(CurrencyComponent, comp1, { + language: 'fr', + }).then((component) => { + const value1 = component.getValueAsString(1); + const value2 = component.getValueAsString(1.1); + const value3 = component.getValueAsString(1.11); + const value4 = component.getValueAsString(1111); + const value5 = component.getValueAsString(1111111); + const value6 = component.getValueAsString(-11111); + + assert.equal(value1, '1,00 $US'); + assert.equal(value2, '1,10 $US'); + assert.equal(value3, '1,11 $US'); + assert.equal(value4, '1 111,00 $US'); + assert.equal(value5, '1 111 111,00 $US'); + assert.equal(value6, '-11 111,00 $US'); + }); }); - }); - - it('Should format currency submissions for table view for French locale', function() { - return Harness.testCreate(CurrencyComponent, comp1, { language: 'fr' }).then((component) => { - const value1 = component.getValueAsString(1); - const value2 = component.getValueAsString(1.1); - const value3 = component.getValueAsString(1.11); - const value4 = component.getValueAsString(1111); - const value5 = component.getValueAsString(1111111); - const value6 = component.getValueAsString(-11111); - - assert.equal(value1, '1,00 $US'); - assert.equal(value2, '1,10 $US'); - assert.equal(value3, '1,11 $US'); - assert.equal(value4, '1 111,00 $US'); - assert.equal(value5, '1 111 111,00 $US'); - assert.equal(value6, '-11 111,00 $US'); + + it('Should format currency sumbissions for table view for USA locale', function () { + return Harness.testCreate(CurrencyComponent, comp1, { + language: 'en-US', + }).then((component) => { + const value1 = component.getValueAsString(1); + const value2 = component.getValueAsString(1.1); + const value3 = component.getValueAsString(1.11); + const value4 = component.getValueAsString(1111); + const value5 = component.getValueAsString(1111111); + const value6 = component.getValueAsString(-11111); + + assert.equal(value1, '$1.00'); + assert.equal(value2, '$1.10'); + assert.equal(value3, '$1.11'); + assert.equal(value4, '$1,111.00'); + assert.equal(value5, '$1,111,111.00'); + assert.equal(value6, '-$11,111.00'); + }); }); - }); - - it('Should format currency sumbissions for table view for USA locale', function() { - return Harness.testCreate(CurrencyComponent, comp1, { language: 'en-US' }).then((component) => { - const value1 = component.getValueAsString(1); - const value2 = component.getValueAsString(1.1); - const value3 = component.getValueAsString(1.11); - const value4 = component.getValueAsString(1111); - const value5 = component.getValueAsString(1111111); - const value6 = component.getValueAsString(-11111); - - assert.equal(value1, '$1.00'); - assert.equal(value2, '$1.10'); - assert.equal(value3, '$1.11'); - assert.equal(value4, '$1,111.00'); - assert.equal(value5, '$1,111,111.00'); - assert.equal(value6, '-$11,111.00'); + + it('Should add trailing zeros', function () { + return Harness.testCreate(CurrencyComponent, comp1, { + language: 'en-US', + }).then((component) => { + assert.equal(component.addZerosAndFormatValue(null)); + assert.equal(component.addZerosAndFormatValue('3'), '3.00'); + assert.equal(component.addZerosAndFormatValue('3.1'), '3.10'); + assert.equal(component.addZerosAndFormatValue('-3'), '-3.00'); + assert.equal(component.addZerosAndFormatValue('$3'), '$3.00'); + assert.equal(component.addZerosAndFormatValue('-$3'), '-$3.00'); + assert.equal(component.addZerosAndFormatValue('$3.3'), '$3.30'); + assert.equal(component.addZerosAndFormatValue('$3.33'), '$3.33'); + }); }); - }); - - it('Should add trailing zeros', function() { - return Harness.testCreate(CurrencyComponent, comp1, { language: 'en-US' }).then((component) => { - assert.equal(component.addZerosAndFormatValue(null),); - assert.equal(component.addZerosAndFormatValue('3'), '3.00'); - assert.equal(component.addZerosAndFormatValue('3.1'), '3.10'); - assert.equal(component.addZerosAndFormatValue('-3'), '-3.00'); - assert.equal(component.addZerosAndFormatValue('$3'), '$3.00'); - assert.equal(component.addZerosAndFormatValue('-$3'), '-$3.00'); - assert.equal(component.addZerosAndFormatValue('$3.3'), '$3.30'); - assert.equal(component.addZerosAndFormatValue('$3.33'), '$3.33'); + + it('Should set values with trailing zeros', function () { + return Harness.testCreate(CurrencyComponent, comp1, { + language: 'en-US', + }).then((component) => { + assert.equal(component.formatValue(null), null); + assert.equal(component.formatValue('0'), '0.00'); + assert.equal(component.formatValue('3'), '3.00'); + assert.equal(component.formatValue('3.3'), '3.30'); + assert.equal(component.formatValue('3.33'), '3.33'); + }); }); - }); - - it('Should set values with trailing zeros', function() { - return Harness.testCreate(CurrencyComponent, comp1, { language: 'en-US' }).then((component) => { - assert.equal(component.formatValue(null), null); - assert.equal(component.formatValue('0'), '0.00'); - assert.equal(component.formatValue('3'), '3.00'); - assert.equal(component.formatValue('3.3'), '3.30'); - assert.equal(component.formatValue('3.33'), '3.33'); + + it('Should format currency for USA locale', function () { + /* eslint-disable max-statements */ + return Harness.testCreate(CurrencyComponent, comp1, { + language: 'en-US', + }).then((component) => { + Harness.testSetInput(component, null, null, ''); + Harness.testSetInput(component, undefined, null, ''); + Harness.testSetInput(component, {}, null, ''); + Harness.testSetInput(component, [], null, ''); + Harness.testSetInput(component, [''], null, ''); + Harness.testSetInput(component, ['1'], 1, '$1.00'); + Harness.testSetInput(component, ['$1.00'], 1, '$1.00'); + Harness.testSetInput(component, 0, 0, '$0.00'); + Harness.testSetInput(component, 1.0, 1, '$1.00'); + Harness.testSetInput(component, -1.0, -1, '-$1.00'); + Harness.testSetInput(component, 1, 1, '$1.00'); + Harness.testSetInput(component, -1, -1, '-$1.00'); + Harness.testSetInput(component, 1000, 1000, '$1,000.00'); + Harness.testSetInput(component, -1000, -1000, '-$1,000.00'); + Harness.testSetInput(component, 1000.01, 1000.01, '$1,000.01'); + Harness.testSetInput(component, -1000.01, -1000.01, '-$1,000.01'); + Harness.testSetInput( + component, + 1234567890.12, + 1234567890.12, + '$1,234,567,890.12', + ); + Harness.testSetInput( + component, + -1234567890.12, + -1234567890.12, + '-$1,234,567,890.12', + ); + Harness.testSetInput( + component, + 123456789.123456789, + 123456789.12, + '$123,456,789.12', + ); + Harness.testSetInput( + component, + -123456789.123456789, + -123456789.12, + '-$123,456,789.12', + ); + Harness.testSetInput(component, '0', 0, '$0.00'); + Harness.testSetInput(component, '1.00', 1, '$1.00'); + Harness.testSetInput(component, '-1.00', -1, '-$1.00'); + Harness.testSetInput(component, '1', 1, '$1.00'); + Harness.testSetInput(component, '-1', -1, '-$1.00'); + Harness.testSetInput(component, '1000', 1000, '$1,000.00'); + Harness.testSetInput(component, '-1000', -1000, '-$1,000.00'); + Harness.testSetInput(component, '1000.01', 1000.01, '$1,000.01'); + Harness.testSetInput(component, '-1000.01', -1000.01, '-$1,000.01'); + Harness.testSetInput( + component, + '1234567890.12', + 1234567890.12, + '$1,234,567,890.12', + ); + Harness.testSetInput( + component, + '-1234567890.12', + -1234567890.12, + '-$1,234,567,890.12', + ); + Harness.testSetInput( + component, + '123456789.123456789', + 123456789.12, + '$123,456,789.12', + ); + Harness.testSetInput( + component, + '-123456789.123456789', + -123456789.12, + '-$123,456,789.12', + ); + Harness.testSetInput(component, '$0', 0, '$0.00'); + Harness.testSetInput(component, '$_', 0, '$0.00'); + Harness.testSetInput(component, '-$_', 0, '$0.00'); + Harness.testSetInput(component, '$1.00', 1, '$1.00'); + Harness.testSetInput(component, '-$1.00', -1, '-$1.00'); + Harness.testSetInput(component, '$1', 1, '$1.00'); + Harness.testSetInput(component, '-$1', -1, '-$1.00'); + Harness.testSetInput(component, '$1000', 1000, '$1,000.00'); + Harness.testSetInput(component, '-$1000', -1000, '-$1,000.00'); + Harness.testSetInput(component, '$1000.01', 1000.01, '$1,000.01'); + Harness.testSetInput( + component, + '-$1000.01', + -1000.01, + '-$1,000.01', + ); + Harness.testSetInput( + component, + '$1234567890.12', + 1234567890.12, + '$1,234,567,890.12', + ); + Harness.testSetInput( + component, + '-$1234567890.12', + -1234567890.12, + '-$1,234,567,890.12', + ); + Harness.testSetInput( + component, + '$123456789.123456789', + 123456789.12, + '$123,456,789.12', + ); + Harness.testSetInput( + component, + '-$123456789.123456789', + -123456789.12, + '-$123,456,789.12', + ); + }); + /* eslint-enable max-statements */ }); - }); - - it('Should format currency for USA locale', function() { - /* eslint-disable max-statements */ - return Harness.testCreate(CurrencyComponent, comp1, { language: 'en-US' }).then((component) => { - Harness.testSetInput(component, null, null, ''); - Harness.testSetInput(component, undefined, null, ''); - Harness.testSetInput(component, {}, null, ''); - Harness.testSetInput(component, [], null, ''); - Harness.testSetInput(component, [''], null, ''); - Harness.testSetInput(component, ['1'], 1, '$1.00'); - Harness.testSetInput(component, ['$1.00'], 1, '$1.00'); - Harness.testSetInput(component, 0, 0, '$0.00'); - Harness.testSetInput(component, 1.00, 1, '$1.00'); - Harness.testSetInput(component, -1.00, -1, '-$1.00'); - Harness.testSetInput(component, 1, 1, '$1.00'); - Harness.testSetInput(component, -1, -1, '-$1.00'); - Harness.testSetInput(component, 1000, 1000, '$1,000.00'); - Harness.testSetInput(component, -1000, -1000, '-$1,000.00'); - Harness.testSetInput(component, 1000.01, 1000.01, '$1,000.01'); - Harness.testSetInput(component, -1000.01, -1000.01, '-$1,000.01'); - Harness.testSetInput(component, 1234567890.12, 1234567890.12, '$1,234,567,890.12'); - Harness.testSetInput(component, -1234567890.12, -1234567890.12, '-$1,234,567,890.12'); - Harness.testSetInput(component, 123456789.123456789, 123456789.12, '$123,456,789.12'); - Harness.testSetInput(component, -123456789.123456789, -123456789.12, '-$123,456,789.12'); - Harness.testSetInput(component, '0', 0, '$0.00'); - Harness.testSetInput(component, '1.00', 1, '$1.00'); - Harness.testSetInput(component, '-1.00', -1, '-$1.00'); - Harness.testSetInput(component, '1', 1, '$1.00'); - Harness.testSetInput(component, '-1', -1, '-$1.00'); - Harness.testSetInput(component, '1000', 1000, '$1,000.00'); - Harness.testSetInput(component, '-1000', -1000, '-$1,000.00'); - Harness.testSetInput(component, '1000.01', 1000.01, '$1,000.01'); - Harness.testSetInput(component, '-1000.01', -1000.01, '-$1,000.01'); - Harness.testSetInput(component, '1234567890.12', 1234567890.12, '$1,234,567,890.12'); - Harness.testSetInput(component, '-1234567890.12', -1234567890.12, '-$1,234,567,890.12'); - Harness.testSetInput(component, '123456789.123456789', 123456789.12, '$123,456,789.12'); - Harness.testSetInput(component, '-123456789.123456789', -123456789.12, '-$123,456,789.12'); - Harness.testSetInput(component, '$0', 0, '$0.00'); - Harness.testSetInput(component, '$_', 0, '$0.00'); - Harness.testSetInput(component, '-$_', 0, '$0.00'); - Harness.testSetInput(component, '$1.00', 1, '$1.00'); - Harness.testSetInput(component, '-$1.00', -1, '-$1.00'); - Harness.testSetInput(component, '$1', 1, '$1.00'); - Harness.testSetInput(component, '-$1', -1, '-$1.00'); - Harness.testSetInput(component, '$1000', 1000, '$1,000.00'); - Harness.testSetInput(component, '-$1000', -1000, '-$1,000.00'); - Harness.testSetInput(component, '$1000.01', 1000.01, '$1,000.01'); - Harness.testSetInput(component, '-$1000.01', -1000.01, '-$1,000.01'); - Harness.testSetInput(component, '$1234567890.12', 1234567890.12, '$1,234,567,890.12'); - Harness.testSetInput(component, '-$1234567890.12', -1234567890.12, '-$1,234,567,890.12'); - Harness.testSetInput(component, '$123456789.123456789', 123456789.12, '$123,456,789.12'); - Harness.testSetInput(component, '-$123456789.123456789', -123456789.12, '-$123,456,789.12'); + + it('Should format currency for British locale', function () { + return Harness.testCreate(CurrencyComponent, comp1, { + language: 'en-GB', + }).then((component) => { + Harness.testSetInput(component, null, null, ''); + Harness.testSetInput(component, 0, 0, 'US$0.00'); + Harness.testSetInput(component, 1.0, 1, 'US$1.00'); + Harness.testSetInput(component, -1.0, -1, '-US$1.00'); + Harness.testSetInput(component, 1, 1, 'US$1.00'); + Harness.testSetInput(component, -1, -1, '-US$1.00'); + Harness.testSetInput(component, 1000, 1000, 'US$1,000.00'); + Harness.testSetInput(component, -1000, -1000, '-US$1,000.00'); + Harness.testSetInput(component, 1000.01, 1000.01, 'US$1,000.01'); + Harness.testSetInput(component, -1000.01, -1000.01, '-US$1,000.01'); + Harness.testSetInput( + component, + 1234567890.12, + 1234567890.12, + 'US$1,234,567,890.12', + ); + Harness.testSetInput( + component, + -1234567890.12, + -1234567890.12, + '-US$1,234,567,890.12', + ); + Harness.testSetInput( + component, + 123456789.123456789, + 123456789.12, + 'US$123,456,789.12', + ); + Harness.testSetInput( + component, + -123456789.123456789, + -123456789.12, + '-US$123,456,789.12', + ); + }); }); - /* eslint-enable max-statements */ - }); - - it('Should format currency for British locale', function() { - return Harness.testCreate(CurrencyComponent, comp1, { language: 'en-GB' }).then((component) => { - Harness.testSetInput(component, null, null, ''); - Harness.testSetInput(component, 0, 0, 'US$0.00'); - Harness.testSetInput(component, 1.00, 1, 'US$1.00'); - Harness.testSetInput(component, -1.00, -1, '-US$1.00'); - Harness.testSetInput(component, 1, 1, 'US$1.00'); - Harness.testSetInput(component, -1, -1, '-US$1.00'); - Harness.testSetInput(component, 1000, 1000, 'US$1,000.00'); - Harness.testSetInput(component, -1000, -1000, '-US$1,000.00'); - Harness.testSetInput(component, 1000.01, 1000.01, 'US$1,000.01'); - Harness.testSetInput(component, -1000.01, -1000.01, '-US$1,000.01'); - Harness.testSetInput(component, 1234567890.12, 1234567890.12, 'US$1,234,567,890.12'); - Harness.testSetInput(component, -1234567890.12, -1234567890.12, '-US$1,234,567,890.12'); - Harness.testSetInput(component, 123456789.123456789, 123456789.12, 'US$123,456,789.12'); - Harness.testSetInput(component, -123456789.123456789, -123456789.12, '-US$123,456,789.12'); + + it('Should format currency for French locale', function () { + return Harness.testCreate(CurrencyComponent, comp1, { + language: 'fr', + }).then((component) => { + // The spaces in these tests are a weird unicode space so be careful duplicating the tests. + Harness.testSetInput(component, null, null, ''); + Harness.testSetInput(component, 0, 0, '0,00 $US'); + Harness.testSetInput(component, 1.0, 1, '1,00 $US'); + Harness.testSetInput(component, -1.0, -1, '-1,00 $US'); + Harness.testSetInput(component, 1, 1, '1,00 $US'); + Harness.testSetInput(component, -1, -1, '-1,00 $US'); + Harness.testSetInput(component, 1000, 1000, '1 000,00 $US'); + Harness.testSetInput(component, -1000, -1000, '-1 000,00 $US'); + Harness.testSetInput(component, 1000.01, 1000.01, '1 000,01 $US'); + Harness.testSetInput( + component, + -1000.01, + -1000.01, + '-1 000,01 $US', + ); + Harness.testSetInput( + component, + 1234567890.12, + 1234567890.12, + '1 234 567 890,12 $US', + ); + Harness.testSetInput( + component, + -1234567890.12, + -1234567890.12, + '-1 234 567 890,12 $US', + ); + Harness.testSetInput( + component, + 1234567890.123456789, + 1234567890.12, + '1 234 567 890,12 $US', + ); + Harness.testSetInput( + component, + -1234567890.123456789, + -1234567890.12, + '-1 234 567 890,12 $US', + ); + }); }); - }); - - it('Should format currency for French locale', function() { - return Harness.testCreate(CurrencyComponent, comp1, { language: 'fr' }).then((component) => { - // The spaces in these tests are a weird unicode space so be careful duplicating the tests. - Harness.testSetInput(component, null, null, ''); - Harness.testSetInput(component, 0, 0, '0,00 $US'); - Harness.testSetInput(component, 1.00, 1, '1,00 $US'); - Harness.testSetInput(component, -1.00, -1, '-1,00 $US'); - Harness.testSetInput(component, 1, 1, '1,00 $US'); - Harness.testSetInput(component, -1, -1, '-1,00 $US'); - Harness.testSetInput(component, 1000, 1000, '1 000,00 $US'); - Harness.testSetInput(component, -1000, -1000, '-1 000,00 $US'); - Harness.testSetInput(component, 1000.01, 1000.01, '1 000,01 $US'); - Harness.testSetInput(component, -1000.01, -1000.01, '-1 000,01 $US'); - Harness.testSetInput(component, 1234567890.12, 1234567890.12, '1 234 567 890,12 $US'); - Harness.testSetInput(component, -1234567890.12, -1234567890.12, '-1 234 567 890,12 $US'); - Harness.testSetInput(component, 1234567890.123456789, 1234567890.12, '1 234 567 890,12 $US'); - Harness.testSetInput(component, -1234567890.123456789, -1234567890.12, '-1 234 567 890,12 $US'); + + it('Should format currency for German locale', function () { + return Harness.testCreate(CurrencyComponent, comp1, { + language: 'de', + }).then((component) => { + Harness.testSetInput(component, null, null, ''); + Harness.testSetInput(component, 0, 0, '0,00 $'); + Harness.testSetInput(component, 1.0, 1.0, '1,00 $'); + Harness.testSetInput(component, -1.0, -1.0, '-1,00 $'); + Harness.testSetInput(component, 1, 1, '1,00 $'); + Harness.testSetInput(component, -1, -1, '-1,00 $'); + Harness.testSetInput(component, 1000, 1000, '1.000,00 $'); + Harness.testSetInput(component, -1000, -1000, '-1.000,00 $'); + Harness.testSetInput(component, 1000.01, 1000.01, '1.000,01 $'); + Harness.testSetInput(component, -1000.01, -1000.01, '-1.000,01 $'); + Harness.testSetInput( + component, + 1234567890.12, + 1234567890.12, + '1.234.567.890,12 $', + ); + Harness.testSetInput( + component, + -1234567890.12, + -1234567890.12, + '-1.234.567.890,12 $', + ); + Harness.testSetInput( + component, + 1234567890.123456789, + 1234567890.12, + '1.234.567.890,12 $', + ); + Harness.testSetInput( + component, + -1234567890.123456789, + -1234567890.12, + '-1.234.567.890,12 $', + ); + }); }); - }); - - it('Should format currency for German locale', function() { - return Harness.testCreate(CurrencyComponent, comp1, { language: 'de' }).then((component) => { - Harness.testSetInput(component, null, null, ''); - Harness.testSetInput(component, 0, 0, '0,00 $'); - Harness.testSetInput(component, 1.00, 1.00, '1,00 $'); - Harness.testSetInput(component, -1.00, -1.00, '-1,00 $'); - Harness.testSetInput(component, 1, 1, '1,00 $'); - Harness.testSetInput(component, -1, -1, '-1,00 $'); - Harness.testSetInput(component, 1000, 1000, '1.000,00 $'); - Harness.testSetInput(component, -1000, -1000, '-1.000,00 $'); - Harness.testSetInput(component, 1000.01, 1000.01, '1.000,01 $'); - Harness.testSetInput(component, -1000.01, -1000.01, '-1.000,01 $'); - Harness.testSetInput(component, 1234567890.12, 1234567890.12, '1.234.567.890,12 $'); - Harness.testSetInput(component, -1234567890.12, -1234567890.12, '-1.234.567.890,12 $'); - Harness.testSetInput(component, 1234567890.123456789, 1234567890.12, '1.234.567.890,12 $'); - Harness.testSetInput(component, -1234567890.123456789, -1234567890.12, '-1.234.567.890,12 $'); + + it('Should return value as string properly for multiple values', function (done) { + Harness.testCreate(CurrencyComponent, comp4) + .then((component) => { + component.refs.input = null; + assert.equal( + component.getValueAsString([100, 200, 300, 500]), + '$100.00, $200.00, $300.00, $500.00', + ); + done(); + }) + .catch(done); }); - }); - - it('Should return value as string properly for multiple values', function(done) { - Harness.testCreate(CurrencyComponent, comp4).then((component) => { - component.refs.input = null; - assert.equal(component.getValueAsString([100, 200, 300, 500]), '$100.00, $200.00, $300.00, $500.00'); - done(); - }).catch(done); - }); }); diff --git a/src/components/currency/editForm/Currency.edit.data.js b/src/components/currency/editForm/Currency.edit.data.js index a10d18351d..f7b3638240 100644 --- a/src/components/currency/editForm/Currency.edit.data.js +++ b/src/components/currency/editForm/Currency.edit.data.js @@ -1,190 +1,197 @@ export default [ - { - key: 'case', - ignore: true - }, - { - type: 'select', - input: true, - weight: 50, - key: 'currency', - label: 'Currency', - tooltip: 'The currency to use in currency formatting. Possible values are (ISO-4217) currency codes.', - defaultValue: 'USD', - dataSrc: 'values', - data: { - values: [ - { label: 'US Dollar (USD)', value: 'USD' }, - { label: 'Euro (EUR)', value: 'EUR' }, - { label: 'Pound Sterling (GBP)', value: 'GBP' }, - { label: 'Australian Dollar (AUD)', value: 'AUD' }, - { label: 'Afghani (AFN)', value: 'AFN' }, - { label: 'Lek (ALL)', value: 'ALL' }, - { label: 'Algerian Dinar (DZD)', value: 'DZD' }, - { label: 'Kwanza (AOA)', value: 'AOA' }, - { label: 'East Caribbean Dollar (XCD)', value: 'XCD' }, - { label: 'Argentine Peso (ARS)', value: 'ARS' }, - { label: 'Armenian Dram (AMD)', value: 'AMD' }, - { label: 'Aruban Florin (AWG)', value: 'AWG' }, - { label: 'Azerbaijan Manat (AZN)', value: 'AZN' }, - { label: 'Bahamian Dollar (BSD)', value: 'BSD' }, - { label: 'Bahraini Dinar (BHD)', value: 'BHD' }, - { label: 'Taka (BDT)', value: 'BDT' }, - { label: 'Barbados Dollar (BBD)', value: 'BBD' }, - { label: 'Belarusian Ruble (BYN)', value: 'BYN' }, - { label: 'Belize Dollar (BZD)', value: 'BZD' }, - { label: 'CFA Franc BCEAO (XOF)', value: 'XOF' }, - { label: 'Bermudian Dollar (BMD)', value: 'BMD' }, - { label: 'Indian Rupee (INR)', value: 'INR' }, - { label: 'Ngultrum (BTN)', value: 'BTN' }, - { label: 'Boliviano (BOB)', value: 'BOB' }, - { label: 'Mvdol (BOV)', value: 'BOV' }, - { label: 'Convertible Mark (BAM)', value: 'BAM' }, - { label: 'Pula (BWP)', value: 'BWP' }, - { label: 'Norwegian Krone (NOK)', value: 'NOK' }, - { label: 'Brazilian Real (BRL)', value: 'BRL' }, - { label: 'Brunei Dollar (BND)', value: 'BND' }, - { label: 'Bulgarian Lev (BGN)', value: 'BGN' }, - { label: 'Burundi Franc (BIF)', value: 'BIF' }, - { label: 'Cabo Verde Escudo (CVE)', value: 'CVE' }, - { label: 'Riel (KHR)', value: 'KHR' }, - { label: 'CFA Franc BEAC (XAF)', value: 'XAF' }, - { label: 'Canadian Dollar (CAD)', value: 'CAD' }, - { label: 'Cayman Islands Dollar (KYD)', value: 'KYD' }, - { label: 'Chilean Peso (CLP)', value: 'CLP' }, - { label: 'Unidad de Fomento (CLF)', value: 'CLF' }, - { label: 'Yuan Renminbi (CNY)', value: 'CNY' }, - { label: 'Colombian Peso (COP)', value: 'COP' }, - { label: 'Unidad de Valor Real (COU)', value: 'COU' }, - { label: 'Comorian Franc (KMF)', value: 'KMF' }, - { label: 'Congolese Franc (CDF)', value: 'CDF' }, - { label: 'New Zealand Dollar (NZD)', value: 'NZD' }, - { label: 'Costa Rican Colon (CRC)', value: 'CRC' }, - { label: 'Kuna (HRK)', value: 'HRK' }, - { label: 'Cuban Peso (CUP)', value: 'CUP' }, - { label: 'Peso Convertible (CUC)', value: 'CUC' }, - { label: 'Netherlands Antillean Guilder (ANG)', value: 'ANG' }, - { label: 'Czech Koruna (CZK)', value: 'CZK' }, - { label: 'Danish Krone (DKK)', value: 'DKK' }, - { label: 'Djibouti Franc (DJF)', value: 'DJF' }, - { label: 'Dominican Peso (DOP)', value: 'DOP' }, - { label: 'Egyptian Pound (EGP)', value: 'EGP' }, - { label: 'El Salvador Colon (SVC)', value: 'SVC' }, - { label: 'Nakfa (ERN)', value: 'ERN' }, - { label: 'Ethiopian Birr (ETB)', value: 'ETB' }, - { label: 'Falkland Islands Pound (FKP)', value: 'FKP' }, - { label: 'Fiji Dollar (FJD)', value: 'FJD' }, - { label: 'CFP Franc (XPF)', value: 'XPF' }, - { label: 'Dalasi (GMD)', value: 'GMD' }, - { label: 'Lari (GEL)', value: 'GEL' }, - { label: 'Ghana Cedi (GHS)', value: 'GHS' }, - { label: 'Gibraltar Pound (GIP)', value: 'GIP' }, - { label: 'Quetzal (GTQ)', value: 'GTQ' }, - { label: 'Guinean Franc (GNF)', value: 'GNF' }, - { label: 'Guyana Dollar (GYD)', value: 'GYD' }, - { label: 'Gourde (HTG)', value: 'HTG' }, - { label: 'Lempira (HNL)', value: 'HNL' }, - { label: 'Hong Kong Dollar (HKD)', value: 'HKD' }, - { label: 'Forint (HUF)', value: 'HUF' }, - { label: 'Iceland Krona (ISK)', value: 'ISK' }, - { label: 'Indian Rupee (INR)', value: 'INR' }, - { label: 'Rupiah (IDR)', value: 'IDR' }, - { label: 'SDR (Special Drawing Right) (XDR)', value: 'XDR' }, - { label: 'Iranian Rial (IRR)', value: 'IRR' }, - { label: 'Iraqi Dinar (IQD)', value: 'IQD' }, - { label: 'New Israeli Sheqel (ILS)', value: 'ILS' }, - { label: 'Jamaican Dollar (JMD)', value: 'JMD' }, - { label: 'Yen (JPY)', value: 'JPY' }, - { label: 'Jordanian Dinar (JOD)', value: 'JOD' }, - { label: 'Tenge (KZT)', value: 'KZT' }, - { label: 'Kenyan Shilling (KES)', value: 'KES' }, - { label: 'North Korean Won (KPW)', value: 'KPW' }, - { label: 'Won (KRW)', value: 'KRW' }, - { label: 'Kuwaiti Dinar (KWD)', value: 'KWD' }, - { label: 'Som (KGS)', value: 'KGS' }, - { label: 'Lao Kip (LAK)', value: 'LAK' }, - { label: 'Lebanese Pound (LBP)', value: 'LBP' }, - { label: 'Loti (LSL)', value: 'LSL' }, - { label: 'Rand (ZAR)', value: 'ZAR' }, - { label: 'Liberian Dollar (LRD)', value: 'LRD' }, - { label: 'Libyan Dinar (LYD)', value: 'LYD' }, - { label: 'Swiss Franc (CHF)', value: 'CHF' }, - { label: 'Pataca (MOP)', value: 'MOP' }, - { label: 'Denar (MKD)', value: 'MKD' }, - { label: 'Malagasy Ariary (MGA)', value: 'MGA' }, - { label: 'Malawi Kwacha (MWK)', value: 'MWK' }, - { label: 'Malaysian Ringgit (MYR)', value: 'MYR' }, - { label: 'Rufiyaa (MVR)', value: 'MVR' }, - { label: 'Ouguiya (MRU)', value: 'MRU' }, - { label: 'Mauritius Rupee (MUR)', value: 'MUR' }, - { label: 'ADB Unit of Account (XUA)', value: 'XUA' }, - { label: 'Mexican Peso (MXN)', value: 'MXN' }, - { label: 'Mexican Unidad de Inversion (UDI) (MXV)', value: 'MXV' }, - { label: 'Moldovan Leu (MDL)', value: 'MDL' }, - { label: 'Tugrik (MNT)', value: 'MNT' }, - { label: 'Moroccan Dirham (MAD)', value: 'MAD' }, - { label: 'Mozambique Metical (MZN)', value: 'MZN' }, - { label: 'Kyat (MMK)', value: 'MMK' }, - { label: 'Namibia Dollar (NAD)', value: 'NAD' }, - { label: 'Nepalese Rupee (NPR)', value: 'NPR' }, - { label: 'Cordoba Oro (NIO)', value: 'NIO' }, - { label: 'Naira (NGN)', value: 'NGN' }, - { label: 'Rial Omani (OMR)', value: 'OMR' }, - { label: 'Pakistan Rupee (PKR)', value: 'PKR' }, - { label: 'Balboa (PAB)', value: 'PAB' }, - { label: 'Kina (PGK)', value: 'PGK' }, - { label: 'Guarani (PYG)', value: 'PYG' }, - { label: 'Sol (PEN)', value: 'PEN' }, - { label: 'Philippine Peso (PHP)', value: 'PHP' }, - { label: 'Zloty (PLN)', value: 'PLN' }, - { label: 'Qatari Rial (QAR)', value: 'QAR' }, - { label: 'Romanian Leu (RON)', value: 'RON' }, - { label: 'Russian Ruble (RUB)', value: 'RUB' }, - { label: 'Rwanda Franc (RWF)', value: 'RWF' }, - { label: 'Saint Helena Pound (SHP)', value: 'SHP' }, - { label: 'Tala (WST)', value: 'WST' }, - { label: 'Dobra (STN)', value: 'STN' }, - { label: 'Saudi Riyal (SAR)', value: 'SAR' }, - { label: 'Serbian Dinar (RSD)', value: 'RSD' }, - { label: 'Seychelles Rupee (SCR)', value: 'SCR' }, - { label: 'Leone (SLL)', value: 'SLL' }, - { label: 'Singapore Dollar (SGD)', value: 'SGD' }, - { label: 'Sucre (XSU)', value: 'XSU' }, - { label: 'Solomon Islands Dollar (SBD)', value: 'SBD' }, - { label: 'Somali Shilling (SOS)', value: 'SOS' }, - { label: 'South Sudanese Pound (SSP)', value: 'SSP' }, - { label: 'Sri Lanka Rupee (LKR)', value: 'LKR' }, - { label: 'Sudanese Pound (SDG)', value: 'SDG' }, - { label: 'Surinam Dollar (SRD)', value: 'SRD' }, - { label: 'Lilangeni (SZL)', value: 'SZL' }, - { label: 'Swedish Krona (SEK)', value: 'SEK' }, - { label: 'WIR Euro (CHE)', value: 'CHE' }, - { label: 'WIR Franc (CHW)', value: 'CHW' }, - { label: 'Syrian Pound (SYP)', value: 'SYP' }, - { label: 'New Taiwan Dollar (TWD)', value: 'TWD' }, - { label: 'Somoni (TJS)', value: 'TJS' }, - { label: 'Tanzanian Shilling (TZS)', value: 'TZS' }, - { label: 'Baht (THB)', value: 'THB' }, - { label: 'Pa’anga (TOP)', value: 'TOP' }, - { label: 'Trinidad and Tobago Dollar (TTD)', value: 'TTD' }, - { label: 'Tunisian Dinar (TND)', value: 'TND' }, - { label: 'Turkish Lira (TRY)', value: 'TRY' }, - { label: 'Turkmenistan New Manat (TMT)', value: 'TMT' }, - { label: 'Uganda Shilling (UGX)', value: 'UGX' }, - { label: 'Hryvnia (UAH)', value: 'UAH' }, - { label: 'UAE Dirham (AED)', value: 'AED' }, - { label: 'US Dollar (Next day) (USN)', value: 'USN' }, - { label: 'Peso Uruguayo (UYU)', value: 'UYU' }, - { label: 'Uruguay Peso en Unidades Indexadas (UYI)', value: 'UYI' }, - { label: 'Unidad Previsional (UYW)', value: 'UYW' }, - { label: 'Uzbekistan Sum (UZS)', value: 'UZS' }, - { label: 'Vatu (VUV)', value: 'VUV' }, - { label: 'Bolívar Soberano (VES)', value: 'VES' }, - { label: 'Dong (VND)', value: 'VND' }, - { label: 'Yemeni Rial (YER)', value: 'YER' }, - { label: 'Zambian Kwacha (ZMW)', value: 'ZMW' }, - { label: 'Zimbabwe Dollar (ZWL),', value: 'ZWL' } - ] - } - } + { + key: 'case', + ignore: true, + }, + { + type: 'select', + input: true, + weight: 50, + key: 'currency', + label: 'Currency', + tooltip: + 'The currency to use in currency formatting. Possible values are (ISO-4217) currency codes.', + defaultValue: 'USD', + dataSrc: 'values', + data: { + values: [ + { label: 'US Dollar (USD)', value: 'USD' }, + { label: 'Euro (EUR)', value: 'EUR' }, + { label: 'Pound Sterling (GBP)', value: 'GBP' }, + { label: 'Australian Dollar (AUD)', value: 'AUD' }, + { label: 'Afghani (AFN)', value: 'AFN' }, + { label: 'Lek (ALL)', value: 'ALL' }, + { label: 'Algerian Dinar (DZD)', value: 'DZD' }, + { label: 'Kwanza (AOA)', value: 'AOA' }, + { label: 'East Caribbean Dollar (XCD)', value: 'XCD' }, + { label: 'Argentine Peso (ARS)', value: 'ARS' }, + { label: 'Armenian Dram (AMD)', value: 'AMD' }, + { label: 'Aruban Florin (AWG)', value: 'AWG' }, + { label: 'Azerbaijan Manat (AZN)', value: 'AZN' }, + { label: 'Bahamian Dollar (BSD)', value: 'BSD' }, + { label: 'Bahraini Dinar (BHD)', value: 'BHD' }, + { label: 'Taka (BDT)', value: 'BDT' }, + { label: 'Barbados Dollar (BBD)', value: 'BBD' }, + { label: 'Belarusian Ruble (BYN)', value: 'BYN' }, + { label: 'Belize Dollar (BZD)', value: 'BZD' }, + { label: 'CFA Franc BCEAO (XOF)', value: 'XOF' }, + { label: 'Bermudian Dollar (BMD)', value: 'BMD' }, + { label: 'Indian Rupee (INR)', value: 'INR' }, + { label: 'Ngultrum (BTN)', value: 'BTN' }, + { label: 'Boliviano (BOB)', value: 'BOB' }, + { label: 'Mvdol (BOV)', value: 'BOV' }, + { label: 'Convertible Mark (BAM)', value: 'BAM' }, + { label: 'Pula (BWP)', value: 'BWP' }, + { label: 'Norwegian Krone (NOK)', value: 'NOK' }, + { label: 'Brazilian Real (BRL)', value: 'BRL' }, + { label: 'Brunei Dollar (BND)', value: 'BND' }, + { label: 'Bulgarian Lev (BGN)', value: 'BGN' }, + { label: 'Burundi Franc (BIF)', value: 'BIF' }, + { label: 'Cabo Verde Escudo (CVE)', value: 'CVE' }, + { label: 'Riel (KHR)', value: 'KHR' }, + { label: 'CFA Franc BEAC (XAF)', value: 'XAF' }, + { label: 'Canadian Dollar (CAD)', value: 'CAD' }, + { label: 'Cayman Islands Dollar (KYD)', value: 'KYD' }, + { label: 'Chilean Peso (CLP)', value: 'CLP' }, + { label: 'Unidad de Fomento (CLF)', value: 'CLF' }, + { label: 'Yuan Renminbi (CNY)', value: 'CNY' }, + { label: 'Colombian Peso (COP)', value: 'COP' }, + { label: 'Unidad de Valor Real (COU)', value: 'COU' }, + { label: 'Comorian Franc (KMF)', value: 'KMF' }, + { label: 'Congolese Franc (CDF)', value: 'CDF' }, + { label: 'New Zealand Dollar (NZD)', value: 'NZD' }, + { label: 'Costa Rican Colon (CRC)', value: 'CRC' }, + { label: 'Kuna (HRK)', value: 'HRK' }, + { label: 'Cuban Peso (CUP)', value: 'CUP' }, + { label: 'Peso Convertible (CUC)', value: 'CUC' }, + { label: 'Netherlands Antillean Guilder (ANG)', value: 'ANG' }, + { label: 'Czech Koruna (CZK)', value: 'CZK' }, + { label: 'Danish Krone (DKK)', value: 'DKK' }, + { label: 'Djibouti Franc (DJF)', value: 'DJF' }, + { label: 'Dominican Peso (DOP)', value: 'DOP' }, + { label: 'Egyptian Pound (EGP)', value: 'EGP' }, + { label: 'El Salvador Colon (SVC)', value: 'SVC' }, + { label: 'Nakfa (ERN)', value: 'ERN' }, + { label: 'Ethiopian Birr (ETB)', value: 'ETB' }, + { label: 'Falkland Islands Pound (FKP)', value: 'FKP' }, + { label: 'Fiji Dollar (FJD)', value: 'FJD' }, + { label: 'CFP Franc (XPF)', value: 'XPF' }, + { label: 'Dalasi (GMD)', value: 'GMD' }, + { label: 'Lari (GEL)', value: 'GEL' }, + { label: 'Ghana Cedi (GHS)', value: 'GHS' }, + { label: 'Gibraltar Pound (GIP)', value: 'GIP' }, + { label: 'Quetzal (GTQ)', value: 'GTQ' }, + { label: 'Guinean Franc (GNF)', value: 'GNF' }, + { label: 'Guyana Dollar (GYD)', value: 'GYD' }, + { label: 'Gourde (HTG)', value: 'HTG' }, + { label: 'Lempira (HNL)', value: 'HNL' }, + { label: 'Hong Kong Dollar (HKD)', value: 'HKD' }, + { label: 'Forint (HUF)', value: 'HUF' }, + { label: 'Iceland Krona (ISK)', value: 'ISK' }, + { label: 'Indian Rupee (INR)', value: 'INR' }, + { label: 'Rupiah (IDR)', value: 'IDR' }, + { label: 'SDR (Special Drawing Right) (XDR)', value: 'XDR' }, + { label: 'Iranian Rial (IRR)', value: 'IRR' }, + { label: 'Iraqi Dinar (IQD)', value: 'IQD' }, + { label: 'New Israeli Sheqel (ILS)', value: 'ILS' }, + { label: 'Jamaican Dollar (JMD)', value: 'JMD' }, + { label: 'Yen (JPY)', value: 'JPY' }, + { label: 'Jordanian Dinar (JOD)', value: 'JOD' }, + { label: 'Tenge (KZT)', value: 'KZT' }, + { label: 'Kenyan Shilling (KES)', value: 'KES' }, + { label: 'North Korean Won (KPW)', value: 'KPW' }, + { label: 'Won (KRW)', value: 'KRW' }, + { label: 'Kuwaiti Dinar (KWD)', value: 'KWD' }, + { label: 'Som (KGS)', value: 'KGS' }, + { label: 'Lao Kip (LAK)', value: 'LAK' }, + { label: 'Lebanese Pound (LBP)', value: 'LBP' }, + { label: 'Loti (LSL)', value: 'LSL' }, + { label: 'Rand (ZAR)', value: 'ZAR' }, + { label: 'Liberian Dollar (LRD)', value: 'LRD' }, + { label: 'Libyan Dinar (LYD)', value: 'LYD' }, + { label: 'Swiss Franc (CHF)', value: 'CHF' }, + { label: 'Pataca (MOP)', value: 'MOP' }, + { label: 'Denar (MKD)', value: 'MKD' }, + { label: 'Malagasy Ariary (MGA)', value: 'MGA' }, + { label: 'Malawi Kwacha (MWK)', value: 'MWK' }, + { label: 'Malaysian Ringgit (MYR)', value: 'MYR' }, + { label: 'Rufiyaa (MVR)', value: 'MVR' }, + { label: 'Ouguiya (MRU)', value: 'MRU' }, + { label: 'Mauritius Rupee (MUR)', value: 'MUR' }, + { label: 'ADB Unit of Account (XUA)', value: 'XUA' }, + { label: 'Mexican Peso (MXN)', value: 'MXN' }, + { + label: 'Mexican Unidad de Inversion (UDI) (MXV)', + value: 'MXV', + }, + { label: 'Moldovan Leu (MDL)', value: 'MDL' }, + { label: 'Tugrik (MNT)', value: 'MNT' }, + { label: 'Moroccan Dirham (MAD)', value: 'MAD' }, + { label: 'Mozambique Metical (MZN)', value: 'MZN' }, + { label: 'Kyat (MMK)', value: 'MMK' }, + { label: 'Namibia Dollar (NAD)', value: 'NAD' }, + { label: 'Nepalese Rupee (NPR)', value: 'NPR' }, + { label: 'Cordoba Oro (NIO)', value: 'NIO' }, + { label: 'Naira (NGN)', value: 'NGN' }, + { label: 'Rial Omani (OMR)', value: 'OMR' }, + { label: 'Pakistan Rupee (PKR)', value: 'PKR' }, + { label: 'Balboa (PAB)', value: 'PAB' }, + { label: 'Kina (PGK)', value: 'PGK' }, + { label: 'Guarani (PYG)', value: 'PYG' }, + { label: 'Sol (PEN)', value: 'PEN' }, + { label: 'Philippine Peso (PHP)', value: 'PHP' }, + { label: 'Zloty (PLN)', value: 'PLN' }, + { label: 'Qatari Rial (QAR)', value: 'QAR' }, + { label: 'Romanian Leu (RON)', value: 'RON' }, + { label: 'Russian Ruble (RUB)', value: 'RUB' }, + { label: 'Rwanda Franc (RWF)', value: 'RWF' }, + { label: 'Saint Helena Pound (SHP)', value: 'SHP' }, + { label: 'Tala (WST)', value: 'WST' }, + { label: 'Dobra (STN)', value: 'STN' }, + { label: 'Saudi Riyal (SAR)', value: 'SAR' }, + { label: 'Serbian Dinar (RSD)', value: 'RSD' }, + { label: 'Seychelles Rupee (SCR)', value: 'SCR' }, + { label: 'Leone (SLL)', value: 'SLL' }, + { label: 'Singapore Dollar (SGD)', value: 'SGD' }, + { label: 'Sucre (XSU)', value: 'XSU' }, + { label: 'Solomon Islands Dollar (SBD)', value: 'SBD' }, + { label: 'Somali Shilling (SOS)', value: 'SOS' }, + { label: 'South Sudanese Pound (SSP)', value: 'SSP' }, + { label: 'Sri Lanka Rupee (LKR)', value: 'LKR' }, + { label: 'Sudanese Pound (SDG)', value: 'SDG' }, + { label: 'Surinam Dollar (SRD)', value: 'SRD' }, + { label: 'Lilangeni (SZL)', value: 'SZL' }, + { label: 'Swedish Krona (SEK)', value: 'SEK' }, + { label: 'WIR Euro (CHE)', value: 'CHE' }, + { label: 'WIR Franc (CHW)', value: 'CHW' }, + { label: 'Syrian Pound (SYP)', value: 'SYP' }, + { label: 'New Taiwan Dollar (TWD)', value: 'TWD' }, + { label: 'Somoni (TJS)', value: 'TJS' }, + { label: 'Tanzanian Shilling (TZS)', value: 'TZS' }, + { label: 'Baht (THB)', value: 'THB' }, + { label: 'Pa’anga (TOP)', value: 'TOP' }, + { label: 'Trinidad and Tobago Dollar (TTD)', value: 'TTD' }, + { label: 'Tunisian Dinar (TND)', value: 'TND' }, + { label: 'Turkish Lira (TRY)', value: 'TRY' }, + { label: 'Turkmenistan New Manat (TMT)', value: 'TMT' }, + { label: 'Uganda Shilling (UGX)', value: 'UGX' }, + { label: 'Hryvnia (UAH)', value: 'UAH' }, + { label: 'UAE Dirham (AED)', value: 'AED' }, + { label: 'US Dollar (Next day) (USN)', value: 'USN' }, + { label: 'Peso Uruguayo (UYU)', value: 'UYU' }, + { + label: 'Uruguay Peso en Unidades Indexadas (UYI)', + value: 'UYI', + }, + { label: 'Unidad Previsional (UYW)', value: 'UYW' }, + { label: 'Uzbekistan Sum (UZS)', value: 'UZS' }, + { label: 'Vatu (VUV)', value: 'VUV' }, + { label: 'Bolívar Soberano (VES)', value: 'VES' }, + { label: 'Dong (VND)', value: 'VND' }, + { label: 'Yemeni Rial (YER)', value: 'YER' }, + { label: 'Zambian Kwacha (ZMW)', value: 'ZMW' }, + { label: 'Zimbabwe Dollar (ZWL),', value: 'ZWL' }, + ], + }, + }, ]; diff --git a/src/components/currency/editForm/Currency.edit.display.js b/src/components/currency/editForm/Currency.edit.display.js index 500d715d85..cb358dde58 100644 --- a/src/components/currency/editForm/Currency.edit.display.js +++ b/src/components/currency/editForm/Currency.edit.display.js @@ -1,38 +1,40 @@ export default [ - { - key: 'inputMask', - ignore: true - }, - { - key: 'allowMultipleMasks', - ignore: true - }, - { - key: 'showWordCount', - ignore: true - }, - { - key: 'showCharCount', - ignore: true - }, - { - key: 'spellcheck', - ignore: true - }, - { - type: 'textfield', - input: true, - weight: 310, - key: 'prefix', - label: 'prefix', - tooltip: 'Specify the prefix symbol after the component (e.g.: USD, EUR)' - }, - { - type: 'textfield', - input: true, - weight: 320, - key: 'suffix', - label: 'suffix', - tooltip: 'Specify the suffix symbol after the component (e.g.: USD, EUR).' - } + { + key: 'inputMask', + ignore: true, + }, + { + key: 'allowMultipleMasks', + ignore: true, + }, + { + key: 'showWordCount', + ignore: true, + }, + { + key: 'showCharCount', + ignore: true, + }, + { + key: 'spellcheck', + ignore: true, + }, + { + type: 'textfield', + input: true, + weight: 310, + key: 'prefix', + label: 'prefix', + tooltip: + 'Specify the prefix symbol after the component (e.g.: USD, EUR)', + }, + { + type: 'textfield', + input: true, + weight: 320, + key: 'suffix', + label: 'suffix', + tooltip: + 'Specify the suffix symbol after the component (e.g.: USD, EUR).', + }, ]; diff --git a/src/components/currency/fixtures/comp1.js b/src/components/currency/fixtures/comp1.js index f94ca6d35f..e9e0dc035e 100644 --- a/src/components/currency/fixtures/comp1.js +++ b/src/components/currency/fixtures/comp1.js @@ -1,31 +1,29 @@ export default { - 'input': true, - 'tableView': true, - 'inputType': 'text', - 'inputMask': '', - 'label': 'Money', - 'key': 'money', - 'placeholder': '', - 'prefix': '$', - 'suffix': 'USD', - 'defaultValue': '', - 'protected': false, - 'persistent': true, - 'clearOnHide': true, - 'validate': { - 'required': false, - 'multiple': '', - 'custom': '' - }, - 'conditional': { - 'show': '', - 'when': null, - 'eq': '' - }, - 'type': 'currency', - 'requireDecimal': true, - 'delimiter': true, - 'tags': [ - - ] + input: true, + tableView: true, + inputType: 'text', + inputMask: '', + label: 'Money', + key: 'money', + placeholder: '', + prefix: '$', + suffix: 'USD', + defaultValue: '', + protected: false, + persistent: true, + clearOnHide: true, + validate: { + required: false, + multiple: '', + custom: '', + }, + conditional: { + show: '', + when: null, + eq: '', + }, + type: 'currency', + requireDecimal: true, + delimiter: true, + tags: [], }; diff --git a/src/components/currency/fixtures/comp2.js b/src/components/currency/fixtures/comp2.js index 0254f68e99..0c50a88751 100644 --- a/src/components/currency/fixtures/comp2.js +++ b/src/components/currency/fixtures/comp2.js @@ -1,18 +1,18 @@ export default { - 'label': 'Currency', - 'mask': false, - 'spellcheck': true, - 'tableView': false, - 'multiple': true, - 'currency': 'USD', - 'inputFormat': 'plain', - 'calculateServer': false, - 'validate': { - 'multiple': true - }, - 'key': 'currency', - 'type': 'currency', - 'input': true, - 'delimiter': true, - 'defaultValue': [null] + label: 'Currency', + mask: false, + spellcheck: true, + tableView: false, + multiple: true, + currency: 'USD', + inputFormat: 'plain', + calculateServer: false, + validate: { + multiple: true, + }, + key: 'currency', + type: 'currency', + input: true, + delimiter: true, + defaultValue: [null], }; diff --git a/src/components/currency/fixtures/comp3.js b/src/components/currency/fixtures/comp3.js index 8a380d8906..4bb00fc222 100644 --- a/src/components/currency/fixtures/comp3.js +++ b/src/components/currency/fixtures/comp3.js @@ -1,12 +1,12 @@ export default { - 'label': 'Currency', - 'mask': false, - 'spellcheck': true, - 'tableView': false, - 'currency': 'USD', - 'inputFormat': 'plain', - 'key': 'currency', - 'type': 'currency', - 'input': true, - 'delimiter': true + label: 'Currency', + mask: false, + spellcheck: true, + tableView: false, + currency: 'USD', + inputFormat: 'plain', + key: 'currency', + type: 'currency', + input: true, + delimiter: true, }; diff --git a/src/components/currency/fixtures/comp4.js b/src/components/currency/fixtures/comp4.js index 44f7245c25..f9438d2ab5 100644 --- a/src/components/currency/fixtures/comp4.js +++ b/src/components/currency/fixtures/comp4.js @@ -1,18 +1,16 @@ export default { - label: 'Currency', - mask: false, - spellcheck: true, - tableView: false, - modalEdit: true, - multiple: true, - currency: 'USD', - inputFormat: 'plain', - truncateMultipleSpaces: false, - key: 'currency', - type: 'currency', - input: true, - delimiter: true, - defaultValue: [ - null - ] + label: 'Currency', + mask: false, + spellcheck: true, + tableView: false, + modalEdit: true, + multiple: true, + currency: 'USD', + inputFormat: 'plain', + truncateMultipleSpaces: false, + key: 'currency', + type: 'currency', + input: true, + delimiter: true, + defaultValue: [null], }; diff --git a/src/components/currency/fixtures/values.js b/src/components/currency/fixtures/values.js index f284c1176f..c0fc090036 100644 --- a/src/components/currency/fixtures/values.js +++ b/src/components/currency/fixtures/values.js @@ -1,6 +1 @@ -export default [ - 1.00, - 2.31, - 1000.00, - 123456.78, -]; +export default [1.0, 2.31, 1000.0, 123456.78]; diff --git a/src/components/datagrid/DataGrid.form.js b/src/components/datagrid/DataGrid.form.js index 6e7218674b..87447c85a4 100644 --- a/src/components/datagrid/DataGrid.form.js +++ b/src/components/datagrid/DataGrid.form.js @@ -3,19 +3,22 @@ import DataGridEditData from './editForm/DataGrid.edit.data'; import DataGridEditDisplay from './editForm/DataGrid.edit.display'; import DataGridEditValidation from './editForm/DataGrid.edit.validation'; -export default function(...extend) { - return Components.baseEditForm([ - { - key: 'display', - components: DataGridEditDisplay - }, - { - key: 'data', - components: DataGridEditData - }, - { - key: 'validation', - components: DataGridEditValidation - }, - ], ...extend); +export default function (...extend) { + return Components.baseEditForm( + [ + { + key: 'display', + components: DataGridEditDisplay, + }, + { + key: 'data', + components: DataGridEditData, + }, + { + key: 'validation', + components: DataGridEditValidation, + }, + ], + ...extend, + ); } diff --git a/src/components/datagrid/DataGrid.js b/src/components/datagrid/DataGrid.js index 5fec671236..94b324c87b 100644 --- a/src/components/datagrid/DataGrid.js +++ b/src/components/datagrid/DataGrid.js @@ -3,784 +3,876 @@ import NestedArrayComponent from '../_classes/nestedarray/NestedArrayComponent'; import { fastCloneDeep, getFocusableElements } from '../../utils/utils'; export default class DataGridComponent extends NestedArrayComponent { - static schema(...extend) { - return NestedArrayComponent.schema({ - label: 'Data Grid', - key: 'dataGrid', - type: 'datagrid', - clearOnHide: true, - input: true, - tree: true, - components: [] - }, ...extend); - } + static schema(...extend) { + return NestedArrayComponent.schema( + { + label: 'Data Grid', + key: 'dataGrid', + type: 'datagrid', + clearOnHide: true, + input: true, + tree: true, + components: [], + }, + ...extend, + ); + } - static get builderInfo() { - return { - title: 'Data Grid', - icon: 'th', - group: 'data', - documentation: '/userguide/form-building/data-components#data-grid', - showPreview: false, - weight: 30, - schema: DataGridComponent.schema() - }; - } - - constructor(...args) { - super(...args); - this.type = 'datagrid'; - this.tabIndex = 0; - } - - init() { - this.components = this.components || []; - - // Add new values based on minLength. - this.rows = []; - this.columns = [...this.component.components]; - - if (this.initRows || !_.isEqual(this.dataValue, this.emptyValue)) { - this.createRows(true); - } - - this.visibleColumns = {}; - this.prevHasAddButton = this.hasAddButton(); - this.checkColumns(); - } - - get dataValue() { - const dataValue = super.dataValue; - if (!dataValue || !Array.isArray(dataValue)) { - return this.emptyValue; - } - return dataValue; - } - - set dataValue(value) { - super.dataValue = value; - } - - get defaultSchema() { - return DataGridComponent.schema(); - } - - get initEmpty() { - return this.component.initEmpty || this.component.noFirstRow; - } - - get initRows() { - return this.builderMode || this.path === 'defaultValue' || !this.initEmpty; - } - - get emptyValue() { - return this.initEmpty ? [] : [{}]; - } - - get addAnotherPosition() { - return _.get(this.component, 'addAnotherPosition', 'bottom'); - } - - get minLength() { - if (this.hasRowGroups()) { - return _.sum(this.getGroupSizes()); - } - else { - return _.get(this.component, 'validate.minLength', 0); - } - } - - get defaultValue() { - const isBuilderMode = this.builderMode; - const isEmptyInit = this.initEmpty; - // Ensure we have one and only one row in builder mode. - if (isBuilderMode || (isEmptyInit && !this.dataValue.length)) { - return isEmptyInit && !isBuilderMode ? [] : [{}]; - } - - const value = super.defaultValue; - let defaultValue; - - if (Array.isArray(value)) { - defaultValue = value; - } - else if (value && (typeof value === 'object')) { - defaultValue = [value]; - } - else { - defaultValue = this.emptyValue; - } - - for (let dIndex = defaultValue.length; dIndex < this.minLength; dIndex++) { - defaultValue.push({}); - } - - return defaultValue; - } - - set disabled(disabled) { - super.disabled = disabled; - _.each(this.refs[`${this.datagridKey}-addRow`], (button) => { - button.disabled = disabled; - }); - _.each(this.refs[`${this.datagridKey}-removeRow`], (button) => { - button.disabled = disabled; - }); - } - - get disabled() { - return super.disabled; - } - - get datagridKey() { - return `datagrid-${this.key}`; - } - - get allowReorder() { - return !this.options.readOnly && _.get(this.component, 'reorder', false); - } - - get iteratableRows() { - return this.rows.map((row, index) => ({ - components: row, - data: this.dataValue[index], - })); - } - - isEmpty(value = this.dataValue) { - const isEmpty = super.isEmpty(value); - - if (this.components?.length) { - return this.components.reduce((isEmpty, component) => { - return isEmpty && component.isEmpty(); - }, true); - } - - return isEmpty; - } - - /** - * Split rows into chunks. - * @param {Number[]} groups - array of numbers where each item is size of group - * @param {Array} rows - rows collection - * @return {Array} - */ - getRowChunks(groups, rows) { - const [, chunks] = groups.reduce( - ([startIndex, acc], size) => { - const endIndex = startIndex + size; - return [endIndex, [...acc, [startIndex, endIndex]]]; - }, [0, []] - ); - return chunks.map(range => _.slice(rows, ...range)); - } - - /** - * Create groups object. - * Each key in object represents index of first row in group. - * @return {Object} - */ - getGroups() { - const groups = _.get(this.component, 'rowGroups', []); - const sizes = _.map(groups, 'numberOfRows').slice(0, -1); - const indexes = sizes.reduce((groupIndexes, size) => { - const last = groupIndexes[groupIndexes.length - 1]; - return groupIndexes.concat(last + size); - }, [0]); - - return groups.reduce( - (gidxs, group, idx) => { + static get builderInfo() { return { - ...gidxs, - [indexes[idx]]: group + title: 'Data Grid', + icon: 'th', + group: 'data', + documentation: '/userguide/form-building/data-components#data-grid', + showPreview: false, + weight: 30, + schema: DataGridComponent.schema(), }; - }, - {} - ); - } - - /** - * Retrun group sizes. - * @return {Number[]} - */ - getGroupSizes() { - return _.map(_.get(this.component, 'rowGroups', []), 'numberOfRows'); - } - - hasRowGroups() { - return _.get(this, 'component.enableRowGroups', false) && !this.builderMode; - } - - totalRowsNumber(groups) { - return _.sum(_.map(groups, 'numberOfRows')); - } - - setStaticValue(n) { - this.dataValue = _.range(n).map(() => ({})); - } - - hasExtraColumn() { - return (this.hasRemoveButtons() || this.canAddColumn); - } - - hasRemoveButtons() { - return !this.builderMode && !this.component.disableAddingRemovingRows && - !this.options.readOnly && - !this.disabled && - this.fullMode && - (this.dataValue.length > _.get(this.component, 'validate.minLength', 0)); - } - - hasTopSubmit() { - return this.hasAddButton() && ['top', 'both'].includes(this.addAnotherPosition); - } - - hasBottomSubmit() { - return this.hasAddButton() && ['bottom', 'both'].includes(this.addAnotherPosition); - } - - get canAddColumn() { - return this.builderMode; - } - - render() { - const columns = this.getColumns(); - let columnExtra = 0; - const hasRemoveButtons = this.hasRemoveButtons(); - if (this.component.reorder) { - columnExtra++; - } - if (hasRemoveButtons) { - columnExtra++; - } - if (this.canAddColumn) { - columnExtra++; - } - const colWidth = Math.floor(12 / (columns.length + columnExtra)); - return super.render(this.renderTemplate('datagrid', { - rows: this.getRows(), - columns: columns, - groups: this.hasRowGroups() ? this.getGroups() : [], - visibleColumns: this.visibleColumns, - hasToggle: _.get(this, 'component.groupToggle', false), - hasHeader: this.hasHeader(), - hasExtraColumn: this.hasExtraColumn(), - hasAddButton: this.hasAddButton(), - hasRemoveButtons, - hasTopSubmit: this.hasTopSubmit(), - hasBottomSubmit: this.hasBottomSubmit(), - hasGroups: this.hasRowGroups(), - numColumns: columns.length + (this.hasExtraColumn() ? 1 : 0), - datagridKey: this.datagridKey, - allowReorder: this.allowReorder, - builder: this.builderMode, - canAddColumn: this.canAddColumn, - tabIndex: this.tabIndex, - placeholder: this.renderTemplate('builderPlaceholder', { - position: this.componentComponents.length, - }), - colWidth: colWidth.toString() - })); - } - - getRows() { - return this.rows.map(row => { - const components = {}; - _.each(row, (col, key) => { - components[key] = col.render(); - }); - return components; - }); - } - - getColumns() { - return this.columns.filter((comp) => { - return (!Object.prototype.hasOwnProperty.call(this.visibleColumns, comp.key) || this.visibleColumns[comp.key]); - }); - } - - hasHeader() { - return this.component.components.reduce((hasHeader, col) => { - // If any of the components has a title and it isn't hidden, display the header. - return hasHeader || ((col.label || col.title) && !col.hideLabel); - }, false); - } - - loadRefs(element, refs) { - super.loadRefs(element, refs); - - if (refs['messageContainer'] === 'single') { - const container = _.last(element.querySelectorAll('[ref=messageContainer]')); - this.refs['messageContainer'] = container || this.refs['messageContainer']; - } - } - - attach(element) { - this.loadRefs(element, { - [`${this.datagridKey}-row`]: 'multiple', - [`${this.datagridKey}-tbody`]: 'single', - [`${this.datagridKey}-addRow`]: 'multiple', - [`${this.datagridKey}-removeRow`]: 'multiple', - [`${this.datagridKey}-group-header`]: 'multiple', - [this.datagridKey]: 'multiple', - 'messageContainer': 'single' - }); - - if (this.allowReorder) { - this.refs[`${this.datagridKey}-row`].forEach((row, index) => { - row.dragInfo = { index }; - }); - - if (this.root.dragulaLib) { - this.dragula = this.root.dragulaLib([this.refs[`${this.datagridKey}-tbody`]], { - moves: (_draggedElement, _oldParent, clickedElement) => { - const clickedElementKey = clickedElement.getAttribute('data-key'); - const oldParentKey = _oldParent.getAttribute('data-key'); - - //Check if the clicked button belongs to that container, if false, it belongs to the nested container - if (oldParentKey === clickedElementKey) { - return clickedElement.classList.contains('formio-drag-button'); + } + + constructor(...args) { + super(...args); + this.type = 'datagrid'; + this.tabIndex = 0; + } + + init() { + this.components = this.components || []; + + // Add new values based on minLength. + this.rows = []; + this.columns = [...this.component.components]; + + if (this.initRows || !_.isEqual(this.dataValue, this.emptyValue)) { + this.createRows(true); + } + + this.visibleColumns = {}; + this.prevHasAddButton = this.hasAddButton(); + this.checkColumns(); + } + + get dataValue() { + const dataValue = super.dataValue; + if (!dataValue || !Array.isArray(dataValue)) { + return this.emptyValue; + } + return dataValue; + } + + set dataValue(value) { + super.dataValue = value; + } + + get defaultSchema() { + return DataGridComponent.schema(); + } + + get initEmpty() { + return this.component.initEmpty || this.component.noFirstRow; + } + + get initRows() { + return ( + this.builderMode || this.path === 'defaultValue' || !this.initEmpty + ); + } + + get emptyValue() { + return this.initEmpty ? [] : [{}]; + } + + get addAnotherPosition() { + return _.get(this.component, 'addAnotherPosition', 'bottom'); + } + + get minLength() { + if (this.hasRowGroups()) { + return _.sum(this.getGroupSizes()); + } else { + return _.get(this.component, 'validate.minLength', 0); + } + } + + get defaultValue() { + const isBuilderMode = this.builderMode; + const isEmptyInit = this.initEmpty; + // Ensure we have one and only one row in builder mode. + if (isBuilderMode || (isEmptyInit && !this.dataValue.length)) { + return isEmptyInit && !isBuilderMode ? [] : [{}]; + } + + const value = super.defaultValue; + let defaultValue; + + if (Array.isArray(value)) { + defaultValue = value; + } else if (value && typeof value === 'object') { + defaultValue = [value]; + } else { + defaultValue = this.emptyValue; + } + + for ( + let dIndex = defaultValue.length; + dIndex < this.minLength; + dIndex++ + ) { + defaultValue.push({}); + } + + return defaultValue; + } + + set disabled(disabled) { + super.disabled = disabled; + _.each(this.refs[`${this.datagridKey}-addRow`], (button) => { + button.disabled = disabled; + }); + _.each(this.refs[`${this.datagridKey}-removeRow`], (button) => { + button.disabled = disabled; + }); + } + + get disabled() { + return super.disabled; + } + + get datagridKey() { + return `datagrid-${this.key}`; + } + + get allowReorder() { + return ( + !this.options.readOnly && _.get(this.component, 'reorder', false) + ); + } + + get iteratableRows() { + return this.rows.map((row, index) => ({ + components: row, + data: this.dataValue[index], + })); + } + + isEmpty(value = this.dataValue) { + const isEmpty = super.isEmpty(value); + + if (this.components?.length) { + return this.components.reduce((isEmpty, component) => { + return isEmpty && component.isEmpty(); + }, true); + } + + return isEmpty; + } + + /** + * Split rows into chunks. + * @param {Number[]} groups - array of numbers where each item is size of group + * @param {Array} rows - rows collection + * @return {Array} + */ + getRowChunks(groups, rows) { + const [, chunks] = groups.reduce( + ([startIndex, acc], size) => { + const endIndex = startIndex + size; + return [endIndex, [...acc, [startIndex, endIndex]]]; + }, + [0, []], + ); + return chunks.map((range) => _.slice(rows, ...range)); + } + + /** + * Create groups object. + * Each key in object represents index of first row in group. + * @return {Object} + */ + getGroups() { + const groups = _.get(this.component, 'rowGroups', []); + const sizes = _.map(groups, 'numberOfRows').slice(0, -1); + const indexes = sizes.reduce( + (groupIndexes, size) => { + const last = groupIndexes[groupIndexes.length - 1]; + return groupIndexes.concat(last + size); + }, + [0], + ); + + return groups.reduce((gidxs, group, idx) => { + return { + ...gidxs, + [indexes[idx]]: group, + }; + }, {}); + } + + /** + * Retrun group sizes. + * @return {Number[]} + */ + getGroupSizes() { + return _.map(_.get(this.component, 'rowGroups', []), 'numberOfRows'); + } + + hasRowGroups() { + return ( + _.get(this, 'component.enableRowGroups', false) && !this.builderMode + ); + } + + totalRowsNumber(groups) { + return _.sum(_.map(groups, 'numberOfRows')); + } + + setStaticValue(n) { + this.dataValue = _.range(n).map(() => ({})); + } + + hasExtraColumn() { + return this.hasRemoveButtons() || this.canAddColumn; + } + + hasRemoveButtons() { + return ( + !this.builderMode && + !this.component.disableAddingRemovingRows && + !this.options.readOnly && + !this.disabled && + this.fullMode && + this.dataValue.length > + _.get(this.component, 'validate.minLength', 0) + ); + } + + hasTopSubmit() { + return ( + this.hasAddButton() && + ['top', 'both'].includes(this.addAnotherPosition) + ); + } + + hasBottomSubmit() { + return ( + this.hasAddButton() && + ['bottom', 'both'].includes(this.addAnotherPosition) + ); + } + + get canAddColumn() { + return this.builderMode; + } + + render() { + const columns = this.getColumns(); + let columnExtra = 0; + const hasRemoveButtons = this.hasRemoveButtons(); + if (this.component.reorder) { + columnExtra++; + } + if (hasRemoveButtons) { + columnExtra++; + } + if (this.canAddColumn) { + columnExtra++; + } + const colWidth = Math.floor(12 / (columns.length + columnExtra)); + return super.render( + this.renderTemplate('datagrid', { + rows: this.getRows(), + columns: columns, + groups: this.hasRowGroups() ? this.getGroups() : [], + visibleColumns: this.visibleColumns, + hasToggle: _.get(this, 'component.groupToggle', false), + hasHeader: this.hasHeader(), + hasExtraColumn: this.hasExtraColumn(), + hasAddButton: this.hasAddButton(), + hasRemoveButtons, + hasTopSubmit: this.hasTopSubmit(), + hasBottomSubmit: this.hasBottomSubmit(), + hasGroups: this.hasRowGroups(), + numColumns: columns.length + (this.hasExtraColumn() ? 1 : 0), + datagridKey: this.datagridKey, + allowReorder: this.allowReorder, + builder: this.builderMode, + canAddColumn: this.canAddColumn, + tabIndex: this.tabIndex, + placeholder: this.renderTemplate('builderPlaceholder', { + position: this.componentComponents.length, + }), + colWidth: colWidth.toString(), + }), + ); + } + + getRows() { + return this.rows.map((row) => { + const components = {}; + _.each(row, (col, key) => { + components[key] = col.render(); + }); + return components; + }); + } + + getColumns() { + return this.columns.filter((comp) => { + return ( + !Object.prototype.hasOwnProperty.call( + this.visibleColumns, + comp.key, + ) || this.visibleColumns[comp.key] + ); + }); + } + + hasHeader() { + return this.component.components.reduce((hasHeader, col) => { + // If any of the components has a title and it isn't hidden, display the header. + return hasHeader || ((col.label || col.title) && !col.hideLabel); + }, false); + } + + loadRefs(element, refs) { + super.loadRefs(element, refs); + + if (refs['messageContainer'] === 'single') { + const container = _.last( + element.querySelectorAll('[ref=messageContainer]'), + ); + this.refs['messageContainer'] = + container || this.refs['messageContainer']; + } + } + + attach(element) { + this.loadRefs(element, { + [`${this.datagridKey}-row`]: 'multiple', + [`${this.datagridKey}-tbody`]: 'single', + [`${this.datagridKey}-addRow`]: 'multiple', + [`${this.datagridKey}-removeRow`]: 'multiple', + [`${this.datagridKey}-group-header`]: 'multiple', + [this.datagridKey]: 'multiple', + messageContainer: 'single', + }); + + if (this.allowReorder) { + this.refs[`${this.datagridKey}-row`].forEach((row, index) => { + row.dragInfo = { index }; + }); + + if (this.root.dragulaLib) { + this.dragula = this.root + .dragulaLib([this.refs[`${this.datagridKey}-tbody`]], { + moves: ( + _draggedElement, + _oldParent, + clickedElement, + ) => { + const clickedElementKey = + clickedElement.getAttribute('data-key'); + const oldParentKey = + _oldParent.getAttribute('data-key'); + + //Check if the clicked button belongs to that container, if false, it belongs to the nested container + if (oldParentKey === clickedElementKey) { + return clickedElement.classList.contains( + 'formio-drag-button', + ); + } + }, + }) + .on('drop', this.onReorder.bind(this)); + + this.dragula.on('cloned', (el, original) => { + if (el && el.children && original && original.children) { + _.each(original.children, (child, index) => { + const styles = getComputedStyle(child, null); + + if (styles.cssText !== '') { + el.children[index].style.cssText = + styles.cssText; + } else { + const cssText = Object.values(styles).reduce( + (css, propertyName) => { + return `${css}${propertyName}:${styles.getPropertyValue( + propertyName, + )};`; + }, + '', + ); + + el.children[index].style.cssText = cssText; + } + }); + } + }); } - } - }).on('drop', this.onReorder.bind(this)); - - this.dragula.on('cloned', (el, original) => { - if (el && el.children && original && original.children) { - _.each(original.children, (child, index) => { - const styles = getComputedStyle(child, null); - - if (styles.cssText !== '') { - el.children[index].style.cssText = styles.cssText; - } - else { - const cssText = Object.values(styles).reduce( - (css, propertyName) => { - return `${css}${propertyName}:${styles.getPropertyValue( - propertyName - )};`; - }, - '' + } + + this.refs[`${this.datagridKey}-addRow`].forEach((addButton) => { + this.addEventListener(addButton, 'click', this.addRow.bind(this)); + }); + + this.refs[`${this.datagridKey}-removeRow`].forEach( + (removeButton, index) => { + this.addEventListener( + removeButton, + 'click', + this.removeRow.bind(this, index), ); + }, + ); - el.children[index].style.cssText = cssText; - } + if (this.hasRowGroups()) { + this.refs.chunks = this.getRowChunks( + this.getGroupSizes(), + this.refs[`${this.datagridKey}-row`], + ); + this.refs[`${this.datagridKey}-group-header`].forEach( + (header, index) => { + this.addEventListener(header, 'click', () => + this.toggleGroup(header, index), + ); + }, + ); + } + + const columns = this.getColumns(); + const rowLength = columns.length; + this.rows.forEach((row, rowIndex) => { + let columnIndex = 0; + columns.forEach((col) => { + this.attachComponents( + this.refs[this.datagridKey][ + rowIndex * rowLength + columnIndex + ], + [this.rows[rowIndex][col.key]], + this.getComponentsContainer(), + ); + columnIndex++; }); - } }); - } + return super.attach(element); } - this.refs[`${this.datagridKey}-addRow`].forEach((addButton) => { - this.addEventListener(addButton, 'click', this.addRow.bind(this)); - }); + getComponentsContainer() { + return this.component.components; + } - this.refs[`${this.datagridKey}-removeRow`].forEach((removeButton, index) => { - this.addEventListener(removeButton, 'click', this.removeRow.bind(this, index)); - }); + onReorder(element, _target, _source, sibling) { + if (!element.dragInfo || (sibling && !sibling.dragInfo)) { + console.warn( + 'There is no Drag Info available for either dragged or sibling element', + ); + return; + } - if (this.hasRowGroups()) { - this.refs.chunks = this.getRowChunks(this.getGroupSizes(), this.refs[`${this.datagridKey}-row`]); - this.refs[`${this.datagridKey}-group-header`].forEach((header, index) => { - this.addEventListener(header, 'click', () => this.toggleGroup(header, index)); - }); + const oldPosition = element.dragInfo.index; + //should drop at next sibling position; no next sibling means drop to last position + const newPosition = sibling + ? sibling.dragInfo.index + : this.dataValue.length; + const movedBelow = newPosition > oldPosition; + const dataValue = fastCloneDeep(this.dataValue); + const draggedRowData = dataValue[oldPosition]; + + //insert element at new position + dataValue.splice(newPosition, 0, draggedRowData); + //remove element from old position (if was moved above, after insertion it's at +1 index) + dataValue.splice(movedBelow ? oldPosition : oldPosition + 1, 1); + + //need to re-build rows to re-calculate indexes and other indexed fields for component instance (like rows for ex.) + this.setValue(dataValue, { isReordered: true }); + this.rebuild(); + } + + focusOnNewRowElement(row) { + Object.keys(row).find((key) => { + const element = row[key].element; + if (element) { + const focusableElements = getFocusableElements(element); + if (focusableElements && focusableElements[0]) { + focusableElements[0].focus(); + return true; + } + } + return false; + }); } - const columns = this.getColumns(); - const rowLength = columns.length; - this.rows.forEach((row, rowIndex) => { - let columnIndex = 0; - columns.forEach((col) => { - this.attachComponents( - this.refs[this.datagridKey][(rowIndex * rowLength) + columnIndex], - [this.rows[rowIndex][col.key]], - this.getComponentsContainer(), - ); - columnIndex++; - }); - }); - return super.attach(element); - } - - getComponentsContainer() { - return this.component.components; - } - - onReorder(element, _target, _source, sibling) { - if (!element.dragInfo || (sibling && !sibling.dragInfo)) { - console.warn('There is no Drag Info available for either dragged or sibling element'); - return; - } - - const oldPosition = element.dragInfo.index; - //should drop at next sibling position; no next sibling means drop to last position - const newPosition = sibling ? sibling.dragInfo.index : this.dataValue.length; - const movedBelow = newPosition > oldPosition; - const dataValue = fastCloneDeep(this.dataValue); - const draggedRowData = dataValue[oldPosition]; - - //insert element at new position - dataValue.splice(newPosition, 0, draggedRowData); - //remove element from old position (if was moved above, after insertion it's at +1 index) - dataValue.splice(movedBelow ? oldPosition : oldPosition + 1, 1); - - //need to re-build rows to re-calculate indexes and other indexed fields for component instance (like rows for ex.) - this.setValue(dataValue, { isReordered: true }); - this.rebuild(); - } - - focusOnNewRowElement(row) { - Object.keys(row).find((key) => { - const element = row[key].element; - if (element) { - const focusableElements = getFocusableElements(element); - if (focusableElements && focusableElements[0]) { - focusableElements[0].focus(); - return true; + addRow() { + const index = this.rows.length; + + // Handle length mismatch between rows and dataValue + if (this.dataValue.length === index) { + this.dataValue.push({}); } - } - return false; - }); - } - - addRow() { - const index = this.rows.length; - - // Handle length mismatch between rows and dataValue - if (this.dataValue.length === index) { - this.dataValue.push({}); - } - - let row; - const dataValue = this.dataValue; - const defaultValue = this.defaultValue; - - if (this.initEmpty && defaultValue[index]) { - row = defaultValue[index]; - dataValue[index] = row; - } - else { - row = dataValue[index]; - } - - this.rows[index] = this.createRowComponents(row, index); - this.emit('dataGridAddRow', { - component: this.component, - row - }); - this.checkConditions(); - this.triggerChange(); - this.redraw().then(() => { - this.focusOnNewRowElement(this.rows[index]); - }); - } - - updateComponentsRowIndex(components, rowIndex) { - components.forEach((component, colIndex) => { - if (component.options?.name) { - const newName = `[${this.key}][${rowIndex}]`; - component.options.name = component.options.name.replace(`[${this.key}][${component.rowIndex}]`, newName); - } - component.rowIndex = rowIndex; - component.row = `${rowIndex}-${colIndex}`; - component.path = this.calculateComponentPath(component); - }); - } - - updateRowsComponents(rowIndex) { - this.rows.slice(rowIndex).forEach((row, index) => { - this.updateComponentsRowIndex(Object.values(row), rowIndex + index); - }); - } - - removeRow(index) { - const makeEmpty = index === 0 && this.rows.length === 1; - const flags = { isReordered: !makeEmpty, resetValue: makeEmpty }; - this.splice(index, flags); - this.emit('dataGridDeleteRow', { index }); - const [row] = this.rows.splice(index, 1); - this.removeRowComponents(row); - this.updateRowsComponents(index); - this.setValue(this.dataValue, flags); - this.redraw(); - } - - removeRowComponents(row) { - _.each(row, (component) => this.removeComponent(component)); - } - - getRowValues() { - return this.dataValue; - } - - setRowComponentsData(rowIndex, rowData) { - _.each(this.rows[rowIndex], (component) => { - component.data = rowData; - }); - } - - createRows(init, rebuild) { - let added = false; - const rowValues = this.getRowValues(); - // Create any missing rows. - rowValues.forEach((row, index) => { - if (!rebuild && this.rows[index]) { - this.setRowComponentsData(index, row); - } - else { - if (this.rows[index]) { - this.removeRowComponents(this.rows[index]); + + let row; + const dataValue = this.dataValue; + const defaultValue = this.defaultValue; + + if (this.initEmpty && defaultValue[index]) { + row = defaultValue[index]; + dataValue[index] = row; + } else { + row = dataValue[index]; } + this.rows[index] = this.createRowComponents(row, index); - added = true; - } - }); - // Delete any extra rows. - const removedRows = this.rows.splice(rowValues.length); - const removed = !!removedRows.length; - // Delete components of extra rows (to make sure that this.components contain only components of exisiting rows) - if (removed) { - removedRows.forEach(row => this.removeRowComponents(row)); - } - - if (!init && (added || removed)) { - this.redraw(); - } - return added; - } - - createRowComponents(row, rowIndex) { - const components = {}; - this.tabIndex = 0; - this.component.components.map((col, colIndex) => { - const options = _.clone(this.options); - options.name += `[${rowIndex}]`; - options.row = `${rowIndex}-${colIndex}`; - - let columnComponent; - - if (this.builderMode) { - col.id = col.id + rowIndex; - columnComponent = col; - } - else { - columnComponent = { ...col, id: (col.id + rowIndex) }; - } - - const component = this.createComponent(columnComponent, options, row); - component.parentDisabled = !!this.disabled; - component.rowIndex = rowIndex; - component.inDataGrid = true; - if ( - columnComponent.tabindex && - parseInt(columnComponent.tabindex) > this.tabIndex - ) { - this.tabIndex = parseInt(columnComponent.tabindex); - } - components[col.key] = component; - }); - return components; - } - - /** - * Checks the validity of this datagrid. - * - * @param data - * @param dirty - * @return {*} - */ - checkValidity(data, dirty, row, silentCheck) { - data = data || this.rootValue; - row = row || this.data; - - if (!this.checkCondition(row, data)) { - this.setCustomValidity(''); - return true; - } - - if (!this.checkComponentValidity(data, dirty, row, { silentCheck })) { - return false; - } - - const isValid = this.checkRows('checkValidity', data, dirty, true, silentCheck); - - this.checkModal(isValid, dirty); - - return isValid; - } - - checkColumns(data, flags = {}) { - data = data || this.rootValue; - let show = false; - - if (!this.rows || !this.rows.length) { - return { rebuild: false, show: false }; - } - - if (this.builderMode) { - return { rebuild: false, show: true }; - } - - const visibility = {}; - - let logicRebuild = false; - - const dataValue = this.dataValue; - this.rows.forEach((row, rowIndex) => { - _.each(row, (col, key) => { - if (col && (typeof col.checkConditions === 'function')) { - const firstRowCheck = visibility[key] === undefined; - visibility[key] = !!visibility[key] || - (col.checkConditions(data, flags, dataValue[rowIndex]) && col.type !== 'hidden'); - - if (col.component.logic && firstRowCheck) { - const compIndex = _.findIndex(this.columns, ['key', key]); - const equalColumns = _.isEqualWith(this.columns[compIndex], col.component, (col1, col2, key) => { - // Don't compare columns by their auto-generated ids. - if (key === 'id') { - return true; - } + this.emit('dataGridAddRow', { + component: this.component, + row, + }); + this.checkConditions(); + this.triggerChange(); + this.redraw().then(() => { + this.focusOnNewRowElement(this.rows[index]); + }); + } + + updateComponentsRowIndex(components, rowIndex) { + components.forEach((component, colIndex) => { + if (component.options?.name) { + const newName = `[${this.key}][${rowIndex}]`; + component.options.name = component.options.name.replace( + `[${this.key}][${component.rowIndex}]`, + newName, + ); + } + component.rowIndex = rowIndex; + component.row = `${rowIndex}-${colIndex}`; + component.path = this.calculateComponentPath(component); + }); + } + + updateRowsComponents(rowIndex) { + this.rows.slice(rowIndex).forEach((row, index) => { + this.updateComponentsRowIndex(Object.values(row), rowIndex + index); + }); + } + + removeRow(index) { + const makeEmpty = index === 0 && this.rows.length === 1; + const flags = { isReordered: !makeEmpty, resetValue: makeEmpty }; + this.splice(index, flags); + this.emit('dataGridDeleteRow', { index }); + const [row] = this.rows.splice(index, 1); + this.removeRowComponents(row); + this.updateRowsComponents(index); + this.setValue(this.dataValue, flags); + this.redraw(); + } + + removeRowComponents(row) { + _.each(row, (component) => this.removeComponent(component)); + } + + getRowValues() { + return this.dataValue; + } + + setRowComponentsData(rowIndex, rowData) { + _.each(this.rows[rowIndex], (component) => { + component.data = rowData; + }); + } + + createRows(init, rebuild) { + let added = false; + const rowValues = this.getRowValues(); + // Create any missing rows. + rowValues.forEach((row, index) => { + if (!rebuild && this.rows[index]) { + this.setRowComponentsData(index, row); + } else { + if (this.rows[index]) { + this.removeRowComponents(this.rows[index]); + } + this.rows[index] = this.createRowComponents(row, index); + added = true; + } + }); + // Delete any extra rows. + const removedRows = this.rows.splice(rowValues.length); + const removed = !!removedRows.length; + // Delete components of extra rows (to make sure that this.components contain only components of exisiting rows) + if (removed) { + removedRows.forEach((row) => this.removeRowComponents(row)); + } + + if (!init && (added || removed)) { + this.redraw(); + } + return added; + } + + createRowComponents(row, rowIndex) { + const components = {}; + this.tabIndex = 0; + this.component.components.map((col, colIndex) => { + const options = _.clone(this.options); + options.name += `[${rowIndex}]`; + options.row = `${rowIndex}-${colIndex}`; + + let columnComponent; + + if (this.builderMode) { + col.id = col.id + rowIndex; + columnComponent = col; + } else { + columnComponent = { ...col, id: col.id + rowIndex }; + } + + const component = this.createComponent( + columnComponent, + options, + row, + ); + component.parentDisabled = !!this.disabled; + component.rowIndex = rowIndex; + component.inDataGrid = true; + if ( + columnComponent.tabindex && + parseInt(columnComponent.tabindex) > this.tabIndex + ) { + this.tabIndex = parseInt(columnComponent.tabindex); + } + components[col.key] = component; + }); + return components; + } + + /** + * Checks the validity of this datagrid. + * + * @param data + * @param dirty + * @return {*} + */ + checkValidity(data, dirty, row, silentCheck) { + data = data || this.rootValue; + row = row || this.data; + + if (!this.checkCondition(row, data)) { + this.setCustomValidity(''); + return true; + } + + if (!this.checkComponentValidity(data, dirty, row, { silentCheck })) { + return false; + } + + const isValid = this.checkRows( + 'checkValidity', + data, + dirty, + true, + silentCheck, + ); + + this.checkModal(isValid, dirty); + + return isValid; + } + + checkColumns(data, flags = {}) { + data = data || this.rootValue; + let show = false; + + if (!this.rows || !this.rows.length) { + return { rebuild: false, show: false }; + } + + if (this.builderMode) { + return { rebuild: false, show: true }; + } + + const visibility = {}; + + let logicRebuild = false; + + const dataValue = this.dataValue; + this.rows.forEach((row, rowIndex) => { + _.each(row, (col, key) => { + if (col && typeof col.checkConditions === 'function') { + const firstRowCheck = visibility[key] === undefined; + visibility[key] = + !!visibility[key] || + (col.checkConditions( + data, + flags, + dataValue[rowIndex], + ) && + col.type !== 'hidden'); + + if (col.component.logic && firstRowCheck) { + const compIndex = _.findIndex(this.columns, [ + 'key', + key, + ]); + const equalColumns = _.isEqualWith( + this.columns[compIndex], + col.component, + (col1, col2, key) => { + // Don't compare columns by their auto-generated ids. + if (key === 'id') { + return true; + } + }, + ); + + if (!equalColumns) { + logicRebuild = true; + this.columns[compIndex] = col.component; + } + } + } }); + }); + const rebuild = + !_.isEqual(visibility, this.visibleColumns) || logicRebuild; + _.each(visibility, (col) => { + show |= col; + }); + + this.visibleColumns = visibility; + return { rebuild, show }; + } + + checkComponentConditions(data, flags, row) { + const isVisible = this.visible; + // If table isn't visible, don't bother calculating columns. + if (!super.checkComponentConditions(data, flags, row)) { + return false; + } + + const { rebuild, show } = this.checkColumns(data, flags); + // Check if a rebuild is needed or the visibility changes. + if (rebuild || !isVisible) { + this.createRows(false, rebuild); + } + + // Return if this table should show. + return show; + } - if (!equalColumns) { - logicRebuild = true; - this.columns[compIndex] = col.component; + setValue(value, flags = {}) { + if (!value) { + this.dataValue = this.defaultValue; + this.createRows(); + return false; + } + if (!Array.isArray(value)) { + if (typeof value === 'object') { + value = [value]; + } else { + this.createRows(); + value = [{}]; } - } } - }); - }); - const rebuild = !_.isEqual(visibility, this.visibleColumns) || logicRebuild; - _.each(visibility, (col) => { - show |= col; - }); - - this.visibleColumns = visibility; - return { rebuild, show }; - } - - checkComponentConditions(data, flags, row) { - const isVisible = this.visible; - // If table isn't visible, don't bother calculating columns. - if (!super.checkComponentConditions(data, flags, row)) { - return false; - } - - const { rebuild, show } = this.checkColumns(data, flags); - // Check if a rebuild is needed or the visibility changes. - if (rebuild || !isVisible) { - this.createRows(false, rebuild); - } - - // Return if this table should show. - return show; - } - - setValue(value, flags = {}) { - if (!value) { - this.dataValue = this.defaultValue; - this.createRows(); - return false; - } - if (!Array.isArray(value)) { - if (typeof value === 'object') { - value = [value]; - } - else { - this.createRows(); - value = [{}]; - } - } - - // Make sure we always have at least one row. - // NOTE: Removing this will break "Public Configurations" in portal. ;) - if (value && !value.length && !this.initEmpty) { - value.push({}); - } - const isSettingSubmission = flags.fromSubmission && !_.isEqual(value, this.emptyValue); - const changed = this.hasChanged(value, this.dataValue); - - this.dataValue = value; - - if (this.initRows || isSettingSubmission || - (Array.isArray(this.dataValue) && this.dataValue.length !== this.rows.length)) { - if (!this.createRows() && changed) { - this.redraw(); - } - } - - if (this.componentModal && isSettingSubmission) { - this.componentModal.setValue(value); - } - - this.rows.forEach((row, rowIndex) => { - if (value.length <= rowIndex) { - return; - } - _.each(row, (col) => { - col.rowIndex = rowIndex; - this.setNestedValue(col, value[rowIndex], flags); - }); - }); - - this.updateOnChange(flags, changed); - return changed; - } - - restoreComponentsContext() { - this.rows.forEach((row, index) => _.forIn(row, (component) => component.data = this.dataValue[index])); - } - - getComponent(path, fn) { - path = Array.isArray(path) ? path : [path]; - const [key, ...remainingPath] = path; - let result = []; - if (_.isNumber(key) && remainingPath.length) { - const compKey = remainingPath.pop(); - result = this.rows[key][compKey]; - // If the component is inside a Layout Component, try to find it among all the row's components - if (!result) { - Object.entries(this.rows[key]).forEach(([, comp]) => { - if ('getComponent' in comp) { - const possibleResult = comp.getComponent([compKey], fn); - if (possibleResult) { - result = possibleResult; + + // Make sure we always have at least one row. + // NOTE: Removing this will break "Public Configurations" in portal. ;) + if (value && !value.length && !this.initEmpty) { + value.push({}); + } + const isSettingSubmission = + flags.fromSubmission && !_.isEqual(value, this.emptyValue); + const changed = this.hasChanged(value, this.dataValue); + + this.dataValue = value; + + if ( + this.initRows || + isSettingSubmission || + (Array.isArray(this.dataValue) && + this.dataValue.length !== this.rows.length) + ) { + if (!this.createRows() && changed) { + this.redraw(); } - } + } + + if (this.componentModal && isSettingSubmission) { + this.componentModal.setValue(value); + } + + this.rows.forEach((row, rowIndex) => { + if (value.length <= rowIndex) { + return; + } + _.each(row, (col) => { + col.rowIndex = rowIndex; + this.setNestedValue(col, value[rowIndex], flags); + }); }); - } - if (result && _.isFunction(fn)) { - fn(result, this.getComponents()); - } - if (remainingPath.length && 'getComponent' in result) { - return result.getComponent(remainingPath, fn); - } - return result; - } - if (!_.isString(key)) { - return result; - } - - this.everyComponent((component, components) => { - if (component.component.key === key) { - let comp = component; - if (remainingPath.length > 0 && 'getComponent' in component) { - comp = component.getComponent(remainingPath, fn); + + this.updateOnChange(flags, changed); + return changed; + } + + restoreComponentsContext() { + this.rows.forEach((row, index) => + _.forIn( + row, + (component) => (component.data = this.dataValue[index]), + ), + ); + } + + getComponent(path, fn) { + path = Array.isArray(path) ? path : [path]; + const [key, ...remainingPath] = path; + let result = []; + if (_.isNumber(key) && remainingPath.length) { + const compKey = remainingPath.pop(); + result = this.rows[key][compKey]; + // If the component is inside a Layout Component, try to find it among all the row's components + if (!result) { + Object.entries(this.rows[key]).forEach(([, comp]) => { + if ('getComponent' in comp) { + const possibleResult = comp.getComponent([compKey], fn); + if (possibleResult) { + result = possibleResult; + } + } + }); + } + if (result && _.isFunction(fn)) { + fn(result, this.getComponents()); + } + if (remainingPath.length && 'getComponent' in result) { + return result.getComponent(remainingPath, fn); + } + return result; } - else if (fn) { - fn(component, components); + if (!_.isString(key)) { + return result; } - result = result.concat(comp); - } - }); + this.everyComponent((component, components) => { + if (component.component.key === key) { + let comp = component; + if (remainingPath.length > 0 && 'getComponent' in component) { + comp = component.getComponent(remainingPath, fn); + } else if (fn) { + fn(component, components); + } - return result.length > 0 ? result : null; - } + result = result.concat(comp); + } + }); + + return result.length > 0 ? result : null; + } - toggleGroup(element, index) { - element.classList.toggle('collapsed'); - _.each(this.refs.chunks[index], row => { - row.classList.toggle('hidden'); - }); - } + toggleGroup(element, index) { + element.classList.toggle('collapsed'); + _.each(this.refs.chunks[index], (row) => { + row.classList.toggle('hidden'); + }); + } } diff --git a/src/components/datagrid/DataGrid.unit.js b/src/components/datagrid/DataGrid.unit.js index 0d9857950c..58cf92fb76 100644 --- a/src/components/datagrid/DataGrid.unit.js +++ b/src/components/datagrid/DataGrid.unit.js @@ -7,702 +7,889 @@ import DataGridComponent from './DataGrid'; import { Formio } from '../../Formio'; import { - comp1, - comp2, - comp3, - comp4, - comp5, - comp6, - comp7, - comp8, - withDefValue, - withRowGroupsAndDefValue, - modalWithRequiredFields, - withConditionalFieldsAndValidations, - withLogic, - withCollapsibleRowGroups, - withAllowCalculateOverride, - twoWithAllowCalculatedOverride, + comp1, + comp2, + comp3, + comp4, + comp5, + comp6, + comp7, + comp8, + withDefValue, + withRowGroupsAndDefValue, + modalWithRequiredFields, + withConditionalFieldsAndValidations, + withLogic, + withCollapsibleRowGroups, + withAllowCalculateOverride, + twoWithAllowCalculatedOverride, } from './fixtures'; -describe('DataGrid Component', function() { - it('Test modal edit confirmation dialog', function(done) { - Harness.testCreate(DataGridComponent, comp5).then((component) => { - component.componentModal.openModal(); - const fakeEvent = { - preventDefault: () => {} - }; - component.componentModal.showDialogListener(fakeEvent); - assert.equal(component.componentModal.isOpened, false, 'Should be closed without confirmation dialog since value was not changed'); - setTimeout(() => { - component.componentModal.openModal(); - Harness.setInputValue(component, 'data[dataGrid][0][textField]', 'My Text'); - setTimeout(() => { - component.componentModal.showDialogListener(fakeEvent); - assert.equal(component.componentModal.isValueChanged(), true, 'Should return true since value was modified'); - assert.equal(component.componentModal.isOpened, true, 'Should stay opened and wait until user confirm closing without changes saving'); - assert(component.componentModal.dialog, 'Should open confirmation dialog'); - component.componentModal.closeDialog(fakeEvent); - component.destroy(); - done(); - }, 100); - }, 100); - }).catch(done); - }); - - it(`Should show alert message in modal edit, when clicking on modal overlay and value was changed, - and clear values when pushing 'yes, delete it' in alert container`, function(done) { - Harness.testCreate(DataGridComponent, comp4).then((component) => { - const hiddenModalWindow = component.element.querySelector('.component-rendering-hidden'); - assert.equal(!!hiddenModalWindow, true); - - const clickEvent = new Event('click'); - const openModalElement = component.refs.openModal; - //open modal edit window - openModalElement.dispatchEvent(clickEvent); - - setTimeout(() => { - assert.equal(!!component.element.querySelector('.component-rendering-hidden'), false); - - const inputEvent = new Event('input'); - const dataGridInputField = component.element.querySelector('[name="data[dataGrid][0][number]"]'); - - dataGridInputField.value = 55555; - //input value in dataGrid field inside modal edit window - dataGridInputField.dispatchEvent(inputEvent); - - setTimeout(() => { - assert.equal(component.element.querySelector('[name="data[dataGrid][0][number]"]').value, '55555'); - - const clickEvent = new Event('click'); - const modalOverlay = component.refs.modalOverlay; - //click outside modal edit window - modalOverlay.dispatchEvent(clickEvent); - - setTimeout(() => { - assert.equal(!!component.componentModal.dialog, true); +describe('DataGrid Component', function () { + it('Test modal edit confirmation dialog', function (done) { + Harness.testCreate(DataGridComponent, comp5) + .then((component) => { + component.componentModal.openModal(); + const fakeEvent = { + preventDefault: () => {}, + }; + component.componentModal.showDialogListener(fakeEvent); + assert.equal( + component.componentModal.isOpened, + false, + 'Should be closed without confirmation dialog since value was not changed', + ); + setTimeout(() => { + component.componentModal.openModal(); + Harness.setInputValue( + component, + 'data[dataGrid][0][textField]', + 'My Text', + ); + setTimeout(() => { + component.componentModal.showDialogListener(fakeEvent); + assert.equal( + component.componentModal.isValueChanged(), + true, + 'Should return true since value was modified', + ); + assert.equal( + component.componentModal.isOpened, + true, + 'Should stay opened and wait until user confirm closing without changes saving', + ); + assert( + component.componentModal.dialog, + 'Should open confirmation dialog', + ); + component.componentModal.closeDialog(fakeEvent); + component.destroy(); + done(); + }, 100); + }, 100); + }) + .catch(done); + }); + + it(`Should show alert message in modal edit, when clicking on modal overlay and value was changed, + and clear values when pushing 'yes, delete it' in alert container`, function (done) { + Harness.testCreate(DataGridComponent, comp4).then((component) => { + const hiddenModalWindow = component.element.querySelector( + '.component-rendering-hidden', + ); + assert.equal(!!hiddenModalWindow, true); const clickEvent = new Event('click'); - const btnForCleaningValues = document.querySelector('[ref="dialogYesButton"]'); - //click on 'yes, delete it' button inside alert window - btnForCleaningValues.dispatchEvent(clickEvent); + const openModalElement = component.refs.openModal; + //open modal edit window + openModalElement.dispatchEvent(clickEvent); setTimeout(() => { - const clickEvent = new Event('click'); - const openModalElement = component.refs.openModal; - //open edit modal window again - openModalElement.dispatchEvent(clickEvent); + assert.equal( + !!component.element.querySelector( + '.component-rendering-hidden', + ), + false, + ); - setTimeout(() => { - assert.equal( component.element.querySelector('[name="data[dataGrid][0][number]"]').value, ''); - done(); - }, 350); - }, 300); - }, 250); - }, 200); - }, 150); - }); - }); + const inputEvent = new Event('input'); + const dataGridInputField = component.element.querySelector( + '[name="data[dataGrid][0][number]"]', + ); - it('Should build a data grid component', function() { - return Harness.testCreate(DataGridComponent, comp1).then((component) => { - Harness.testElements(component, 'input[type="text"]', 3); - }); - }); - - it('Should build a data grid component with formio-component-datagrid class property', function(done) { - Harness.testCreate(DataGridComponent, comp6).then((component) => { - const element = component.element.component.components[0].element; - setTimeout(() => { - assert.deepEqual(element.className.includes('formio-component-datagrid'), true); - done(); - }, 200); - }, done) - .catch(done); - }); - - it('Should not skip validation on input nested components', function(done) { - Harness.testCreate(DataGridComponent, comp1) - .then(cmp => { - expect(cmp.shouldSkipValidation()).to.be.false; - done(); - }, done) - .catch(done); - }); - - it('Should get and set values within the grid.', function() { - return Harness.testCreate(DataGridComponent, comp1).then((component) => { - Harness.testSetGet(component, [ - { - make: 'Jeep', - model: 'Wrangler', - year: 1997 - }, - { - make: 'Chevy', - model: 'Tahoe', - year: 2014 - } - ]); + dataGridInputField.value = 55555; + //input value in dataGrid field inside modal edit window + dataGridInputField.dispatchEvent(inputEvent); + + setTimeout(() => { + assert.equal( + component.element.querySelector( + '[name="data[dataGrid][0][number]"]', + ).value, + '55555', + ); + + const clickEvent = new Event('click'); + const modalOverlay = component.refs.modalOverlay; + //click outside modal edit window + modalOverlay.dispatchEvent(clickEvent); + + setTimeout(() => { + assert.equal(!!component.componentModal.dialog, true); + + const clickEvent = new Event('click'); + const btnForCleaningValues = document.querySelector( + '[ref="dialogYesButton"]', + ); + //click on 'yes, delete it' button inside alert window + btnForCleaningValues.dispatchEvent(clickEvent); + + setTimeout(() => { + const clickEvent = new Event('click'); + const openModalElement = component.refs.openModal; + //open edit modal window again + openModalElement.dispatchEvent(clickEvent); + + setTimeout(() => { + assert.equal( + component.element.querySelector( + '[name="data[dataGrid][0][number]"]', + ).value, + '', + ); + done(); + }, 350); + }, 300); + }, 250); + }, 200); + }, 150); + }); }); - }); - - it('Should be able to add another row.', function() { - return Harness.testCreate(DataGridComponent, comp1).then((component) => { - Harness.testSetGet(component, [ - { - make: 'Jeep', - model: 'Wrangler', - year: 1997 - } - ]); - component.addRow(); - assert.deepEqual(component.getValue(), [ - { - make: 'Jeep', - model: 'Wrangler', - year: 1997 - }, - { - make: '', - model: '', - } - ]); + + it('Should build a data grid component', function () { + return Harness.testCreate(DataGridComponent, comp1).then( + (component) => { + Harness.testElements(component, 'input[type="text"]', 3); + }, + ); }); - }); - - it('Should allow provide default value', function(done) { - try { - Harness.testCreate(DataGridComponent, withDefValue) - .then((datagrid) => { - expect(datagrid.getValue()).to.deep.equal([ - { name: 'Alex', age: 1 }, - { name: 'Bob', age: 2 }, - { name: 'Conny', age: 3 } - ]); - done(); - }, done) - .catch(done); - } - catch (err) { - done(err); - } - }); - - it('Should allow provide default value in row-groups model', function(done) { - try { - Harness.testCreate(DataGridComponent, withRowGroupsAndDefValue) - .then((datagrid) => { - expect(datagrid.getValue()).to.deep.equal([ - { name: 'Alex', age: 1 }, - { name: 'Bob', age: 2 }, - { name: 'Conny', age: 3 }, - { name: '' }, - { name: '' } - ]); - done(); - }, done) - .catch(done); - } - catch (err) { - done(err); - } - }); - - it('Should not cause setValue loops when logic within hidden component is set', function(done) { - Formio.createForm(document.createElement('div'), withLogic) - .then((form) => { - const datagrid = form.getComponent('dataGrid'); - const spyFunc = sinon.spy(datagrid, 'checkComponentConditions'); - const textField = form.getComponent('escalationId'); - const select = form.getComponent('teamName'); - - textField.component.hidden = true; - select.setValue('preRiskAnalysis', { modified: true }); - - setTimeout(() => { - expect(spyFunc.callCount).to.be.lessThan(4); - done(); - }, 1500); - }); - }); - - it('Should collapse group rows on group header click', function(done) { - Formio.createForm(document.createElement('div'), withCollapsibleRowGroups) - .then((form) => { - const groupHeadersRefName= 'datagrid-dataGrid-group-header'; - const datagrid = form.getComponent('dataGrid'); - assert.equal(datagrid.refs[groupHeadersRefName][0]?.classList?.contains('collapsed'), false); - assert.equal(datagrid.refs.chunks[0][0].classList?.contains('hidden'), false); - assert.equal(datagrid.refs.chunks[0][1].classList?.contains('hidden'), false); - - const clickEvent = new Event('click'); - datagrid.refs[groupHeadersRefName][0].dispatchEvent(clickEvent); - setTimeout(() => { - const collapedGroupRows = datagrid.refs.chunks[0] || []; - assert.equal(datagrid.refs[groupHeadersRefName][0]?.classList?.contains('collapsed'), true); - assert.equal(collapedGroupRows[0]?.classList?.contains('hidden'), true); - assert.equal(collapedGroupRows[1]?.classList?.contains('hidden'), true); - done(); - }, 300); - }); - }); - - describe('get minLength', function() { - it('should return minimal number of required rows', function() { - const EIDV = 'Invalid default value'; - const EDFC = 'Differ from configured value'; - const EDFG = 'Differ from number of rows in groups'; - const base = { type: 'datagrid', key: 'testkey' }; - let schema = _.cloneDeep(base); - let datagrid = new DataGridComponent(schema, {}); - - expect(datagrid.minLength, EIDV).to.equal(0); - - schema = Object.assign(_.cloneDeep(base), { validate: { minLength: 5 } }); - datagrid = new DataGridComponent(schema, {}); - expect(datagrid.minLength, EDFC).to.equal(5); - - schema = Object.assign(_.cloneDeep(base), { - enableRowGroups: true, - rowGroups: [ - { label: 'H1', numberOfRows: 1 }, - { label: 'B2', numberOfRows: 3 }, - { label: 'C3', numberOfRows: 3 }, - { label: 'M4', numberOfRows: 2 } - ] - }); - datagrid = new DataGridComponent(schema, {}); - expect(datagrid.minLength, EDFG).to.equal(9); - - schema = Object.assign(_.cloneDeep(base), { - validate: { minLength: 5 }, - enableRowGroups: true, - rowGroups: [ - { label: 'H1', numberOfRows: 1 }, - { label: 'B2', numberOfRows: 3 }, - { label: 'C3', numberOfRows: 3 }, - { label: 'M4', numberOfRows: 2 } - ] - }); - datagrid = new DataGridComponent(schema, {}); - if (datagrid.minLength === 5) { - expect.fail('Number of row should take precedence over config'); - } - else { - expect(datagrid.minLength, EDFG).to.equal(9); - } + + it('Should build a data grid component with formio-component-datagrid class property', function (done) { + Harness.testCreate(DataGridComponent, comp6) + .then((component) => { + const element = + component.element.component.components[0].element; + setTimeout(() => { + assert.deepEqual( + element.className.includes('formio-component-datagrid'), + true, + ); + done(); + }, 200); + }, done) + .catch(done); }); - }); - describe('getGroupSizes', function() { - it('should return array of numbers representing group sizes', function() { - const { getGroupSizes } = DataGridComponent.prototype; - let self = { component: {} }; + it('Should not skip validation on input nested components', function (done) { + Harness.testCreate(DataGridComponent, comp1) + .then((cmp) => { + expect(cmp.shouldSkipValidation()).to.be.false; + done(); + }, done) + .catch(done); + }); - expect(getGroupSizes.call(self)).to.deep.equal([]); + it('Should get and set values within the grid.', function () { + return Harness.testCreate(DataGridComponent, comp1).then( + (component) => { + Harness.testSetGet(component, [ + { + make: 'Jeep', + model: 'Wrangler', + year: 1997, + }, + { + make: 'Chevy', + model: 'Tahoe', + year: 2014, + }, + ]); + }, + ); + }); - self = { component: { - rowGroups: [{ numberOfRows: 1 }] - } }; + it('Should be able to add another row.', function () { + return Harness.testCreate(DataGridComponent, comp1).then( + (component) => { + Harness.testSetGet(component, [ + { + make: 'Jeep', + model: 'Wrangler', + year: 1997, + }, + ]); + component.addRow(); + assert.deepEqual(component.getValue(), [ + { + make: 'Jeep', + model: 'Wrangler', + year: 1997, + }, + { + make: '', + model: '', + }, + ]); + }, + ); + }); - expect(getGroupSizes.call(self)).to.deep.equal([1]); + it('Should allow provide default value', function (done) { + try { + Harness.testCreate(DataGridComponent, withDefValue) + .then((datagrid) => { + expect(datagrid.getValue()).to.deep.equal([ + { name: 'Alex', age: 1 }, + { name: 'Bob', age: 2 }, + { name: 'Conny', age: 3 }, + ]); + done(); + }, done) + .catch(done); + } catch (err) { + done(err); + } + }); - self = { component: { - rowGroups: [{ numberOfRows: 1 }, { numberOfRows: 2 }] - } }; + it('Should allow provide default value in row-groups model', function (done) { + try { + Harness.testCreate(DataGridComponent, withRowGroupsAndDefValue) + .then((datagrid) => { + expect(datagrid.getValue()).to.deep.equal([ + { name: 'Alex', age: 1 }, + { name: 'Bob', age: 2 }, + { name: 'Conny', age: 3 }, + { name: '' }, + { name: '' }, + ]); + done(); + }, done) + .catch(done); + } catch (err) { + done(err); + } + }); - expect(getGroupSizes.call(self)).to.deep.equal([1, 2]); + it('Should not cause setValue loops when logic within hidden component is set', function (done) { + Formio.createForm(document.createElement('div'), withLogic).then( + (form) => { + const datagrid = form.getComponent('dataGrid'); + const spyFunc = sinon.spy(datagrid, 'checkComponentConditions'); + const textField = form.getComponent('escalationId'); + const select = form.getComponent('teamName'); + + textField.component.hidden = true; + select.setValue('preRiskAnalysis', { modified: true }); + + setTimeout(() => { + expect(spyFunc.callCount).to.be.lessThan(4); + done(); + }, 1500); + }, + ); + }); - self = { component: { - rowGroups: [{ numberOfRows: 1 }, { numberOfRows: 3 }, { numberOfRows: 10 }] - } }; + it('Should collapse group rows on group header click', function (done) { + Formio.createForm( + document.createElement('div'), + withCollapsibleRowGroups, + ).then((form) => { + const groupHeadersRefName = 'datagrid-dataGrid-group-header'; + const datagrid = form.getComponent('dataGrid'); + assert.equal( + datagrid.refs[groupHeadersRefName][0]?.classList?.contains( + 'collapsed', + ), + false, + ); + assert.equal( + datagrid.refs.chunks[0][0].classList?.contains('hidden'), + false, + ); + assert.equal( + datagrid.refs.chunks[0][1].classList?.contains('hidden'), + false, + ); - expect(getGroupSizes.call(self)).to.deep.equal([1, 3, 10]); + const clickEvent = new Event('click'); + datagrid.refs[groupHeadersRefName][0].dispatchEvent(clickEvent); + setTimeout(() => { + const collapedGroupRows = datagrid.refs.chunks[0] || []; + assert.equal( + datagrid.refs[groupHeadersRefName][0]?.classList?.contains( + 'collapsed', + ), + true, + ); + assert.equal( + collapedGroupRows[0]?.classList?.contains('hidden'), + true, + ); + assert.equal( + collapedGroupRows[1]?.classList?.contains('hidden'), + true, + ); + done(); + }, 300); + }); }); - }); - - it('Test "components" property and their context', function(done) { - const testComponentsData = (components, expectedData) => { - components.forEach((comp) => assert.deepEqual( - comp.data, - expectedData, - 'Data of components inside DataGrid should be equal to row\'s data' - )); - }; - Formio.createForm(document.createElement('div'), withConditionalFieldsAndValidations) - .then((form) => { - const rootText = form.getComponent(['text']); - rootText.setValue('Match', { modified: true }); + describe('get minLength', function () { + it('should return minimal number of required rows', function () { + const EIDV = 'Invalid default value'; + const EDFC = 'Differ from configured value'; + const EDFG = 'Differ from number of rows in groups'; + const base = { type: 'datagrid', key: 'testkey' }; + let schema = _.cloneDeep(base); + let datagrid = new DataGridComponent(schema, {}); + + expect(datagrid.minLength, EIDV).to.equal(0); + + schema = Object.assign(_.cloneDeep(base), { + validate: { minLength: 5 }, + }); + datagrid = new DataGridComponent(schema, {}); + expect(datagrid.minLength, EDFC).to.equal(5); + + schema = Object.assign(_.cloneDeep(base), { + enableRowGroups: true, + rowGroups: [ + { label: 'H1', numberOfRows: 1 }, + { label: 'B2', numberOfRows: 3 }, + { label: 'C3', numberOfRows: 3 }, + { label: 'M4', numberOfRows: 2 }, + ], + }); + datagrid = new DataGridComponent(schema, {}); + expect(datagrid.minLength, EDFG).to.equal(9); + + schema = Object.assign(_.cloneDeep(base), { + validate: { minLength: 5 }, + enableRowGroups: true, + rowGroups: [ + { label: 'H1', numberOfRows: 1 }, + { label: 'B2', numberOfRows: 3 }, + { label: 'C3', numberOfRows: 3 }, + { label: 'M4', numberOfRows: 2 }, + ], + }); + datagrid = new DataGridComponent(schema, {}); + if (datagrid.minLength === 5) { + expect.fail('Number of row should take precedence over config'); + } else { + expect(datagrid.minLength, EDFG).to.equal(9); + } + }); + }); - setTimeout(() => { - const emptyRowData = { - rootTest: '', - rowTest: '' - }; - const dataGrid = form.getComponent(['dataGrid']); + describe('getGroupSizes', function () { + it('should return array of numbers representing group sizes', function () { + const { getGroupSizes } = DataGridComponent.prototype; + let self = { component: {} }; - assert.equal(dataGrid.components.length, 6, 'DataGrid.components should contain 6 components'); - testComponentsData(dataGrid.components, emptyRowData); + expect(getGroupSizes.call(self)).to.deep.equal([]); - const showTextFieldInsideDataGridRadio = form.getComponent(['radio']); - showTextFieldInsideDataGridRadio.setValue('show', { modified: true }); + self = { + component: { + rowGroups: [{ numberOfRows: 1 }], + }, + }; - setTimeout(() => { - const rowData1 = { ...emptyRowData, radio1: '' }; - const dataGridRowRadio = form.getComponent(['dataGrid', 0, 'radio1']); + expect(getGroupSizes.call(self)).to.deep.equal([1]); - assert.equal(dataGrid.components.length, 6, 'DataGrid.components should contain 6 components'); - testComponentsData(dataGrid.components, rowData1); - assert.equal(dataGridRowRadio.visible, true, 'Radio inside DataGrid should become visible'); + self = { + component: { + rowGroups: [{ numberOfRows: 1 }, { numberOfRows: 2 }], + }, + }; - dataGridRowRadio.setValue('dgShow', { modified: true }); + expect(getGroupSizes.call(self)).to.deep.equal([1, 2]); - setTimeout(() => { - const rowData2 = { - ...emptyRowData, - radio1: 'dgShow', - rowShowShowTextfieldWhenDataGridRadioHasShowValue: '' - }; - const dataGridRowConditionalField = form.getComponent(['dataGrid', 0, 'rowShowShowTextfieldWhenDataGridRadioHasShowValue']); - - assert.equal(dataGrid.components.length, 6, 'DataGrid.components should contain 6 components'); - testComponentsData(dataGrid.components, rowData2); - assert.equal(dataGridRowConditionalField.visible, true, 'Conditional field inside DataGrid should become visible'); - - const rootTest = form.getComponent(['dataGrid', 0, 'rootTest']); - const rowTest = form.getComponent(['dataGrid', 0, 'rowTest']); - - rootTest.setValue('Match', { modified: true }); - rowTest.setValue('Match', { modified: true }); - - setTimeout(() => { - const rowData3 = { - ...rowData2, - rowTest: 'Match', - rootTest: 'Match' - }; + self = { + component: { + rowGroups: [ + { numberOfRows: 1 }, + { numberOfRows: 3 }, + { numberOfRows: 10 }, + ], + }, + }; - assert.equal(dataGrid.components.length, 6, 'DataGrid.components should contain 6 components'); - testComponentsData(dataGrid.components, rowData3); + expect(getGroupSizes.call(self)).to.deep.equal([1, 3, 10]); + }); + }); - form.checkAsyncValidity(null, true).then((valid) => { - assert(valid, 'Form should be valid'); - done(); - }).catch(done); - }, 300); - }, 300); - }, 300); - }, 300); - }) - .catch(done); - }); -}); + it('Test "components" property and their context', function (done) { + const testComponentsData = (components, expectedData) => { + components.forEach((comp) => + assert.deepEqual( + comp.data, + expectedData, + "Data of components inside DataGrid should be equal to row's data", + ), + ); + }; -describe('DataGrid Panels', function() { - it('Should build a data grid component', function() { - return Harness.testCreate(DataGridComponent, comp2); - }); - - it('Should be able to set the values of one panel in the DataGrid.', function() { - return Harness.testCreate(DataGridComponent, comp2).then((component) => { - Harness.testSetGet(component, [ - { - firstName: 'Joe', - lastName: 'Smith' - } - ]); - - // Now add a new row. - component.addRow(); - assert.deepEqual(component.getValue(), [ - { - firstName: 'Joe', - lastName: 'Smith' - }, - { - firstName: '', - lastName: '' - } - ]); - }); - }); - - it('Should have unique IDs inside data grid', function() { - return Harness.testCreate(DataGridComponent, comp7).then((component) => { - component.addRow(); - const idArr = []; - component.components.forEach((row, i) => { - idArr[i] = row.element.component.components[0].id; - }); - assert.equal(idArr[0] !== idArr[1], true); + Formio.createForm( + document.createElement('div'), + withConditionalFieldsAndValidations, + ) + .then((form) => { + const rootText = form.getComponent(['text']); + rootText.setValue('Match', { modified: true }); + + setTimeout(() => { + const emptyRowData = { + rootTest: '', + rowTest: '', + }; + const dataGrid = form.getComponent(['dataGrid']); + + assert.equal( + dataGrid.components.length, + 6, + 'DataGrid.components should contain 6 components', + ); + testComponentsData(dataGrid.components, emptyRowData); + + const showTextFieldInsideDataGridRadio = form.getComponent([ + 'radio', + ]); + showTextFieldInsideDataGridRadio.setValue('show', { + modified: true, + }); + + setTimeout(() => { + const rowData1 = { ...emptyRowData, radio1: '' }; + const dataGridRowRadio = form.getComponent([ + 'dataGrid', + 0, + 'radio1', + ]); + + assert.equal( + dataGrid.components.length, + 6, + 'DataGrid.components should contain 6 components', + ); + testComponentsData(dataGrid.components, rowData1); + assert.equal( + dataGridRowRadio.visible, + true, + 'Radio inside DataGrid should become visible', + ); + + dataGridRowRadio.setValue('dgShow', { modified: true }); + + setTimeout(() => { + const rowData2 = { + ...emptyRowData, + radio1: 'dgShow', + rowShowShowTextfieldWhenDataGridRadioHasShowValue: + '', + }; + const dataGridRowConditionalField = + form.getComponent([ + 'dataGrid', + 0, + 'rowShowShowTextfieldWhenDataGridRadioHasShowValue', + ]); + + assert.equal( + dataGrid.components.length, + 6, + 'DataGrid.components should contain 6 components', + ); + testComponentsData(dataGrid.components, rowData2); + assert.equal( + dataGridRowConditionalField.visible, + true, + 'Conditional field inside DataGrid should become visible', + ); + + const rootTest = form.getComponent([ + 'dataGrid', + 0, + 'rootTest', + ]); + const rowTest = form.getComponent([ + 'dataGrid', + 0, + 'rowTest', + ]); + + rootTest.setValue('Match', { modified: true }); + rowTest.setValue('Match', { modified: true }); + + setTimeout(() => { + const rowData3 = { + ...rowData2, + rowTest: 'Match', + rootTest: 'Match', + }; + + assert.equal( + dataGrid.components.length, + 6, + 'DataGrid.components should contain 6 components', + ); + testComponentsData( + dataGrid.components, + rowData3, + ); + + form.checkAsyncValidity(null, true) + .then((valid) => { + assert(valid, 'Form should be valid'); + done(); + }) + .catch(done); + }, 300); + }, 300); + }, 300); + }, 300); + }) + .catch(done); }); - }); - - it('Should hide label in header for Button component when hideLabel is true.', function() { - const formElement = document.createElement('div'); - return Formio.createForm(formElement, { - display: 'form', - components: [comp8] - }) - .then(() => { - assert.equal(formElement.getElementsByTagName('th')[0].textContent.trim(), '', 'Should hide a label'); - assert.equal(formElement.getElementsByTagName('th')[1].textContent.trim(), 'Text Field', 'Should show a label'); - }); - }); }); -describe('DataGrid disabling', function() { - it('Child components should be disabled', function() { - return Harness.testCreate(DataGridComponent, comp3).then((component) => { - assert.equal(component.components.reduce((acc, child) => acc && child.parentDisabled, true), true); +describe('DataGrid Panels', function () { + it('Should build a data grid component', function () { + return Harness.testCreate(DataGridComponent, comp2); }); - }); -}); -describe('DataGrid modal', function() { - it('Should be highlighted in red when invalid', function(done) { - const formElement = document.createElement('div'); - Formio.createForm(formElement, { - display: 'form', - components: [modalWithRequiredFields] - }) - .then((form) => { - const data = { - dataGrid: [ - { - textField: '', - textArea: '' - } - ] - }; - - form.checkValidity(data, true, data); - - setTimeout(() => { - Harness.testModalWrapperErrorClasses(form); - - const validData = { - dataGrid: [ - { - textField: 'Some text', - textArea: 'Mre text' - } - ] - }; + it('Should be able to set the values of one panel in the DataGrid.', function () { + return Harness.testCreate(DataGridComponent, comp2).then( + (component) => { + Harness.testSetGet(component, [ + { + firstName: 'Joe', + lastName: 'Smith', + }, + ]); + + // Now add a new row. + component.addRow(); + assert.deepEqual(component.getValue(), [ + { + firstName: 'Joe', + lastName: 'Smith', + }, + { + firstName: '', + lastName: '', + }, + ]); + }, + ); + }); - form.setSubmission({ data: validData }); + it('Should have unique IDs inside data grid', function () { + return Harness.testCreate(DataGridComponent, comp7).then( + (component) => { + component.addRow(); + const idArr = []; + component.components.forEach((row, i) => { + idArr[i] = row.element.component.components[0].id; + }); + assert.equal(idArr[0] !== idArr[1], true); + }, + ); + }); - setTimeout(() => { - Harness.testModalWrapperErrorClasses(form, false); - done(); - }, 200); - }, 200); - }) - .catch(done); - }); + it('Should hide label in header for Button component when hideLabel is true.', function () { + const formElement = document.createElement('div'); + return Formio.createForm(formElement, { + display: 'form', + components: [comp8], + }).then(() => { + assert.equal( + formElement.getElementsByTagName('th')[0].textContent.trim(), + '', + 'Should hide a label', + ); + assert.equal( + formElement.getElementsByTagName('th')[1].textContent.trim(), + 'Text Field', + 'Should show a label', + ); + }); + }); }); -describe('DataGrid calculated values', function() { - it('Should allow override calculated value', function(done) { - Formio.createForm(document.createElement('div'), withAllowCalculateOverride) - .then((form) => { - const select = form.getComponent('select'); - const dataGrid = form.getComponent('dataGrid'); - - assert.deepEqual(dataGrid.getValue(), - [{ - firstName: '', - lastName: '' - }] +describe('DataGrid disabling', function () { + it('Child components should be disabled', function () { + return Harness.testCreate(DataGridComponent, comp3).then( + (component) => { + assert.equal( + component.components.reduce( + (acc, child) => acc && child.parentDisabled, + true, + ), + true, + ); + }, ); + }); +}); - select.setValue('a', { modified: true }); - setTimeout(() => { - assert.deepEqual(dataGrid.getValue(), - [{ - firstName: 'A f 1', - lastName: 'A l 1' - }] - ); - - select.setValue('b', { modified: true }); - setTimeout(() => { - assert.deepEqual(dataGrid.getValue(), - [{ - firstName: 'B f 1', - lastName: 'B l 1' - }, - { - firstName: 'B f 2', - lastName: 'B l 2' - }] - ); +describe('DataGrid modal', function () { + it('Should be highlighted in red when invalid', function (done) { + const formElement = document.createElement('div'); + Formio.createForm(formElement, { + display: 'form', + components: [modalWithRequiredFields], + }) + .then((form) => { + const data = { + dataGrid: [ + { + textField: '', + textArea: '', + }, + ], + }; - const firstName = form.getComponent(['dataGrid', 0, 'firstName']); - firstName.setValue('first name', { modified: true }); - select.setValue('c', { modified: true }); - setTimeout(() => { - assert.deepEqual(dataGrid.getValue(), - [{ - firstName: 'first name', - lastName: 'B l 1' - }, - { - firstName: 'B f 2', - lastName: 'B l 2' - }] - ); - done(); - }, 300); - }, 300); - }, 300); - }) - .catch(done); - }); - - it('Should not recalculate value after restoring to previous calculated value', function(done) { - Formio.createForm(document.createElement('div'), withAllowCalculateOverride) - .then((form) => { - const select = form.getComponent('select'); - const dataGrid = form.getComponent('dataGrid'); - - assert.deepEqual(dataGrid.getValue(), - [{ - firstName: '', - lastName: '' - }] - ); + form.checkValidity(data, true, data); + + setTimeout(() => { + Harness.testModalWrapperErrorClasses(form); + + const validData = { + dataGrid: [ + { + textField: 'Some text', + textArea: 'Mre text', + }, + ], + }; + + form.setSubmission({ data: validData }); + + setTimeout(() => { + Harness.testModalWrapperErrorClasses(form, false); + done(); + }, 200); + }, 200); + }) + .catch(done); + }); +}); - select.setValue('a', { modified: true }); - setTimeout(() => { - assert.deepEqual(dataGrid.getValue(), - [{ - firstName: 'A f 1', - lastName: 'A l 1' - }] - ); - - const firstName = form.getComponent(['dataGrid', 0, 'firstName']); - firstName.setValue('first name', { modified: true }); - setTimeout(() => { - select.setValue('c', { modified: true }); - setTimeout(() => { - assert.deepEqual(dataGrid.getValue(), - [{ - firstName: 'first name', - lastName: 'A l 1' - }] - ); - - firstName.setValue('A f 1', { modified: true }); - setTimeout(() => { - assert.equal(select.getValue(), 'c'); - assert.deepEqual(dataGrid.getValue(), - [{ - firstName: 'A f 1', - lastName: 'A l 1' - }] - ); - done(); - }, 300); - }, 300); - }, 300); - }, 300); - }) - .catch(done); - }); - - it('Should calculate value for several DataGrid components', function(done) { - Formio.createForm(document.createElement('div'), twoWithAllowCalculatedOverride) - .then((form) => { - const select = form.getComponent('select'); - const dataGrid = form.getComponent('dataGrid'); - const dataGrid2 = form.getComponent('dataGrid2'); - - assert.deepEqual(dataGrid.getValue(), - [{ - firstName: '', - lastName: '' - }] - ); - assert.deepEqual(dataGrid2.getValue(), - [{ - firstName: '', - lastName: '' - }] - ); +describe('DataGrid calculated values', function () { + it('Should allow override calculated value', function (done) { + Formio.createForm( + document.createElement('div'), + withAllowCalculateOverride, + ) + .then((form) => { + const select = form.getComponent('select'); + const dataGrid = form.getComponent('dataGrid'); + + assert.deepEqual(dataGrid.getValue(), [ + { + firstName: '', + lastName: '', + }, + ]); + + select.setValue('a', { modified: true }); + setTimeout(() => { + assert.deepEqual(dataGrid.getValue(), [ + { + firstName: 'A f 1', + lastName: 'A l 1', + }, + ]); + + select.setValue('b', { modified: true }); + setTimeout(() => { + assert.deepEqual(dataGrid.getValue(), [ + { + firstName: 'B f 1', + lastName: 'B l 1', + }, + { + firstName: 'B f 2', + lastName: 'B l 2', + }, + ]); + + const firstName = form.getComponent([ + 'dataGrid', + 0, + 'firstName', + ]); + firstName.setValue('first name', { modified: true }); + select.setValue('c', { modified: true }); + setTimeout(() => { + assert.deepEqual(dataGrid.getValue(), [ + { + firstName: 'first name', + lastName: 'B l 1', + }, + { + firstName: 'B f 2', + lastName: 'B l 2', + }, + ]); + done(); + }, 300); + }, 300); + }, 300); + }) + .catch(done); + }); - select.setValue('a', { modified: true }); - setTimeout(() => { - assert.deepEqual(dataGrid.getValue(), - [{ - firstName: 'A f 1', - lastName: 'A l 1' - }] - ); - assert.deepEqual(dataGrid2.getValue(), - [{ - firstName: 'A f 1', - lastName: 'A l 1' - }] - ); - - select.setValue('b', { modified: true }); - setTimeout(() => { - assert.deepEqual(dataGrid.getValue(), - [{ - firstName: 'B f 1', - lastName: 'B l 1' - }, - { - firstName: 'B f 2', - lastName: 'B l 2' - }] - ); - assert.deepEqual(dataGrid2.getValue(), - [{ - firstName: 'B f 1', - lastName: 'B l 1' - }, - { - firstName: 'B f 2', - lastName: 'B l 2' - }] - ); + it('Should not recalculate value after restoring to previous calculated value', function (done) { + Formio.createForm( + document.createElement('div'), + withAllowCalculateOverride, + ) + .then((form) => { + const select = form.getComponent('select'); + const dataGrid = form.getComponent('dataGrid'); + + assert.deepEqual(dataGrid.getValue(), [ + { + firstName: '', + lastName: '', + }, + ]); + + select.setValue('a', { modified: true }); + setTimeout(() => { + assert.deepEqual(dataGrid.getValue(), [ + { + firstName: 'A f 1', + lastName: 'A l 1', + }, + ]); + + const firstName = form.getComponent([ + 'dataGrid', + 0, + 'firstName', + ]); + firstName.setValue('first name', { modified: true }); + setTimeout(() => { + select.setValue('c', { modified: true }); + setTimeout(() => { + assert.deepEqual(dataGrid.getValue(), [ + { + firstName: 'first name', + lastName: 'A l 1', + }, + ]); + + firstName.setValue('A f 1', { modified: true }); + setTimeout(() => { + assert.equal(select.getValue(), 'c'); + assert.deepEqual(dataGrid.getValue(), [ + { + firstName: 'A f 1', + lastName: 'A l 1', + }, + ]); + done(); + }, 300); + }, 300); + }, 300); + }, 300); + }) + .catch(done); + }); - const firstName = form.getComponent(['dataGrid', 0, 'firstName']); - firstName.setValue('first name', { modified: true }); - const firstName2 = form.getComponent(['dataGrid2', 0, 'firstName']); - firstName2.setValue('first name 2', { modified: true }); - select.setValue('c', { modified: true }); - setTimeout(() => { - assert.deepEqual(dataGrid.getValue(), - [{ - firstName: 'first name', - lastName: 'B l 1' - }, - { - firstName: 'B f 2', - lastName: 'B l 2' - }] - ); - assert.deepEqual(dataGrid2.getValue(), - [{ - firstName: 'first name 2', - lastName: 'B l 1' - }, - { - firstName: 'B f 2', - lastName: 'B l 2' - }] - ); - done(); - }, 300); - }, 300); - }, 300); - }) - .catch(done); - }); + it('Should calculate value for several DataGrid components', function (done) { + Formio.createForm( + document.createElement('div'), + twoWithAllowCalculatedOverride, + ) + .then((form) => { + const select = form.getComponent('select'); + const dataGrid = form.getComponent('dataGrid'); + const dataGrid2 = form.getComponent('dataGrid2'); + + assert.deepEqual(dataGrid.getValue(), [ + { + firstName: '', + lastName: '', + }, + ]); + assert.deepEqual(dataGrid2.getValue(), [ + { + firstName: '', + lastName: '', + }, + ]); + + select.setValue('a', { modified: true }); + setTimeout(() => { + assert.deepEqual(dataGrid.getValue(), [ + { + firstName: 'A f 1', + lastName: 'A l 1', + }, + ]); + assert.deepEqual(dataGrid2.getValue(), [ + { + firstName: 'A f 1', + lastName: 'A l 1', + }, + ]); + + select.setValue('b', { modified: true }); + setTimeout(() => { + assert.deepEqual(dataGrid.getValue(), [ + { + firstName: 'B f 1', + lastName: 'B l 1', + }, + { + firstName: 'B f 2', + lastName: 'B l 2', + }, + ]); + assert.deepEqual(dataGrid2.getValue(), [ + { + firstName: 'B f 1', + lastName: 'B l 1', + }, + { + firstName: 'B f 2', + lastName: 'B l 2', + }, + ]); + + const firstName = form.getComponent([ + 'dataGrid', + 0, + 'firstName', + ]); + firstName.setValue('first name', { modified: true }); + const firstName2 = form.getComponent([ + 'dataGrid2', + 0, + 'firstName', + ]); + firstName2.setValue('first name 2', { modified: true }); + select.setValue('c', { modified: true }); + setTimeout(() => { + assert.deepEqual(dataGrid.getValue(), [ + { + firstName: 'first name', + lastName: 'B l 1', + }, + { + firstName: 'B f 2', + lastName: 'B l 2', + }, + ]); + assert.deepEqual(dataGrid2.getValue(), [ + { + firstName: 'first name 2', + lastName: 'B l 1', + }, + { + firstName: 'B f 2', + lastName: 'B l 2', + }, + ]); + done(); + }, 300); + }, 300); + }, 300); + }) + .catch(done); + }); }); diff --git a/src/components/datagrid/editForm/DataGrid.edit.data.js b/src/components/datagrid/editForm/DataGrid.edit.data.js index 37a32a08fe..86bb5e76be 100644 --- a/src/components/datagrid/editForm/DataGrid.edit.data.js +++ b/src/components/datagrid/editForm/DataGrid.edit.data.js @@ -1,6 +1,6 @@ export default [ - { - key: 'multiple', - ignore: true - }, + { + key: 'multiple', + ignore: true, + }, ]; diff --git a/src/components/datagrid/editForm/DataGrid.edit.display.js b/src/components/datagrid/editForm/DataGrid.edit.display.js index 394ea506ea..a4866988ca 100644 --- a/src/components/datagrid/editForm/DataGrid.edit.display.js +++ b/src/components/datagrid/editForm/DataGrid.edit.display.js @@ -1,147 +1,151 @@ export default [ - { - key: 'placeholder', - ignore: true - }, - { - type: 'checkbox', - label: 'Disable Adding / Removing Rows', - key: 'disableAddingRemovingRows', - tooltip: 'Check if you want to hide Add Another button and Remove Row button', - weight: 405, - input: true, - clearOnHide: false, - customConditional(context) { - return !context.data.enableRowGroups; + { + key: 'placeholder', + ignore: true, }, - calculateValue(context) { - return context.data.enableRowGroups ? true : context.data.disableAddingRemovingRows; + { + type: 'checkbox', + label: 'Disable Adding / Removing Rows', + key: 'disableAddingRemovingRows', + tooltip: + 'Check if you want to hide Add Another button and Remove Row button', + weight: 405, + input: true, + clearOnHide: false, + customConditional(context) { + return !context.data.enableRowGroups; + }, + calculateValue(context) { + return context.data.enableRowGroups + ? true + : context.data.disableAddingRemovingRows; + }, }, - }, - { - weight: 406, - type: 'textarea', - input: true, - key: 'conditionalAddButton', - label: 'Conditional Add Button', - placeholder: 'show = ...', - tooltip: 'Specify condition when Add Button should be displayed.', - editor: 'ace', - as: 'javascript', - wysiwyg: { - minLines: 3, + { + weight: 406, + type: 'textarea', + input: true, + key: 'conditionalAddButton', + label: 'Conditional Add Button', + placeholder: 'show = ...', + tooltip: 'Specify condition when Add Button should be displayed.', + editor: 'ace', + as: 'javascript', + wysiwyg: { + minLines: 3, + }, }, - }, - { - type: 'checkbox', - label: 'Allow Reorder', - key: 'reorder', - weight: 407, - input: true, - }, - { - type: 'textfield', - label: 'Add Another Text', - key: 'addAnother', - tooltip: 'Set the text of the Add Another button.', - placeholder: 'Add Another', - weight: 410, - input: true, - customConditional(context) { - return !context.data.disableAddingRemovingRows; - } - }, - { - type: 'select', - label: 'Add Another Position', - key: 'addAnotherPosition', - dataSrc: 'values', - tooltip: 'Position for Add Another button with respect to Data Grid Array.', - defaultValue: 'bottom', - input: true, - data: { - values: [ - { label: 'Top', value: 'top' }, - { label: 'Bottom', value: 'bottom' }, - { label: 'Both', value: 'both' } - ] + { + type: 'checkbox', + label: 'Allow Reorder', + key: 'reorder', + weight: 407, + input: true, }, - weight: 411, - customConditional(context) { - return !context.data.disableAddingRemovingRows; - } - }, - { - type: 'checkbox', - label: 'Equal column width', - key: 'layoutFixed', - weight: 430, - input: true, - }, - { - key: 'enableRowGroups', - type: 'checkbox', - label: 'Enable Row Groups', - weight: 440, - input: true - }, - { - label: 'Groups', - disableAddingRemovingRows: false, - defaultOpen: false, - addAnother: '', - addAnotherPosition: 'bottom', - mask: false, - tableView: true, - alwaysEnabled: false, - type: 'datagrid', - input: true, - key: 'rowGroups', - reorder: true, - components: [ - { - label: 'Label', - allowMultipleMasks: false, - showWordCount: false, - showCharCount: false, - tableView: true, - alwaysEnabled: false, + { type: 'textfield', + label: 'Add Another Text', + key: 'addAnother', + tooltip: 'Set the text of the Add Another button.', + placeholder: 'Add Another', + weight: 410, + input: true, + customConditional(context) { + return !context.data.disableAddingRemovingRows; + }, + }, + { + type: 'select', + label: 'Add Another Position', + key: 'addAnotherPosition', + dataSrc: 'values', + tooltip: + 'Position for Add Another button with respect to Data Grid Array.', + defaultValue: 'bottom', input: true, - key: 'label', - widget: { - type: '' + data: { + values: [ + { label: 'Top', value: 'top' }, + { label: 'Bottom', value: 'bottom' }, + { label: 'Both', value: 'both' }, + ], }, - row: '0-0' - }, - { - label: 'Number of Rows', + weight: 411, + customConditional(context) { + return !context.data.disableAddingRemovingRows; + }, + }, + { + type: 'checkbox', + label: 'Equal column width', + key: 'layoutFixed', + weight: 430, + input: true, + }, + { + key: 'enableRowGroups', + type: 'checkbox', + label: 'Enable Row Groups', + weight: 440, + input: true, + }, + { + label: 'Groups', + disableAddingRemovingRows: false, + defaultOpen: false, + addAnother: '', + addAnotherPosition: 'bottom', mask: false, tableView: true, alwaysEnabled: false, - type: 'number', + type: 'datagrid', input: true, - key: 'numberOfRows', - row: '0-1' - } - ], - weight: 441, - conditional: { json: { var: 'data.enableRowGroups' } } - }, - { - label: 'Hide Group on Header Click', - type: 'checkbox', - input: true, - key: 'groupToggle', - weight: 442, - conditional: { json: { var: 'data.enableRowGroups' } } - }, - { - label: 'Initialize Empty', - type: 'checkbox', - input: true, - key: 'initEmpty', - tooltip: 'The DataGrid will have no visible rows when initialized.', - weight: 450 - } + key: 'rowGroups', + reorder: true, + components: [ + { + label: 'Label', + allowMultipleMasks: false, + showWordCount: false, + showCharCount: false, + tableView: true, + alwaysEnabled: false, + type: 'textfield', + input: true, + key: 'label', + widget: { + type: '', + }, + row: '0-0', + }, + { + label: 'Number of Rows', + mask: false, + tableView: true, + alwaysEnabled: false, + type: 'number', + input: true, + key: 'numberOfRows', + row: '0-1', + }, + ], + weight: 441, + conditional: { json: { var: 'data.enableRowGroups' } }, + }, + { + label: 'Hide Group on Header Click', + type: 'checkbox', + input: true, + key: 'groupToggle', + weight: 442, + conditional: { json: { var: 'data.enableRowGroups' } }, + }, + { + label: 'Initialize Empty', + type: 'checkbox', + input: true, + key: 'initEmpty', + tooltip: 'The DataGrid will have no visible rows when initialized.', + weight: 450, + }, ]; diff --git a/src/components/datagrid/editForm/DataGrid.edit.validation.js b/src/components/datagrid/editForm/DataGrid.edit.validation.js index 03e45c630a..f6265aa0d4 100644 --- a/src/components/datagrid/editForm/DataGrid.edit.validation.js +++ b/src/components/datagrid/editForm/DataGrid.edit.validation.js @@ -1,24 +1,24 @@ export default [ - { - ignore: true, - key: 'unique', - }, - { - weight: 110, - key: 'validate.minLength', - label: 'Minimum Length', - placeholder: 'Minimum Length', - type: 'textfield', - tooltip: 'The minimum length requirement this field must meet.', - input: true - }, - { - weight: 120, - key: 'validate.maxLength', - label: 'Maximum Length', - placeholder: 'Maximum Length', - type: 'textfield', - tooltip: 'The maximum length requirement this field must meet.', - input: true - } + { + ignore: true, + key: 'unique', + }, + { + weight: 110, + key: 'validate.minLength', + label: 'Minimum Length', + placeholder: 'Minimum Length', + type: 'textfield', + tooltip: 'The minimum length requirement this field must meet.', + input: true, + }, + { + weight: 120, + key: 'validate.maxLength', + label: 'Maximum Length', + placeholder: 'Maximum Length', + type: 'textfield', + tooltip: 'The maximum length requirement this field must meet.', + input: true, + }, ]; diff --git a/src/components/datagrid/fixtures/comp-modal-with-required-fields.js b/src/components/datagrid/fixtures/comp-modal-with-required-fields.js index 0d2efe2492..f4fce23dc7 100644 --- a/src/components/datagrid/fixtures/comp-modal-with-required-fields.js +++ b/src/components/datagrid/fixtures/comp-modal-with-required-fields.js @@ -1,40 +1,38 @@ export default { - label: 'Data Grid', - reorder: false, - addAnotherPosition: 'bottom', - defaultOpen: false, - layoutFixed: false, - enableRowGroups: false, - initEmpty: false, - tableView: false, - modalEdit: true, - defaultValue: [ - {} - ], - key: 'dataGrid', - type: 'datagrid', - input: true, - components: [ - { - label: 'Text Field', - tableView: true, - validate: { - required: true - }, - key: 'textField', - type: 'textfield', - input: true - }, - { - label: 'Text Area', - autoExpand: false, - tableView: true, - validate: { - required: true - }, - key: 'textArea', - type: 'textarea', - input: true - } - ] + label: 'Data Grid', + reorder: false, + addAnotherPosition: 'bottom', + defaultOpen: false, + layoutFixed: false, + enableRowGroups: false, + initEmpty: false, + tableView: false, + modalEdit: true, + defaultValue: [{}], + key: 'dataGrid', + type: 'datagrid', + input: true, + components: [ + { + label: 'Text Field', + tableView: true, + validate: { + required: true, + }, + key: 'textField', + type: 'textfield', + input: true, + }, + { + label: 'Text Area', + autoExpand: false, + tableView: true, + validate: { + required: true, + }, + key: 'textArea', + type: 'textarea', + input: true, + }, + ], }; diff --git a/src/components/datagrid/fixtures/comp-row-groups-with-def-value.js b/src/components/datagrid/fixtures/comp-row-groups-with-def-value.js index cdc43bd61f..bad8d93b53 100644 --- a/src/components/datagrid/fixtures/comp-row-groups-with-def-value.js +++ b/src/components/datagrid/fixtures/comp-row-groups-with-def-value.js @@ -1,79 +1,79 @@ export default { - 'label': 'Data Grid', - 'disableAddingRemovingRows': true, - 'defaultOpen': false, - 'layoutFixed': false, - 'enableRowGroups': true, - 'mask': false, - 'tableView': true, - 'alwaysEnabled': false, - 'type': 'datagrid', - 'input': true, - 'key': 'dataGrid', - 'components': [ - { - 'label': 'Name', - 'allowMultipleMasks': false, - 'showWordCount': false, - 'showCharCount': false, - 'tableView': true, - 'alwaysEnabled': false, - 'type': 'textfield', - 'input': true, - 'key': 'name', - 'widget': { - 'type': '' - }, - 'row': '0-0' + label: 'Data Grid', + disableAddingRemovingRows: true, + defaultOpen: false, + layoutFixed: false, + enableRowGroups: true, + mask: false, + tableView: true, + alwaysEnabled: false, + type: 'datagrid', + input: true, + key: 'dataGrid', + components: [ + { + label: 'Name', + allowMultipleMasks: false, + showWordCount: false, + showCharCount: false, + tableView: true, + alwaysEnabled: false, + type: 'textfield', + input: true, + key: 'name', + widget: { + type: '', + }, + row: '0-0', + }, + { + label: 'Age', + mask: false, + tableView: true, + alwaysEnabled: false, + type: 'number', + input: true, + key: 'age', + row: '0-1', + }, + ], + encrypted: false, + defaultValue: [ + { + name: 'Alex', + age: 1, + }, + { + name: 'Bob', + age: 2, + }, + { + name: 'Conny', + age: 3, + }, + ], + validate: { + customMessage: '', + json: '', }, - { - 'label': 'Age', - 'mask': false, - 'tableView': true, - 'alwaysEnabled': false, - 'type': 'number', - 'input': true, - 'key': 'age', - 'row': '0-1' - } - ], - 'encrypted': false, - 'defaultValue': [ - { - 'name': 'Alex', - 'age': 1 + conditional: { + show: '', + when: '', + json: '', }, - { - 'name': 'Bob', - 'age': 2 - }, - { - 'name': 'Conny', - 'age': 3 - } - ], - 'validate': { - 'customMessage': '', - 'json': '' - }, - 'conditional': { - 'show': '', - 'when': '', - 'json': '' - }, - 'rowGroups': [ - { - 'label': 'Header', - 'numberOfRows': 1 - }, - { - 'label': 'Body', - 'numberOfRows': 3 - }, - { - 'label': 'Footer', - 'numberOfRows': 1 - } - ], - 'groupToggle': false + rowGroups: [ + { + label: 'Header', + numberOfRows: 1, + }, + { + label: 'Body', + numberOfRows: 3, + }, + { + label: 'Footer', + numberOfRows: 1, + }, + ], + groupToggle: false, }; diff --git a/src/components/datagrid/fixtures/comp-with-allow-calculate-override.js b/src/components/datagrid/fixtures/comp-with-allow-calculate-override.js index da7c89f104..d6fe2f8619 100644 --- a/src/components/datagrid/fixtures/comp-with-allow-calculate-override.js +++ b/src/components/datagrid/fixtures/comp-with-allow-calculate-override.js @@ -1,66 +1,67 @@ export default { - type: 'form', - display: 'form', - components: [ - { - label: 'Select', - widget: 'choicesjs', - tableView: true, - data: { - values: [ - { - label: 'a', - value: 'a' - }, - { - label: 'b', - value: 'b' - }, - { - label: 'c', - value: 'c' - } - ] - }, - key: 'select', - type: 'select', - input: true - }, - { - label: 'Data Grid', - reorder: false, - addAnotherPosition: 'bottom', - layoutFixed: false, - enableRowGroups: false, - initEmpty: false, - tableView: false, - defaultValue: [ + type: 'form', + display: 'form', + components: [ { - firstName: '', - lastName: '' - } - ], - calculateValue: "var temp = instance.defaultValue;\n if(data.select === 'a')\n {\n temp = [{'firstName': 'A f 1','lastName': 'A l 1'}];\n } else if(data.select === 'b') { \n temp = [{'firstName': 'B f 1','lastName': 'B l 1'} \n ,{'firstName': 'B f 2','lastName': 'B l 2'}];\n } else if(data.select === 'c') { \n temp = [{'firstName': 'C f 1','lastName': 'C l 1'}];\n }\n value = temp;", - allowCalculateOverride: true, - key: 'dataGrid', - type: 'datagrid', - input: true, - components: [ - { - label: 'First Name', - tableView: true, - key: 'firstName', - type: 'textfield', - input: true + label: 'Select', + widget: 'choicesjs', + tableView: true, + data: { + values: [ + { + label: 'a', + value: 'a', + }, + { + label: 'b', + value: 'b', + }, + { + label: 'c', + value: 'c', + }, + ], + }, + key: 'select', + type: 'select', + input: true, }, { - label: 'Last Name', - tableView: true, - key: 'lastName', - type: 'textfield', - input: true - } - ] - } - ] + label: 'Data Grid', + reorder: false, + addAnotherPosition: 'bottom', + layoutFixed: false, + enableRowGroups: false, + initEmpty: false, + tableView: false, + defaultValue: [ + { + firstName: '', + lastName: '', + }, + ], + calculateValue: + "var temp = instance.defaultValue;\n if(data.select === 'a')\n {\n temp = [{'firstName': 'A f 1','lastName': 'A l 1'}];\n } else if(data.select === 'b') { \n temp = [{'firstName': 'B f 1','lastName': 'B l 1'} \n ,{'firstName': 'B f 2','lastName': 'B l 2'}];\n } else if(data.select === 'c') { \n temp = [{'firstName': 'C f 1','lastName': 'C l 1'}];\n }\n value = temp;", + allowCalculateOverride: true, + key: 'dataGrid', + type: 'datagrid', + input: true, + components: [ + { + label: 'First Name', + tableView: true, + key: 'firstName', + type: 'textfield', + input: true, + }, + { + label: 'Last Name', + tableView: true, + key: 'lastName', + type: 'textfield', + input: true, + }, + ], + }, + ], }; diff --git a/src/components/datagrid/fixtures/comp-with-collapsible-groups.js b/src/components/datagrid/fixtures/comp-with-collapsible-groups.js index 82b711a36c..17a234488f 100644 --- a/src/components/datagrid/fixtures/comp-with-collapsible-groups.js +++ b/src/components/datagrid/fixtures/comp-with-collapsible-groups.js @@ -1,73 +1,73 @@ export default { - type: 'form', - components: [ - { - label: 'Data Grid', - disableAddingRemovingRows: true, - reorder: false, - addAnotherPosition: 'bottom', - layoutFixed: false, - enableRowGroups: true, - initEmpty: false, - tableView: false, - defaultValue: [ + type: 'form', + components: [ { - textField: '', - number: '', + label: 'Data Grid', + disableAddingRemovingRows: true, + reorder: false, + addAnotherPosition: 'bottom', + layoutFixed: false, + enableRowGroups: true, + initEmpty: false, + tableView: false, + defaultValue: [ + { + textField: '', + number: '', + }, + ], + key: 'dataGrid', + type: 'datagrid', + rowGroups: [ + { + label: 'group1', + numberOfRows: 2, + }, + { + label: 'group2', + numberOfRows: 3, + }, + ], + groupToggle: true, + input: true, + components: [ + { + label: 'Text Field', + tableView: true, + key: 'textField', + type: 'textfield', + input: true, + }, + { + label: 'Number', + mask: false, + tableView: false, + delimiter: false, + requireDecimal: false, + inputFormat: 'plain', + truncateMultipleSpaces: false, + key: 'number', + type: 'number', + input: true, + }, + ], }, - ], - key: 'dataGrid', - type: 'datagrid', - rowGroups: [ { - label: 'group1', - numberOfRows: 2, + type: 'button', + label: 'Submit', + key: 'submit', + disableOnInvalid: true, + input: true, + tableView: false, }, - { - label: 'group2', - numberOfRows: 3, - }, - ], - groupToggle: true, - input: true, - components: [ - { - label: 'Text Field', - tableView: true, - key: 'textField', - type: 'textfield', - input: true, - }, - { - label: 'Number', - mask: false, - tableView: false, - delimiter: false, - requireDecimal: false, - inputFormat: 'plain', - truncateMultipleSpaces: false, - key: 'number', - type: 'number', - input: true, - }, - ], - }, - { - type: 'button', - label: 'Submit', - key: 'submit', - disableOnInvalid: true, - input: true, - tableView: false, - }, - ], - _vid: 0, - title: 'test', - display: 'form', - name: 'test', - path: 'test', - project: '6152d9c4daa2ef2742d17c07', - created: '2022-12-19T10:14:02.292Z', - modified: '2022-12-19T13:19:37.649Z', - machineName: 'njwzrqvnlksevej:test', + ], + _vid: 0, + title: 'test', + display: 'form', + name: 'test', + path: 'test', + project: '6152d9c4daa2ef2742d17c07', + created: '2022-12-19T10:14:02.292Z', + modified: '2022-12-19T13:19:37.649Z', + machineName: 'njwzrqvnlksevej:test', }; diff --git a/src/components/datagrid/fixtures/comp-with-conditional-components-and-validations.js b/src/components/datagrid/fixtures/comp-with-conditional-components-and-validations.js index 94ed00ed97..fe57713528 100644 --- a/src/components/datagrid/fixtures/comp-with-conditional-components-and-validations.js +++ b/src/components/datagrid/fixtures/comp-with-conditional-components-and-validations.js @@ -1,176 +1,179 @@ export default { - type: 'form', - components: [ - { - title: 'Page 1', - label: 'Page 1', - type: 'panel', - key: 'page1', - components: [ + type: 'form', + components: [ { - label: 'HTML', - attrs: [ - { - attr: '', - value: '' - } - ], - content: "Various Tests on Calculations, Conditionals, and Custom Validations:\n
    (the following rules apply to both the DataGrid and EditGrid)\n
    Root Text:\n
    - 1st Textfield in DG and EG should match the 'Root Text' field data\n
    - 2nd Textfield in DG and EG ('Row') should match the data from the 1st Textfield in the DG and EG\n
    Root Radio:\n
    - When show is clicked, Radio should display in DG and EG\n
    Grid Radio:\n
    - When show is clicked for the Grid Radio, the 'Row Show' textfield should display in the DG and EG\n
    Root Calculated Text:\n
    - Data entered in this textfield should display and populate grid calculated field\n
    - Textfield next to above should calculate based on grid calculated field", - refreshOnChange: false, - key: 'html', - type: 'htmlelement', - tableView: false, - input: false, - alwaysEnabled: false - }, - { - label: 'Root Text', - alwaysEnabled: false, - tableView: true, - key: 'text', - type: 'textfield', - input: true - }, - { - label: 'Root - Radio', - optionsLabelPosition: 'right', - inline: false, - alwaysEnabled: false, - tableView: false, - values: [ - { - label: 'Show', - value: 'show', - shortcut: '' - }, - { - label: 'Hide', - value: 'hide', - shortcut: '' - } - ], - validate: { - required: true - }, - key: 'radio', - type: 'radio', - input: true - }, - { - label: 'Root - Text Calculate', - alwaysEnabled: false, - tableView: true, - key: 'textCal', - type: 'textfield', - input: true - }, - { - label: 'Data Grid', - reorder: false, - addAnotherPosition: 'bottom', - defaultOpen: false, - layoutFixed: false, - enableRowGroups: false, - alwaysEnabled: false, - tableView: false, - key: 'dataGrid', - type: 'datagrid', - input: true, - components: [ - { - label: 'Textfield - DG', - tableView: true, - validate: { - custom: 'valid = (input === data.text) ? true : `data must match root textfield: ${input}, ${data.text}`;' - }, - key: 'rootTest', - type: 'textfield', - alwaysEnabled: false, - input: true - }, - { - label: 'Row Test - DG', - tableView: true, - validate: { - custom: 'valid = (input == row.rootTest) ? true : `must equal textfield in dg: ${input}, ${row.rootTest}`;' - }, - key: 'rowTest', - type: 'textfield', - alwaysEnabled: false, - input: true - }, - { - label: 'Radio', - optionsLabelPosition: 'right', - inline: false, - tableView: false, - values: [ + title: 'Page 1', + label: 'Page 1', + type: 'panel', + key: 'page1', + components: [ + { + label: 'HTML', + attrs: [ + { + attr: '', + value: '', + }, + ], + content: + "Various Tests on Calculations, Conditionals, and Custom Validations:\n
    (the following rules apply to both the DataGrid and EditGrid)\n
    Root Text:\n
    - 1st Textfield in DG and EG should match the 'Root Text' field data\n
    - 2nd Textfield in DG and EG ('Row') should match the data from the 1st Textfield in the DG and EG\n
    Root Radio:\n
    - When show is clicked, Radio should display in DG and EG\n
    Grid Radio:\n
    - When show is clicked for the Grid Radio, the 'Row Show' textfield should display in the DG and EG\n
    Root Calculated Text:\n
    - Data entered in this textfield should display and populate grid calculated field\n
    - Textfield next to above should calculate based on grid calculated field", + refreshOnChange: false, + key: 'html', + type: 'htmlelement', + tableView: false, + input: false, + alwaysEnabled: false, + }, + { + label: 'Root Text', + alwaysEnabled: false, + tableView: true, + key: 'text', + type: 'textfield', + input: true, + }, + { + label: 'Root - Radio', + optionsLabelPosition: 'right', + inline: false, + alwaysEnabled: false, + tableView: false, + values: [ + { + label: 'Show', + value: 'show', + shortcut: '', + }, + { + label: 'Hide', + value: 'hide', + shortcut: '', + }, + ], + validate: { + required: true, + }, + key: 'radio', + type: 'radio', + input: true, + }, { - label: 'DG Show', - value: 'dgShow', - shortcut: '' + label: 'Root - Text Calculate', + alwaysEnabled: false, + tableView: true, + key: 'textCal', + type: 'textfield', + input: true, }, { - label: 'DG Hide', - value: 'dgHide', - shortcut: '' - } - ], - validate: { - required: true - }, - key: 'radio1', - customConditional: "show = (data.radio === 'show');", - type: 'radio', - alwaysEnabled: false, - input: true - }, - { - label: "Row Show - Show textfield when data grid radio has 'show' value", - alwaysEnabled: false, - tableView: true, - key: 'rowShowShowTextfieldWhenDataGridRadioHasShowValue', - customConditional: "show = (row.radio1 === 'dgShow');", - type: 'textfield', - input: true, - radio1: null - }, - { - label: "DG Calculated - Value should populate from 'root calculated text' field", - tableView: true, - calculateValue: 'value = data.textCal;', - allowCalculateOverride: true, - key: 'calculateTextDg', - customConditional: 'show = (data.textCal);', - type: 'textfield', - alwaysEnabled: false, - input: true - }, - { - label: 'Textfield - Data should populate from Datagrid calculate field', - alwaysEnabled: false, - tableView: true, - calculateValue: 'value = row.calculateTextDg;', - key: 'textfieldDataShouldPopulateFromDatagridCalculateField', - customConditional: 'show = (data.textCal);', - type: 'textfield', - input: true - } - ], - path: 'dataGrid' - } - ], - input: false, - tableView: false - }, - { - label: 'Submit', - showValidations: false, - tableView: false, - key: 'submit', - type: 'button', - input: true - } - ], - display: 'form' + label: 'Data Grid', + reorder: false, + addAnotherPosition: 'bottom', + defaultOpen: false, + layoutFixed: false, + enableRowGroups: false, + alwaysEnabled: false, + tableView: false, + key: 'dataGrid', + type: 'datagrid', + input: true, + components: [ + { + label: 'Textfield - DG', + tableView: true, + validate: { + custom: 'valid = (input === data.text) ? true : `data must match root textfield: ${input}, ${data.text}`;', + }, + key: 'rootTest', + type: 'textfield', + alwaysEnabled: false, + input: true, + }, + { + label: 'Row Test - DG', + tableView: true, + validate: { + custom: 'valid = (input == row.rootTest) ? true : `must equal textfield in dg: ${input}, ${row.rootTest}`;', + }, + key: 'rowTest', + type: 'textfield', + alwaysEnabled: false, + input: true, + }, + { + label: 'Radio', + optionsLabelPosition: 'right', + inline: false, + tableView: false, + values: [ + { + label: 'DG Show', + value: 'dgShow', + shortcut: '', + }, + { + label: 'DG Hide', + value: 'dgHide', + shortcut: '', + }, + ], + validate: { + required: true, + }, + key: 'radio1', + customConditional: + "show = (data.radio === 'show');", + type: 'radio', + alwaysEnabled: false, + input: true, + }, + { + label: "Row Show - Show textfield when data grid radio has 'show' value", + alwaysEnabled: false, + tableView: true, + key: 'rowShowShowTextfieldWhenDataGridRadioHasShowValue', + customConditional: + "show = (row.radio1 === 'dgShow');", + type: 'textfield', + input: true, + radio1: null, + }, + { + label: "DG Calculated - Value should populate from 'root calculated text' field", + tableView: true, + calculateValue: 'value = data.textCal;', + allowCalculateOverride: true, + key: 'calculateTextDg', + customConditional: 'show = (data.textCal);', + type: 'textfield', + alwaysEnabled: false, + input: true, + }, + { + label: 'Textfield - Data should populate from Datagrid calculate field', + alwaysEnabled: false, + tableView: true, + calculateValue: 'value = row.calculateTextDg;', + key: 'textfieldDataShouldPopulateFromDatagridCalculateField', + customConditional: 'show = (data.textCal);', + type: 'textfield', + input: true, + }, + ], + path: 'dataGrid', + }, + ], + input: false, + tableView: false, + }, + { + label: 'Submit', + showValidations: false, + tableView: false, + key: 'submit', + type: 'button', + input: true, + }, + ], + display: 'form', }; diff --git a/src/components/datagrid/fixtures/comp-with-def-value.js b/src/components/datagrid/fixtures/comp-with-def-value.js index f5a386b827..01fb816cab 100644 --- a/src/components/datagrid/fixtures/comp-with-def-value.js +++ b/src/components/datagrid/fixtures/comp-with-def-value.js @@ -1,67 +1,67 @@ export default { - 'label': 'Data Grid', - 'disableAddingRemovingRows': false, - 'addAnother': '', - 'addAnotherPosition': 'bottom', - 'removePlacement': 'col', - 'defaultOpen': false, - 'layoutFixed': false, - 'enableRowGroups': false, - 'mask': false, - 'tableView': true, - 'alwaysEnabled': false, - 'type': 'datagrid', - 'input': true, - 'key': 'dataGrid', - 'components': [ - { - 'label': 'Name', - 'allowMultipleMasks': false, - 'showWordCount': false, - 'showCharCount': false, - 'tableView': true, - 'alwaysEnabled': false, - 'type': 'textfield', - 'input': true, - 'key': 'name', - 'widget': { - 'type': '' - }, - 'row': '0-0' + label: 'Data Grid', + disableAddingRemovingRows: false, + addAnother: '', + addAnotherPosition: 'bottom', + removePlacement: 'col', + defaultOpen: false, + layoutFixed: false, + enableRowGroups: false, + mask: false, + tableView: true, + alwaysEnabled: false, + type: 'datagrid', + input: true, + key: 'dataGrid', + components: [ + { + label: 'Name', + allowMultipleMasks: false, + showWordCount: false, + showCharCount: false, + tableView: true, + alwaysEnabled: false, + type: 'textfield', + input: true, + key: 'name', + widget: { + type: '', + }, + row: '0-0', + }, + { + label: 'Age', + mask: false, + tableView: true, + alwaysEnabled: false, + type: 'number', + input: true, + key: 'age', + row: '0-1', + }, + ], + encrypted: false, + defaultValue: [ + { + name: 'Alex', + age: 1, + }, + { + name: 'Bob', + age: 2, + }, + { + name: 'Conny', + age: 3, + }, + ], + validate: { + customMessage: '', + json: '', }, - { - 'label': 'Age', - 'mask': false, - 'tableView': true, - 'alwaysEnabled': false, - 'type': 'number', - 'input': true, - 'key': 'age', - 'row': '0-1' - } - ], - 'encrypted': false, - 'defaultValue': [ - { - 'name': 'Alex', - 'age': 1 + conditional: { + show: '', + when: '', + json: '', }, - { - 'name': 'Bob', - 'age': 2 - }, - { - 'name': 'Conny', - 'age': 3 - } - ], - 'validate': { - 'customMessage': '', - 'json': '' - }, - 'conditional': { - 'show': '', - 'when': '', - 'json': '' - } }; diff --git a/src/components/datagrid/fixtures/comp-with-logic.js b/src/components/datagrid/fixtures/comp-with-logic.js index ed147dd2f3..3b3e010ecc 100644 --- a/src/components/datagrid/fixtures/comp-with-logic.js +++ b/src/components/datagrid/fixtures/comp-with-logic.js @@ -1,179 +1,179 @@ export default { - type: 'form', - display: 'form', - components: [ - { - label: 'Data Grid', - key: 'dataGrid', - type: 'datagrid', - input: true, - tableView: false, - components: [ + type: 'form', + display: 'form', + components: [ { - label: 'Team Name', - widget: 'choicesjs', - tableView: true, - data: { - values: [ - { - label: 'Pre Risk Analysis', - value: 'preRiskAnalysis', - }, - { - label: 'Docs', - value: 'docs', - }, - ], - }, - key: 'teamName', - type: 'select', - input: true, - }, - { - label: 'Priority(For Pre-Risk Analysis, select priority as Urgent)', - tableView: false, - values: [ - { - label: 'Normal', - value: 'normal', - shortcut: '', - }, - { - label: 'Urgent', - value: 'urgent', - shortcut: '', - }, - ], - key: 'actionadd3', - type: 'radio', - input: true, - }, - { - label: 'Follow-up Date(For Urgent)', - format: 'dd-MMM-yyyy HH:mm', - hidden: true, - tableView: false, - enableMinDateInput: false, - datePicker: { - disableWeekends: true, - disableWeekdays: false, - }, - enableMaxDateInput: false, - timePicker: { - showMeridian: false, - }, - key: 'followUpDate', - logic: [ - { - name: 'logic', - trigger: { - type: 'javascript', - javascript: - 'return (row.teamName === "preRiskAnalysis" && row.actionadd3 === "urgent")', - }, - actions: [ + label: 'Data Grid', + key: 'dataGrid', + type: 'datagrid', + input: true, + tableView: false, + components: [ { - name: 'action', - type: 'property', - property: { - label: 'Hidden', - value: 'hidden', - type: 'boolean', - }, - state: false, + label: 'Team Name', + widget: 'choicesjs', + tableView: true, + data: { + values: [ + { + label: 'Pre Risk Analysis', + value: 'preRiskAnalysis', + }, + { + label: 'Docs', + value: 'docs', + }, + ], + }, + key: 'teamName', + type: 'select', + input: true, }, - ], - }, - ], - type: 'datetime', - input: true, - widget: { - type: 'calendar', - displayInTimezone: 'viewer', - locale: 'en', - useLocaleSettings: false, - allowInput: true, - mode: 'single', - enableTime: true, - noCalendar: false, - format: 'dd-MMM-yyyy HH:mm', - hourIncrement: 1, - minuteIncrement: 1, - 'time_24hr': true, - minDate: null, - disableWeekends: true, - disableWeekdays: false, - maxDate: null, - }, - }, - { - label: 'Escalation ID', - hidden: false, - clearOnHide: true, - disabled: true, - tableView: true, - key: 'escalationId', - logic: [ - { - name: 'logic1', - trigger: { - type: 'simple', - simple: { - show: true, - when: 'dataGrid.teamName', - eq: 'preRiskAnalysis', - }, - }, - actions: [ { - name: 'ation1', - type: 'value', - value: 'value="RUSH"', - }, - ], - }, - { - name: 'logic2', - trigger: { - type: 'simple', - simple: { - show: true, - when: 'dataGrid.teamName', - eq: 'docs', + label: 'Priority(For Pre-Risk Analysis, select priority as Urgent)', + tableView: false, + values: [ + { + label: 'Normal', + value: 'normal', + shortcut: '', + }, + { + label: 'Urgent', + value: 'urgent', + shortcut: '', + }, + ], + key: 'actionadd3', + type: 'radio', + input: true, }, - }, - actions: [ { - name: 'action2', - type: 'value', - value: 'value="4DDJ"', + label: 'Follow-up Date(For Urgent)', + format: 'dd-MMM-yyyy HH:mm', + hidden: true, + tableView: false, + enableMinDateInput: false, + datePicker: { + disableWeekends: true, + disableWeekdays: false, + }, + enableMaxDateInput: false, + timePicker: { + showMeridian: false, + }, + key: 'followUpDate', + logic: [ + { + name: 'logic', + trigger: { + type: 'javascript', + javascript: + 'return (row.teamName === "preRiskAnalysis" && row.actionadd3 === "urgent")', + }, + actions: [ + { + name: 'action', + type: 'property', + property: { + label: 'Hidden', + value: 'hidden', + type: 'boolean', + }, + state: false, + }, + ], + }, + ], + type: 'datetime', + input: true, + widget: { + type: 'calendar', + displayInTimezone: 'viewer', + locale: 'en', + useLocaleSettings: false, + allowInput: true, + mode: 'single', + enableTime: true, + noCalendar: false, + format: 'dd-MMM-yyyy HH:mm', + hourIncrement: 1, + minuteIncrement: 1, + time_24hr: true, + minDate: null, + disableWeekends: true, + disableWeekdays: false, + maxDate: null, + }, }, - ], - }, - { - name: 'On Hide', - trigger: { - type: 'event', - event: 'hide' - }, - actions: [ { - name: 'Hide Component', - type: 'property', - property: { - label: 'Hidden', - value: 'hidden', - type: 'boolean' - }, - state: true - } - ] - } - ], - type: 'textfield', - input: true, + label: 'Escalation ID', + hidden: false, + clearOnHide: true, + disabled: true, + tableView: true, + key: 'escalationId', + logic: [ + { + name: 'logic1', + trigger: { + type: 'simple', + simple: { + show: true, + when: 'dataGrid.teamName', + eq: 'preRiskAnalysis', + }, + }, + actions: [ + { + name: 'ation1', + type: 'value', + value: 'value="RUSH"', + }, + ], + }, + { + name: 'logic2', + trigger: { + type: 'simple', + simple: { + show: true, + when: 'dataGrid.teamName', + eq: 'docs', + }, + }, + actions: [ + { + name: 'action2', + type: 'value', + value: 'value="4DDJ"', + }, + ], + }, + { + name: 'On Hide', + trigger: { + type: 'event', + event: 'hide', + }, + actions: [ + { + name: 'Hide Component', + type: 'property', + property: { + label: 'Hidden', + value: 'hidden', + type: 'boolean', + }, + state: true, + }, + ], + }, + ], + type: 'textfield', + input: true, + }, + ], }, - ], - } - ] + ], }; diff --git a/src/components/datagrid/fixtures/comp1.js b/src/components/datagrid/fixtures/comp1.js index 3da8080b6b..ea1a15504e 100644 --- a/src/components/datagrid/fixtures/comp1.js +++ b/src/components/datagrid/fixtures/comp1.js @@ -1,121 +1,113 @@ export default { - 'conditional': { - 'eq': '', - 'when': null, - 'show': '' - }, - 'tags': [ - - ], - 'type': 'datagrid', - 'persistent': true, - 'protected': false, - 'key': 'cars', - 'label': 'Cars', - 'tableView': true, - 'components': [ - { - 'tags': [ - - ], - 'hideLabel': true, - 'type': 'textfield', - 'conditional': { - 'eq': '', - 'when': null, - 'show': '' - }, - 'validate': { - 'customPrivate': false, - 'custom': '', - 'pattern': '', - 'maxLength': '', - 'minLength': '', - 'required': false - }, - 'persistent': true, - 'unique': false, - 'protected': false, - 'defaultValue': '', - 'multiple': false, - 'suffix': '', - 'prefix': '', - 'placeholder': '', - 'key': 'make', - 'label': 'Make', - 'inputMask': '', - 'inputType': 'text', - 'tableView': true, - 'input': true + conditional: { + eq: '', + when: null, + show: '', }, - { - 'tags': [ - - ], - 'hideLabel': true, - 'type': 'textfield', - 'conditional': { - 'eq': '', - 'when': null, - 'show': '' - }, - 'validate': { - 'customPrivate': false, - 'custom': '', - 'pattern': '', - 'maxLength': '', - 'minLength': '', - 'required': false - }, - 'persistent': true, - 'unique': false, - 'protected': false, - 'defaultValue': '', - 'multiple': false, - 'suffix': '', - 'prefix': '', - 'placeholder': '', - 'key': 'model', - 'label': 'Model', - 'inputMask': '', - 'inputType': 'text', - 'tableView': true, - 'input': true - }, - { - 'conditional': { - 'eq': '', - 'when': null, - 'show': '' - }, - 'tags': [ - - ], - 'hideLabel': true, - 'type': 'number', - 'validate': { - 'custom': '', - 'multiple': '', - 'integer': '', - 'step': 'any', - 'max': '', - 'min': '', - 'required': false - }, - 'persistent': true, - 'protected': false, - 'defaultValue': '', - 'suffix': '', - 'prefix': '', - 'placeholder': '', - 'key': 'year', - 'label': 'Year', - 'inputType': 'number', - 'delimiter': true, - 'tableView': true, - 'input': true - } - ], - 'tree': true, - 'input': true + tags: [], + type: 'datagrid', + persistent: true, + protected: false, + key: 'cars', + label: 'Cars', + tableView: true, + components: [ + { + tags: [], + hideLabel: true, + type: 'textfield', + conditional: { + eq: '', + when: null, + show: '', + }, + validate: { + customPrivate: false, + custom: '', + pattern: '', + maxLength: '', + minLength: '', + required: false, + }, + persistent: true, + unique: false, + protected: false, + defaultValue: '', + multiple: false, + suffix: '', + prefix: '', + placeholder: '', + key: 'make', + label: 'Make', + inputMask: '', + inputType: 'text', + tableView: true, + input: true, + }, + { + tags: [], + hideLabel: true, + type: 'textfield', + conditional: { + eq: '', + when: null, + show: '', + }, + validate: { + customPrivate: false, + custom: '', + pattern: '', + maxLength: '', + minLength: '', + required: false, + }, + persistent: true, + unique: false, + protected: false, + defaultValue: '', + multiple: false, + suffix: '', + prefix: '', + placeholder: '', + key: 'model', + label: 'Model', + inputMask: '', + inputType: 'text', + tableView: true, + input: true, + }, + { + conditional: { + eq: '', + when: null, + show: '', + }, + tags: [], + hideLabel: true, + type: 'number', + validate: { + custom: '', + multiple: '', + integer: '', + step: 'any', + max: '', + min: '', + required: false, + }, + persistent: true, + protected: false, + defaultValue: '', + suffix: '', + prefix: '', + placeholder: '', + key: 'year', + label: 'Year', + inputType: 'number', + delimiter: true, + tableView: true, + input: true, + }, + ], + tree: true, + input: true, }; diff --git a/src/components/datagrid/fixtures/comp2.js b/src/components/datagrid/fixtures/comp2.js index d09f778b24..a842cba64c 100644 --- a/src/components/datagrid/fixtures/comp2.js +++ b/src/components/datagrid/fixtures/comp2.js @@ -1,27 +1,27 @@ export default { - label: 'Children', - key: 'children', - type: 'datagrid', - input: true, - components: [ - { - type: 'panel', - label: 'User Information', - key: 'userinfo', - components: [ + label: 'Children', + key: 'children', + type: 'datagrid', + input: true, + components: [ { - label: 'First Name', - key: 'firstName', - type: 'textfield', - input: true + type: 'panel', + label: 'User Information', + key: 'userinfo', + components: [ + { + label: 'First Name', + key: 'firstName', + type: 'textfield', + input: true, + }, + { + label: 'Last Name', + key: 'lastName', + type: 'textfield', + input: true, + }, + ], }, - { - label: 'Last Name', - key: 'lastName', - type: 'textfield', - input: true - } - ] - } - ] + ], }; diff --git a/src/components/datagrid/fixtures/comp3.js b/src/components/datagrid/fixtures/comp3.js index 26be8cf388..e8ccd5c3a7 100644 --- a/src/components/datagrid/fixtures/comp3.js +++ b/src/components/datagrid/fixtures/comp3.js @@ -1,21 +1,21 @@ export default { - label: 'Datagrid', - key: 'comp3', - type: 'datagrid', - input: true, - disabled: true, - components: [ - { - label: 'First Name', - key: 'firstName', - type: 'textfield', - input: true - }, - { - label: 'Second Name', - key: 'secondName', - type: 'textfield', - input: true - } - ] + label: 'Datagrid', + key: 'comp3', + type: 'datagrid', + input: true, + disabled: true, + components: [ + { + label: 'First Name', + key: 'firstName', + type: 'textfield', + input: true, + }, + { + label: 'Second Name', + key: 'secondName', + type: 'textfield', + input: true, + }, + ], }; diff --git a/src/components/datagrid/fixtures/comp4.js b/src/components/datagrid/fixtures/comp4.js index 0fc98468bc..794ce38e79 100644 --- a/src/components/datagrid/fixtures/comp4.js +++ b/src/components/datagrid/fixtures/comp4.js @@ -1,27 +1,28 @@ export default { - 'label': 'Data Grid', - 'reorder': false, - 'addAnotherPosition': 'bottom', - 'defaultOpen': false, - 'layoutFixed': false, - 'enableRowGroups': false, - 'tableView': false, - 'modalEdit': true, - 'defaultValue': [{}], - 'key': 'dataGrid', - 'type': 'datagrid', - 'input': true, - 'components': [{ - 'label': 'Number', - 'mask': false, - 'spellcheck': true, - 'tableView': false, - 'delimiter': false, - 'requireDecimal': false, - 'inputFormat': 'plain', - 'key': 'number', - 'type': 'number', - 'input': true - }] - }; - + label: 'Data Grid', + reorder: false, + addAnotherPosition: 'bottom', + defaultOpen: false, + layoutFixed: false, + enableRowGroups: false, + tableView: false, + modalEdit: true, + defaultValue: [{}], + key: 'dataGrid', + type: 'datagrid', + input: true, + components: [ + { + label: 'Number', + mask: false, + spellcheck: true, + tableView: false, + delimiter: false, + requireDecimal: false, + inputFormat: 'plain', + key: 'number', + type: 'number', + input: true, + }, + ], +}; diff --git a/src/components/datagrid/fixtures/comp5.js b/src/components/datagrid/fixtures/comp5.js index fddfa2cd86..eb5601a385 100644 --- a/src/components/datagrid/fixtures/comp5.js +++ b/src/components/datagrid/fixtures/comp5.js @@ -1,75 +1,75 @@ export default { - label: 'Data Grid', - reorder: false, - addAnotherPosition: 'bottom', - defaultOpen: false, - layoutFixed: false, - enableRowGroups: false, - initEmpty: false, - tableView: true, - modalEdit: true, - defaultValue: [ - { - textField: '', - radio1: '', - email: '' - } - ], - key: 'dataGrid', - type: 'datagrid', - input: true, - components: [ - { - label: 'Text Field', - tableView: true, - key: 'textField', - type: 'textfield', - input: true - }, - { - label: 'Number', - mask: false, - spellcheck: true, - tableView: true, - delimiter: false, - requireDecimal: false, - inputFormat: 'plain', - key: 'number', - type: 'number', - input: true - }, - { - label: 'Radio', - optionsLabelPosition: 'right', - inline: false, - tableView: true, - values: [ + label: 'Data Grid', + reorder: false, + addAnotherPosition: 'bottom', + defaultOpen: false, + layoutFixed: false, + enableRowGroups: false, + initEmpty: false, + tableView: true, + modalEdit: true, + defaultValue: [ { - label: 'Ra', - value: 'ra', - shortcut: '' + textField: '', + radio1: '', + email: '', }, + ], + key: 'dataGrid', + type: 'datagrid', + input: true, + components: [ { - label: 'Rb', - value: 'rb', - shortcut: '' + label: 'Text Field', + tableView: true, + key: 'textField', + type: 'textfield', + input: true, }, { - label: 'Rc', - value: 'rc', - shortcut: '' - } - ], - key: 'radio1', - type: 'radio', - input: true - }, - { - label: 'Email', - tableView: true, - key: 'email', - type: 'email', - input: true - } - ] + label: 'Number', + mask: false, + spellcheck: true, + tableView: true, + delimiter: false, + requireDecimal: false, + inputFormat: 'plain', + key: 'number', + type: 'number', + input: true, + }, + { + label: 'Radio', + optionsLabelPosition: 'right', + inline: false, + tableView: true, + values: [ + { + label: 'Ra', + value: 'ra', + shortcut: '', + }, + { + label: 'Rb', + value: 'rb', + shortcut: '', + }, + { + label: 'Rc', + value: 'rc', + shortcut: '', + }, + ], + key: 'radio1', + type: 'radio', + input: true, + }, + { + label: 'Email', + tableView: true, + key: 'email', + type: 'email', + input: true, + }, + ], }; diff --git a/src/components/datagrid/fixtures/comp6.js b/src/components/datagrid/fixtures/comp6.js index 332e091560..c160a9ca61 100644 --- a/src/components/datagrid/fixtures/comp6.js +++ b/src/components/datagrid/fixtures/comp6.js @@ -1,300 +1,348 @@ export default { - _id: '634d14afcfc5b40fecb8fb12', - title: '4824-5', - name: '48245', - path: '48245', - type: 'form', - display: 'form', - tags: [], - owner: '6080329e2c806a03c1e15aa4', - components: [ - { - label: 'Data Grid', - reorder: false, - addAnotherPosition: 'bottom', - layoutFixed: false, - enableRowGroups: false, - initEmpty: false, - tableView: false, - defaultValue: [{}], - key: 'dataGrid', - type: 'datagrid', - input: true, - components: [ + _id: '634d14afcfc5b40fecb8fb12', + title: '4824-5', + name: '48245', + path: '48245', + type: 'form', + display: 'form', + tags: [], + owner: '6080329e2c806a03c1e15aa4', + components: [ { - label: 'Text Field', - tableView: true, - key: 'textField', - type: 'textfield', - input: true, - }, - { - label: 'Text Area', - autoExpand: false, - tableView: true, - key: 'textArea', - type: 'textarea', - input: true, - }, - { - label: 'Number', - mask: false, - tableView: false, - delimiter: false, - requireDecimal: false, - inputFormat: 'plain', - truncateMultipleSpaces: false, - key: 'number', - type: 'number', - input: true, - }, - { - label: 'Password', - autocomplete: 'dzmitry@form.io', - tableView: false, - key: 'password', - type: 'password', - input: true, - protected: true, - }, - { - label: 'Checkbox', - tableView: false, - key: 'checkbox', - type: 'checkbox', - input: true, - }, - { - label: 'Select Boxes', - optionsLabelPosition: 'right', - tableView: false, - values: [ - { label: 'Pamela Raynor', value: 'pamelaRaynor', shortcut: '' }, - { label: 'Evert Dickinson', value: 'evertDickinson', shortcut: '' }, - { label: 'Paige Brekke', value: 'paigeBrekke', shortcut: '' }, - ], - key: 'selectBoxes', - type: 'selectboxes', - input: true, - inputType: 'checkbox', - }, - { - label: 'Select', - widget: 'choicesjs', - tableView: true, - data: { - values: [ - { label: 'Aliza Hessel', value: 'alizaHessel' }, - { label: 'Carlie Nikolaus', value: 'carlieNikolaus' }, - { label: 'Jettie Wolf', value: 'jettieWolf' }, + label: 'Data Grid', + reorder: false, + addAnotherPosition: 'bottom', + layoutFixed: false, + enableRowGroups: false, + initEmpty: false, + tableView: false, + defaultValue: [{}], + key: 'dataGrid', + type: 'datagrid', + input: true, + components: [ + { + label: 'Text Field', + tableView: true, + key: 'textField', + type: 'textfield', + input: true, + }, + { + label: 'Text Area', + autoExpand: false, + tableView: true, + key: 'textArea', + type: 'textarea', + input: true, + }, + { + label: 'Number', + mask: false, + tableView: false, + delimiter: false, + requireDecimal: false, + inputFormat: 'plain', + truncateMultipleSpaces: false, + key: 'number', + type: 'number', + input: true, + }, + { + label: 'Password', + autocomplete: 'dzmitry@form.io', + tableView: false, + key: 'password', + type: 'password', + input: true, + protected: true, + }, + { + label: 'Checkbox', + tableView: false, + key: 'checkbox', + type: 'checkbox', + input: true, + }, + { + label: 'Select Boxes', + optionsLabelPosition: 'right', + tableView: false, + values: [ + { + label: 'Pamela Raynor', + value: 'pamelaRaynor', + shortcut: '', + }, + { + label: 'Evert Dickinson', + value: 'evertDickinson', + shortcut: '', + }, + { + label: 'Paige Brekke', + value: 'paigeBrekke', + shortcut: '', + }, + ], + key: 'selectBoxes', + type: 'selectboxes', + input: true, + inputType: 'checkbox', + }, + { + label: 'Select', + widget: 'choicesjs', + tableView: true, + data: { + values: [ + { label: 'Aliza Hessel', value: 'alizaHessel' }, + { + label: 'Carlie Nikolaus', + value: 'carlieNikolaus', + }, + { label: 'Jettie Wolf', value: 'jettieWolf' }, + ], + }, + key: 'select', + type: 'select', + input: true, + }, + { + label: 'Radio', + optionsLabelPosition: 'right', + inline: false, + tableView: false, + values: [ + { + label: 'Anabelle Spinka', + value: 'anabelleSpinka', + shortcut: '', + }, + { + label: 'Osvaldo Krajcik', + value: 'osvaldoKrajcik', + shortcut: '', + }, + { + label: 'Cierra Reichel', + value: 'cierraReichel', + shortcut: '', + }, + ], + key: 'radio', + type: 'radio', + input: true, + }, + { + label: 'Email', + tableView: true, + key: 'email', + type: 'email', + input: true, + }, + { + label: 'Url', + tableView: true, + key: 'url', + type: 'url', + input: true, + }, + { + label: 'Phone Number', + tableView: true, + key: 'phoneNumber', + type: 'phoneNumber', + input: true, + }, + { + label: 'Tags', + tableView: false, + key: 'tags', + type: 'tags', + input: true, + }, + { + label: 'Address', + tableView: true, + provider: 'google', + key: 'address', + type: 'address', + providerOptions: { + params: { + autocompleteOptions: {}, + key: '', + }, + }, + input: true, + components: [ + { + label: 'Address 1', + tableView: false, + key: 'address1', + type: 'textfield', + input: true, + customConditional: + "show = _.get(instance, 'parent.manualMode', false);", + }, + { + label: 'Address 2', + tableView: false, + key: 'address2', + type: 'textfield', + input: true, + customConditional: + "show = _.get(instance, 'parent.manualMode', false);", + }, + { + label: 'City', + tableView: false, + key: 'city', + type: 'textfield', + input: true, + customConditional: + "show = _.get(instance, 'parent.manualMode', false);", + }, + { + label: 'State', + tableView: false, + key: 'state', + type: 'textfield', + input: true, + customConditional: + "show = _.get(instance, 'parent.manualMode', false);", + }, + { + label: 'Country', + tableView: false, + key: 'country', + type: 'textfield', + input: true, + customConditional: + "show = _.get(instance, 'parent.manualMode', false);", + }, + { + label: 'Zip Code', + tableView: false, + key: 'zip', + type: 'textfield', + input: true, + customConditional: + "show = _.get(instance, 'parent.manualMode', false);", + }, + ], + }, + { + label: 'Date / Time', + tableView: false, + datePicker: { + disableWeekends: false, + disableWeekdays: false, + }, + enableMinDateInput: false, + enableMaxDateInput: false, + key: 'dateTime', + type: 'datetime', + input: true, + widget: { + type: 'calendar', + displayInTimezone: 'viewer', + locale: 'en', + useLocaleSettings: false, + allowInput: true, + mode: 'single', + enableTime: true, + noCalendar: false, + format: 'yyyy-MM-dd hh:mm a', + hourIncrement: 1, + minuteIncrement: 1, + time_24hr: false, + minDate: null, + disableWeekends: false, + disableWeekdays: false, + maxDate: null, + }, + }, + { + label: 'Day', + hideInputLabels: false, + inputsLabelPosition: 'top', + useLocaleSettings: false, + tableView: false, + fields: { + day: { hide: false }, + month: { hide: false }, + year: { hide: false }, + }, + key: 'day', + type: 'day', + input: true, + defaultValue: '00/00/0000', + }, + { + label: 'Time', + tableView: true, + key: 'time', + type: 'time', + input: true, + inputMask: '99:99', + }, + { + label: 'Currency', + mask: false, + spellcheck: true, + tableView: false, + currency: 'USD', + inputFormat: 'plain', + truncateMultipleSpaces: false, + key: 'currency', + type: 'currency', + input: true, + delimiter: true, + }, + { + label: 'Survey', + tableView: false, + questions: [ + { label: 'Ole Ferry', value: 'oleFerry', tooltip: '' }, + { + label: 'Kariane Erdman', + value: 'karianeErdman', + tooltip: '', + }, + ], + values: [ + { + label: 'Alexa Corwin', + value: 'alexaCorwin', + tooltip: '', + }, + { + label: 'Susie Schiller', + value: 'susieSchiller', + tooltip: '', + }, + ], + key: 'survey', + type: 'survey', + input: true, + }, ], - }, - key: 'select', - type: 'select', - input: true, - }, - { - label: 'Radio', - optionsLabelPosition: 'right', - inline: false, - tableView: false, - values: [ - { label: 'Anabelle Spinka', value: 'anabelleSpinka', shortcut: '' }, - { label: 'Osvaldo Krajcik', value: 'osvaldoKrajcik', shortcut: '' }, - { label: 'Cierra Reichel', value: 'cierraReichel', shortcut: '' }, - ], - key: 'radio', - type: 'radio', - input: true, - }, - { - label: 'Email', - tableView: true, - key: 'email', - type: 'email', - input: true, - }, - { label: 'Url', tableView: true, key: 'url', type: 'url', input: true }, - { - label: 'Phone Number', - tableView: true, - key: 'phoneNumber', - type: 'phoneNumber', - input: true, - }, - { - label: 'Tags', - tableView: false, - key: 'tags', - type: 'tags', - input: true, - }, - { - label: 'Address', - tableView: true, - provider: 'google', - key: 'address', - type: 'address', - providerOptions: { - params: { - autocompleteOptions: {}, - key: '', - }, - }, - input: true, - components: [ - { - label: 'Address 1', - tableView: false, - key: 'address1', - type: 'textfield', - input: true, - customConditional: - 'show = _.get(instance, \'parent.manualMode\', false);', - }, - { - label: 'Address 2', - tableView: false, - key: 'address2', - type: 'textfield', - input: true, - customConditional: - 'show = _.get(instance, \'parent.manualMode\', false);', - }, - { - label: 'City', - tableView: false, - key: 'city', - type: 'textfield', - input: true, - customConditional: - 'show = _.get(instance, \'parent.manualMode\', false);', - }, - { - label: 'State', - tableView: false, - key: 'state', - type: 'textfield', - input: true, - customConditional: - 'show = _.get(instance, \'parent.manualMode\', false);', - }, - { - label: 'Country', - tableView: false, - key: 'country', - type: 'textfield', - input: true, - customConditional: - 'show = _.get(instance, \'parent.manualMode\', false);', - }, - { - label: 'Zip Code', - tableView: false, - key: 'zip', - type: 'textfield', - input: true, - customConditional: - 'show = _.get(instance, \'parent.manualMode\', false);', - }, - ], - }, - { - label: 'Date / Time', - tableView: false, - datePicker: { disableWeekends: false, disableWeekdays: false }, - enableMinDateInput: false, - enableMaxDateInput: false, - key: 'dateTime', - type: 'datetime', - input: true, - widget: { - type: 'calendar', - displayInTimezone: 'viewer', - locale: 'en', - useLocaleSettings: false, - allowInput: true, - mode: 'single', - enableTime: true, - noCalendar: false, - format: 'yyyy-MM-dd hh:mm a', - hourIncrement: 1, - minuteIncrement: 1, - 'time_24hr': false, - minDate: null, - disableWeekends: false, - disableWeekdays: false, - maxDate: null, - }, - }, - { - label: 'Day', - hideInputLabels: false, - inputsLabelPosition: 'top', - useLocaleSettings: false, - tableView: false, - fields: { - day: { hide: false }, - month: { hide: false }, - year: { hide: false }, - }, - key: 'day', - type: 'day', - input: true, - defaultValue: '00/00/0000', - }, - { - label: 'Time', - tableView: true, - key: 'time', - type: 'time', - input: true, - inputMask: '99:99', - }, - { - label: 'Currency', - mask: false, - spellcheck: true, - tableView: false, - currency: 'USD', - inputFormat: 'plain', - truncateMultipleSpaces: false, - key: 'currency', - type: 'currency', - input: true, - delimiter: true, }, { - label: 'Survey', - tableView: false, - questions: [ - { label: 'Ole Ferry', value: 'oleFerry', tooltip: '' }, - { label: 'Kariane Erdman', value: 'karianeErdman', tooltip: '' }, - ], - values: [ - { label: 'Alexa Corwin', value: 'alexaCorwin', tooltip: '' }, - { label: 'Susie Schiller', value: 'susieSchiller', tooltip: '' }, - ], - key: 'survey', - type: 'survey', - input: true, + type: 'button', + label: 'Submit', + key: 'submit', + disableOnInvalid: true, + input: true, + tableView: false, }, - ], - }, - { - type: 'button', - label: 'Submit', - key: 'submit', - disableOnInvalid: true, - input: true, - tableView: false, - }, - ], - settings: { theme: 'Materia' }, - properties: {}, - project: '61029d3b4c9d4e24e774bb15', - _vid: 0, - created: '2022-10-17T08:39:11.941Z', - modified: '2022-10-19T08:24:10.344Z', - machineName: 'dev-pvbwkiwgifflcai:48245', + ], + settings: { theme: 'Materia' }, + properties: {}, + project: '61029d3b4c9d4e24e774bb15', + _vid: 0, + created: '2022-10-17T08:39:11.941Z', + modified: '2022-10-19T08:24:10.344Z', + machineName: 'dev-pvbwkiwgifflcai:48245', }; diff --git a/src/components/datagrid/fixtures/comp7.js b/src/components/datagrid/fixtures/comp7.js index f8fa4559a6..026bd9db2d 100644 --- a/src/components/datagrid/fixtures/comp7.js +++ b/src/components/datagrid/fixtures/comp7.js @@ -6,28 +6,26 @@ export default { enableRowGroups: false, initEmpty: false, tableView: false, - defaultValue: [ - {} - ], + defaultValue: [{}], key: 'dataGrid', type: 'datagrid', input: true, components: [ - { - key: 'fieldSet', - type: 'fieldset', - label: 'Field Set', - input: false, - tableView: false, - components: [ - { - label: 'Text Field', - tableView: true, - key: 'textField', - type: 'textfield', - input: true - } - ] - } - ] + { + key: 'fieldSet', + type: 'fieldset', + label: 'Field Set', + input: false, + tableView: false, + components: [ + { + label: 'Text Field', + tableView: true, + key: 'textField', + type: 'textfield', + input: true, + }, + ], + }, + ], }; diff --git a/src/components/datagrid/fixtures/comp8.js b/src/components/datagrid/fixtures/comp8.js index 52d1dcd2b0..1b5f4c23f5 100644 --- a/src/components/datagrid/fixtures/comp8.js +++ b/src/components/datagrid/fixtures/comp8.js @@ -6,30 +6,28 @@ export default { enableRowGroups: false, initEmpty: false, tableView: false, - defaultValue: [ - {} - ], + defaultValue: [{}], key: 'dataGrid', type: 'datagrid', input: true, components: [ - { - label: 'Submit', - showValidations: false, - hideLabel: true, - tableView: false, - key: 'submit', - type: 'button', - saveOnEnter: false, - input: true - }, - { - label: 'Text Field', - dataGridLabel: true, - tableView: true, - key: 'textField', - type: 'textfield', - input: true - } - ] + { + label: 'Submit', + showValidations: false, + hideLabel: true, + tableView: false, + key: 'submit', + type: 'button', + saveOnEnter: false, + input: true, + }, + { + label: 'Text Field', + dataGridLabel: true, + tableView: true, + key: 'textField', + type: 'textfield', + input: true, + }, + ], }; diff --git a/src/components/datagrid/fixtures/index.js b/src/components/datagrid/fixtures/index.js index 7b3ed8a9f5..3a0ebc64d2 100644 --- a/src/components/datagrid/fixtures/index.js +++ b/src/components/datagrid/fixtures/index.js @@ -14,4 +14,21 @@ import withLogic from './comp-with-logic'; import withCollapsibleRowGroups from './comp-with-collapsible-groups'; import withAllowCalculateOverride from './comp-with-allow-calculate-override'; import twoWithAllowCalculatedOverride from './two-comp-with-allow-calculate-override'; -export { comp1, comp2, comp3, comp4, comp5, comp6, comp7, comp8, withCollapsibleRowGroups, withConditionalFieldsAndValidations, withDefValue, withLogic, withRowGroupsAndDefValue, modalWithRequiredFields, withAllowCalculateOverride, twoWithAllowCalculatedOverride }; +export { + comp1, + comp2, + comp3, + comp4, + comp5, + comp6, + comp7, + comp8, + withCollapsibleRowGroups, + withConditionalFieldsAndValidations, + withDefValue, + withLogic, + withRowGroupsAndDefValue, + modalWithRequiredFields, + withAllowCalculateOverride, + twoWithAllowCalculatedOverride, +}; diff --git a/src/components/datagrid/fixtures/two-comp-with-allow-calculate-override.js b/src/components/datagrid/fixtures/two-comp-with-allow-calculate-override.js index 206982202e..41889dab4b 100644 --- a/src/components/datagrid/fixtures/two-comp-with-allow-calculate-override.js +++ b/src/components/datagrid/fixtures/two-comp-with-allow-calculate-override.js @@ -1,102 +1,104 @@ export default { - type: 'form', - display: 'form', - components: [ - { - label: 'Select', - widget: 'choicesjs', - tableView: true, - data: { - values: [ - { - label: 'a', - value: 'a' - }, - { - label: 'b', - value: 'b' - }, - { - label: 'c', - value: 'c' - } - ] - }, - key: 'select', - type: 'select', - input: true - }, - { - label: 'Data Grid', - reorder: false, - addAnotherPosition: 'bottom', - layoutFixed: false, - enableRowGroups: false, - initEmpty: false, - tableView: false, - defaultValue: [ + type: 'form', + display: 'form', + components: [ { - firstName: '', - lastName: '' - } - ], - calculateValue: "var temp = instance.defaultValue;\n if(data.select === 'a')\n {\n temp = [{'firstName': 'A f 1','lastName': 'A l 1'}];\n } else if(data.select === 'b') { \n temp = [{'firstName': 'B f 1','lastName': 'B l 1'} \n ,{'firstName': 'B f 2','lastName': 'B l 2'}];\n } else if(data.select === 'c') { \n temp = [{'firstName': 'C f 1','lastName': 'C l 1'}];\n }\n value = temp;", - allowCalculateOverride: true, - key: 'dataGrid', - type: 'datagrid', - input: true, - components: [ - { - label: 'First Name', - tableView: true, - key: 'firstName', - type: 'textfield', - input: true + label: 'Select', + widget: 'choicesjs', + tableView: true, + data: { + values: [ + { + label: 'a', + value: 'a', + }, + { + label: 'b', + value: 'b', + }, + { + label: 'c', + value: 'c', + }, + ], + }, + key: 'select', + type: 'select', + input: true, }, { - label: 'Last Name', - tableView: true, - key: 'lastName', - type: 'textfield', - input: true - } - ] - }, - { - label: 'Data Grid 2', - reorder: false, - addAnotherPosition: 'bottom', - layoutFixed: false, - enableRowGroups: false, - initEmpty: false, - tableView: false, - defaultValue: [ - { - firstName: '', - lastName: '' - } - ], - calculateValue: "var temp = instance.defaultValue;\n if(data.select === 'a')\n {\n temp = [{'firstName': 'A f 1','lastName': 'A l 1'}];\n } else if(data.select === 'b') { \n temp = [{'firstName': 'B f 1','lastName': 'B l 1'} \n ,{'firstName': 'B f 2','lastName': 'B l 2'}];\n } else if(data.select === 'c') { \n temp = [{'firstName': 'C f 1','lastName': 'C l 1'}];\n }\n value = temp;", - allowCalculateOverride: true, - key: 'dataGrid2', - type: 'datagrid', - input: true, - components: [ - { - label: 'First Name', - tableView: true, - key: 'firstName', - type: 'textfield', - input: true + label: 'Data Grid', + reorder: false, + addAnotherPosition: 'bottom', + layoutFixed: false, + enableRowGroups: false, + initEmpty: false, + tableView: false, + defaultValue: [ + { + firstName: '', + lastName: '', + }, + ], + calculateValue: + "var temp = instance.defaultValue;\n if(data.select === 'a')\n {\n temp = [{'firstName': 'A f 1','lastName': 'A l 1'}];\n } else if(data.select === 'b') { \n temp = [{'firstName': 'B f 1','lastName': 'B l 1'} \n ,{'firstName': 'B f 2','lastName': 'B l 2'}];\n } else if(data.select === 'c') { \n temp = [{'firstName': 'C f 1','lastName': 'C l 1'}];\n }\n value = temp;", + allowCalculateOverride: true, + key: 'dataGrid', + type: 'datagrid', + input: true, + components: [ + { + label: 'First Name', + tableView: true, + key: 'firstName', + type: 'textfield', + input: true, + }, + { + label: 'Last Name', + tableView: true, + key: 'lastName', + type: 'textfield', + input: true, + }, + ], }, { - label: 'Last Name', - tableView: true, - key: 'lastName', - type: 'textfield', - input: true - } - ] - } - ] + label: 'Data Grid 2', + reorder: false, + addAnotherPosition: 'bottom', + layoutFixed: false, + enableRowGroups: false, + initEmpty: false, + tableView: false, + defaultValue: [ + { + firstName: '', + lastName: '', + }, + ], + calculateValue: + "var temp = instance.defaultValue;\n if(data.select === 'a')\n {\n temp = [{'firstName': 'A f 1','lastName': 'A l 1'}];\n } else if(data.select === 'b') { \n temp = [{'firstName': 'B f 1','lastName': 'B l 1'} \n ,{'firstName': 'B f 2','lastName': 'B l 2'}];\n } else if(data.select === 'c') { \n temp = [{'firstName': 'C f 1','lastName': 'C l 1'}];\n }\n value = temp;", + allowCalculateOverride: true, + key: 'dataGrid2', + type: 'datagrid', + input: true, + components: [ + { + label: 'First Name', + tableView: true, + key: 'firstName', + type: 'textfield', + input: true, + }, + { + label: 'Last Name', + tableView: true, + key: 'lastName', + type: 'textfield', + input: true, + }, + ], + }, + ], }; diff --git a/src/components/datamap/DataMap.form.js b/src/components/datamap/DataMap.form.js index 2967af769f..7c95e71c9c 100644 --- a/src/components/datamap/DataMap.form.js +++ b/src/components/datamap/DataMap.form.js @@ -2,15 +2,18 @@ import Components from '../Components'; import DataMapEditData from './editForm/DataMap.edit.data'; import DataMapEditDisplay from './editForm/DataMap.edit.display'; -export default function(...extend) { - return Components.baseEditForm([ - { - key: 'display', - components: DataMapEditDisplay - }, - { - key: 'data', - components: DataMapEditData - }, - ], ...extend); +export default function (...extend) { + return Components.baseEditForm( + [ + { + key: 'display', + components: DataMapEditDisplay, + }, + { + key: 'data', + components: DataMapEditData, + }, + ], + ...extend, + ); } diff --git a/src/components/datamap/DataMap.js b/src/components/datamap/DataMap.js index c8fbede942..246185c967 100644 --- a/src/components/datamap/DataMap.js +++ b/src/components/datamap/DataMap.js @@ -2,345 +2,374 @@ import Component from '../_classes/component/Component'; import DataGridComponent from '../datagrid/DataGrid'; import _ from 'lodash'; import EventEmitter from 'eventemitter3'; -import { componentValueTypes, getComponentSavedTypes, uniqueKey } from '../../utils/utils'; +import { + componentValueTypes, + getComponentSavedTypes, + uniqueKey, +} from '../../utils/utils'; export default class DataMapComponent extends DataGridComponent { - static schema(...extend) { - return Component.schema({ - label: 'Data Map', - key: 'dataMap', - type: 'datamap', - clearOnHide: true, - addAnother: 'Add Another', - disableAddingRemovingRows: false, - keyBeforeValue: true, - valueComponent: { - type: 'textfield', - key: 'value', - label: 'Value', - input: true - }, - input: true, - validate: { - maxLength: 0, - minLength: 0 - } - }, ...extend); - } - - static get builderInfo() { - return { - title: 'Data Map', - icon: 'th-list', - group: 'data', - documentation: '/userguide/form-building/data-components#data-map', - showPreview: false, - weight: 20, - schema: DataMapComponent.schema() - }; - } - - get schema() { - const schema = super.schema; - if (this.components && (this.components.length > 0)) { - schema.valueComponent = this.components[this.components.length - 1].schema; - } - return _.omit(schema, 'components'); - } - - static savedValueTypes(schema) { - return getComponentSavedTypes(schema) || [componentValueTypes.object]; - } - - constructor(component, options, data) { - super(component, options, data); - this.type = 'datamap'; - } - - init() { - this.components = []; - this.rows = []; - this.createRows(); - this.visibleColumns = { - key: true, - [this.valueKey]: true - }; - this.component.valueComponent.hideLabel = true; - } - - get defaultSchema() { - return DataMapComponent.schema(); - } - - get emptyValue() { - return {}; - } - - get dataValue() { - if ( - !this.key || - (!this.visible && this.component.clearOnHide) - ) { - return this.emptyValue; - } - if (!this.hasValue() && this.shouldAddDefaultValue) { - this.dataValue = this.emptyValue; - } - return _.get(this.data, this.key); - } - - set dataValue(value) { - super.dataValue = value; - } - - get defaultValue() { - const value = super.defaultValue; - if (Array.isArray(value)) { - return value[0]; - } - return this.emptyValue; - } - - get keySchema() { - return { - type: 'textfield', - input: true, - hideLabel: true, - label: this.component.keyLabel || 'Key', - key: '__key', - disableBuilderActions: true, - }; - } - - get valueKey() { - return this.component.valueComponent.key; - } - - getRowValues() { - const dataValue = this.dataValue; - if (this.builderMode) { - return [dataValue]; - } - if (_.isEmpty(dataValue)) { - return []; - } - - return Object.keys(dataValue).map(() => dataValue); - } - - getComponentsContainer() { - if (this.builderMode) { - return this.getComponents().map(comp => comp.component); - } - - return super.getComponentsContainer(); - } - - get iteratableRows() { - return this.rows.map((row) => { - return Object.keys(row).map(key => ({ - components: row[key], - data: row[key].dataValue, - })); - }); - } - - componentContext(component) { - return this.iteratableRows[component.row].find(comp => comp.components.key === component.key).data; - } - - hasHeader() { - return true; - } - - hasRemoveButtons() { - return !this.component.disableAddingRemovingRows && - !this.options.readOnly && - !this.disabled && - this.fullMode; - } - - getColumns() { - const keySchema = Object.assign({}, this.keySchema); - const valueSchema = Object.assign({}, this.component.valueComponent); - keySchema.hideLabel = false; - valueSchema.hideLabel = false; - return this.component.keyBeforeValue ? - [keySchema, valueSchema] : - [valueSchema, keySchema]; - } - - getRowKey(rowIndex) { - const keys = Object.keys(this.dataValue); - if (!keys[rowIndex]) { - keys[rowIndex] = uniqueKey(this.dataValue, this.defaultRowKey); - } - return keys[rowIndex]; - } - - get defaultRowKey() { - return 'key'; - } - - setRowComponentsData(rowIndex, rowData) { - _.each(this.rows[rowIndex], (component) => { - if (component.key === '__key') { - component.data = { - '__key': Object.keys(rowData)[rowIndex], + static schema(...extend) { + return Component.schema( + { + label: 'Data Map', + key: 'dataMap', + type: 'datamap', + clearOnHide: true, + addAnother: 'Add Another', + disableAddingRemovingRows: false, + keyBeforeValue: true, + valueComponent: { + type: 'textfield', + key: 'value', + label: 'Value', + input: true, + }, + input: true, + validate: { + maxLength: 0, + minLength: 0, + }, + }, + ...extend, + ); + } + + static get builderInfo() { + return { + title: 'Data Map', + icon: 'th-list', + group: 'data', + documentation: '/userguide/form-building/data-components#data-map', + showPreview: false, + weight: 20, + schema: DataMapComponent.schema(), + }; + } + + get schema() { + const schema = super.schema; + if (this.components && this.components.length > 0) { + schema.valueComponent = + this.components[this.components.length - 1].schema; + } + return _.omit(schema, 'components'); + } + + static savedValueTypes(schema) { + return getComponentSavedTypes(schema) || [componentValueTypes.object]; + } + + constructor(component, options, data) { + super(component, options, data); + this.type = 'datamap'; + } + + init() { + this.components = []; + this.rows = []; + this.createRows(); + this.visibleColumns = { + key: true, + [this.valueKey]: true, + }; + this.component.valueComponent.hideLabel = true; + } + + get defaultSchema() { + return DataMapComponent.schema(); + } + + get emptyValue() { + return {}; + } + + get dataValue() { + if (!this.key || (!this.visible && this.component.clearOnHide)) { + return this.emptyValue; + } + if (!this.hasValue() && this.shouldAddDefaultValue) { + this.dataValue = this.emptyValue; + } + return _.get(this.data, this.key); + } + + set dataValue(value) { + super.dataValue = value; + } + + get defaultValue() { + const value = super.defaultValue; + if (Array.isArray(value)) { + return value[0]; + } + return this.emptyValue; + } + + get keySchema() { + return { + type: 'textfield', + input: true, + hideLabel: true, + label: this.component.keyLabel || 'Key', + key: '__key', + disableBuilderActions: true, }; - } - else { - component.data = rowData; - } - }); - } - - getValueAsString(value, options) { - if (options?.email && this.visible && !this.skipInEmail && _.isObject(value)) { - let result = (` + } + + get valueKey() { + return this.component.valueComponent.key; + } + + getRowValues() { + const dataValue = this.dataValue; + if (this.builderMode) { + return [dataValue]; + } + if (_.isEmpty(dataValue)) { + return []; + } + + return Object.keys(dataValue).map(() => dataValue); + } + + getComponentsContainer() { + if (this.builderMode) { + return this.getComponents().map((comp) => comp.component); + } + + return super.getComponentsContainer(); + } + + get iteratableRows() { + return this.rows.map((row) => { + return Object.keys(row).map((key) => ({ + components: row[key], + data: row[key].dataValue, + })); + }); + } + + componentContext(component) { + return this.iteratableRows[component.row].find( + (comp) => comp.components.key === component.key, + ).data; + } + + hasHeader() { + return true; + } + + hasRemoveButtons() { + return ( + !this.component.disableAddingRemovingRows && + !this.options.readOnly && + !this.disabled && + this.fullMode + ); + } + + getColumns() { + const keySchema = Object.assign({}, this.keySchema); + const valueSchema = Object.assign({}, this.component.valueComponent); + keySchema.hideLabel = false; + valueSchema.hideLabel = false; + return this.component.keyBeforeValue + ? [keySchema, valueSchema] + : [valueSchema, keySchema]; + } + + getRowKey(rowIndex) { + const keys = Object.keys(this.dataValue); + if (!keys[rowIndex]) { + keys[rowIndex] = uniqueKey(this.dataValue, this.defaultRowKey); + } + return keys[rowIndex]; + } + + get defaultRowKey() { + return 'key'; + } + + setRowComponentsData(rowIndex, rowData) { + _.each(this.rows[rowIndex], (component) => { + if (component.key === '__key') { + component.data = { + __key: Object.keys(rowData)[rowIndex], + }; + } else { + component.data = rowData; + } + }); + } + + getValueAsString(value, options) { + if ( + options?.email && + this.visible && + !this.skipInEmail && + _.isObject(value) + ) { + let result = ` - `); + `; - result = Object.keys(value).reduce((result, key) => { - result += (` + result = Object.keys(value).reduce((result, key) => { + result += ` - + - `); - return result; - }, result); + `; + return result; + }, result); - result += (` + result += `
    ${key}${this.getView(value[key], options)}${this.getView( + value[key], + options, + )}
    - `); - - return result; - } - if (_.isEmpty(value)) { - return ''; + `; + + return result; + } + if (_.isEmpty(value)) { + return ''; + } + if (options?.modalPreview) { + delete options.modalPreview; + return this.getDataValueAsTable(value, options); + } + + return typeof value === 'object' ? '[Complex Data]' : value; } - if (options?.modalPreview) { - delete options.modalPreview; - return this.getDataValueAsTable(value, options); - } - - return typeof value === 'object' ? '[Complex Data]' : value; - } - getDataValueAsTable(value, options) { - let result = (` + getDataValueAsTable(value, options) { + let result = ` - `); + `; - if (this.visible && _.isObject(value)) { - Object.keys(value).forEach((key) => { - result += (` + if (this.visible && _.isObject(value)) { + Object.keys(value).forEach((key) => { + result += ` - + - `); - }); - } + `; + }); + } - result += (` + result += `
    ${key}${this.getView(value[key], options)}${this.getView( + value[key], + options, + )}
    - `); - - return result; - } - - createRowComponents(row, rowIndex) { - // Use original value component API key in builder mode to be able to edit value component - let key = this.builderMode ? this.valueKey : this.getRowKey(rowIndex); - - // Create a new event emitter since fields are isolated. - const options = _.clone(this.options); - options.events = new EventEmitter(); - options.name += `[${rowIndex}]`; - options.row = `${rowIndex}`; - - const components = {}; - components['__key'] = this.createComponent(this.keySchema, options, { __key: this.builderMode ? this.defaultRowKey : key }); - components['__key'].on('componentChange', (event) => { - const dataValue = this.dataValue; - const newKey = uniqueKey(dataValue, event.value); - dataValue[newKey] = dataValue[key]; - delete dataValue[key]; - const comp = components[this.valueKey]; - comp.component.key = newKey; - comp.path = this.calculateComponentPath(comp); - key = newKey; - }); - - const valueComponent = _.clone(this.component.valueComponent); - valueComponent.key = key; - - const componentOptions = this.options; - componentOptions.row = options.row; - components[this.valueKey] = this.createComponent(valueComponent, componentOptions, this.dataValue); - return components; - } - - get canAddColumn() { - return false; - } - - addChildComponent(component) { - this.component.valueComponent = component; - } - - saveChildComponent(component) { - // Update the Value Component, the Key Component is not allowed to edit - if (component.key !== this.keySchema.key) { - this.component.valueComponent = component; - } - } - - removeChildComponent() { - const defaultSchema = DataMapComponent.schema(); - this.component.valueComponent = defaultSchema.valueComponent; - } - - addRow() { - const index = this.rows.length; - this.rows[index] = this.createRowComponents(this.dataValue, index); - this.redraw(); - this.triggerChange(); - } - - removeRow(index) { - const keys = Object.keys(this.dataValue); - if (keys[index]) { - delete this.dataValue[keys[index]]; - } - this.rows.splice(index, 1); - this.redraw(); - this.triggerChange(); - } - - setValue(value, flags = {}) { - const changed = this.hasChanged(value, this.dataValue); - this.dataValue = value; - this.createRows(); - this.updateOnChange(flags, changed); - return changed; - } - - checkColumns() { - if (this.builderMode || (!this.dataValue || !Object.keys(this.dataValue).length)) { - return { rebuild: false, show: true }; - } - - if (Object.keys(this.dataValue).length > (this.rows || []).length) { - return { rebuild: true, show: true }; - } - - return { rebuild: false, show: true }; - } + `; + + return result; + } + + createRowComponents(row, rowIndex) { + // Use original value component API key in builder mode to be able to edit value component + let key = this.builderMode ? this.valueKey : this.getRowKey(rowIndex); + + // Create a new event emitter since fields are isolated. + const options = _.clone(this.options); + options.events = new EventEmitter(); + options.name += `[${rowIndex}]`; + options.row = `${rowIndex}`; + + const components = {}; + components['__key'] = this.createComponent(this.keySchema, options, { + __key: this.builderMode ? this.defaultRowKey : key, + }); + components['__key'].on('componentChange', (event) => { + const dataValue = this.dataValue; + const newKey = uniqueKey(dataValue, event.value); + dataValue[newKey] = dataValue[key]; + delete dataValue[key]; + const comp = components[this.valueKey]; + comp.component.key = newKey; + comp.path = this.calculateComponentPath(comp); + key = newKey; + }); + + const valueComponent = _.clone(this.component.valueComponent); + valueComponent.key = key; + + const componentOptions = this.options; + componentOptions.row = options.row; + components[this.valueKey] = this.createComponent( + valueComponent, + componentOptions, + this.dataValue, + ); + return components; + } + + get canAddColumn() { + return false; + } + + addChildComponent(component) { + this.component.valueComponent = component; + } + + saveChildComponent(component) { + // Update the Value Component, the Key Component is not allowed to edit + if (component.key !== this.keySchema.key) { + this.component.valueComponent = component; + } + } + + removeChildComponent() { + const defaultSchema = DataMapComponent.schema(); + this.component.valueComponent = defaultSchema.valueComponent; + } + + addRow() { + const index = this.rows.length; + this.rows[index] = this.createRowComponents(this.dataValue, index); + this.redraw(); + this.triggerChange(); + } + + removeRow(index) { + const keys = Object.keys(this.dataValue); + if (keys[index]) { + delete this.dataValue[keys[index]]; + } + this.rows.splice(index, 1); + this.redraw(); + this.triggerChange(); + } + + setValue(value, flags = {}) { + const changed = this.hasChanged(value, this.dataValue); + this.dataValue = value; + this.createRows(); + this.updateOnChange(flags, changed); + return changed; + } + + checkColumns() { + if ( + this.builderMode || + !this.dataValue || + !Object.keys(this.dataValue).length + ) { + return { rebuild: false, show: true }; + } + + if (Object.keys(this.dataValue).length > (this.rows || []).length) { + return { rebuild: true, show: true }; + } + + return { rebuild: false, show: true }; + } } diff --git a/src/components/datamap/DataMap.unit.js b/src/components/datamap/DataMap.unit.js index 663d4fa7b2..8edbc1cf6a 100644 --- a/src/components/datamap/DataMap.unit.js +++ b/src/components/datamap/DataMap.unit.js @@ -4,59 +4,84 @@ import DataMapComponent from './DataMap'; import { comp1, formWithConditionalPanel } from './fixtures'; import assert from 'power-assert'; -describe('DataMap Component', function() { - it('Should build a data map component', function() { - return Harness.testCreate(DataMapComponent, comp1); - }); +describe('DataMap Component', function () { + it('Should build a data map component', function () { + return Harness.testCreate(DataMapComponent, comp1); + }); - it('Should get and set values within the datamap.', function() { - return Harness.testCreate(DataMapComponent, comp1).then((component) => { - Harness.testSetGet(component, { - one: 'One', - two: 'Two', - three: 'Three' - }); + it('Should get and set values within the datamap.', function () { + return Harness.testCreate(DataMapComponent, comp1).then((component) => { + Harness.testSetGet(component, { + one: 'One', + two: 'Two', + three: 'Three', + }); + }); }); - }); - it( - 'Should render data from submission properly when the Data Map is inside conditionally shown layout component', - function(done) { - Formio.createForm(document.createElement('div'), formWithConditionalPanel, { readOnly: true }) - .then((form) => { - form.submission = { - data: { - checkbox: true, - dataMap1: { - key: 'a' - }, - dataMap: { - key: 'b', - key1: 'c', - }, - }, - state: 'submitted', - }; + it('Should render data from submission properly when the Data Map is inside conditionally shown layout component', function (done) { + Formio.createForm( + document.createElement('div'), + formWithConditionalPanel, + { readOnly: true }, + ) + .then((form) => { + form.submission = { + data: { + checkbox: true, + dataMap1: { + key: 'a', + }, + dataMap: { + key: 'b', + key1: 'c', + }, + }, + state: 'submitted', + }; - setTimeout(() => { - const dataMap1 = form.getComponent(['dataMap1']); - const dataMap = form.getComponent(['dataMap']); - assert.equal(dataMap1.visible, true, 'Data Map should become visible'); - assert.equal(dataMap.visible, true, 'Data Map inside a panel should become visible'); - assert.deepEqual(dataMap1.dataValue, { - key: 'a' - }, 'Should set value of the Data Map properly'); - assert.deepEqual(dataMap.dataValue, { - key: 'b', - key1: 'c', - }, 'Should set value of the Data Map inside a panel properly'); - assert.equal(dataMap1.rows.length, 1, 'Should create rows for Data Grid'); - assert.equal(dataMap.rows.length, 2, 'Should create rows for Data Grid inside a panel'); + setTimeout(() => { + const dataMap1 = form.getComponent(['dataMap1']); + const dataMap = form.getComponent(['dataMap']); + assert.equal( + dataMap1.visible, + true, + 'Data Map should become visible', + ); + assert.equal( + dataMap.visible, + true, + 'Data Map inside a panel should become visible', + ); + assert.deepEqual( + dataMap1.dataValue, + { + key: 'a', + }, + 'Should set value of the Data Map properly', + ); + assert.deepEqual( + dataMap.dataValue, + { + key: 'b', + key1: 'c', + }, + 'Should set value of the Data Map inside a panel properly', + ); + assert.equal( + dataMap1.rows.length, + 1, + 'Should create rows for Data Grid', + ); + assert.equal( + dataMap.rows.length, + 2, + 'Should create rows for Data Grid inside a panel', + ); - done(); - }, 300); - }) - .catch(done); - } - ); + done(); + }, 300); + }) + .catch(done); + }); }); diff --git a/src/components/datamap/editForm/DataMap.edit.data.js b/src/components/datamap/editForm/DataMap.edit.data.js index 98c49f298a..370d7a14f5 100644 --- a/src/components/datamap/editForm/DataMap.edit.data.js +++ b/src/components/datamap/editForm/DataMap.edit.data.js @@ -1,10 +1,10 @@ export default [ - { - key: 'multiple', - ignore: true, - }, - { - key: 'defaultValue', - ignore: true, - } + { + key: 'multiple', + ignore: true, + }, + { + key: 'defaultValue', + ignore: true, + }, ]; diff --git a/src/components/datamap/editForm/DataMap.edit.display.js b/src/components/datamap/editForm/DataMap.edit.display.js index 2989507ad5..8854a34305 100644 --- a/src/components/datamap/editForm/DataMap.edit.display.js +++ b/src/components/datamap/editForm/DataMap.edit.display.js @@ -1,46 +1,49 @@ export default [ - { - key: 'placeholder', - ignore: true - }, - { - key: 'tabindex', - ignore: true - }, - { - type: 'textfield', - label: 'Label for Key column', - key: 'keyLabel', - tooltip: 'Provide a label text for Key column (otherwise \'Key\' will be used)', - weight: 404, - input: true - }, - { - type: 'checkbox', - label: 'Disable Adding / Removing Rows', - key: 'disableAddingRemovingRows', - tooltip: 'Check if you want to hide Add Another button and Remove Row button', - weight: 405, - input: true - }, - { - type: 'checkbox', - label: 'Show key column before value', - key: 'keyBeforeValue', - tooltip: 'Check if you would like to show the Key before the Value column.', - weight: 406, - input: true - }, - { - type: 'textfield', - label: 'Add Another Text', - key: 'addAnother', - tooltip: 'Set the text of the Add Another button.', - placeholder: 'Add Another', - weight: 410, - input: true, - customConditional(context) { - return !context.data.disableAddingRemovingRows; - } - } + { + key: 'placeholder', + ignore: true, + }, + { + key: 'tabindex', + ignore: true, + }, + { + type: 'textfield', + label: 'Label for Key column', + key: 'keyLabel', + tooltip: + "Provide a label text for Key column (otherwise 'Key' will be used)", + weight: 404, + input: true, + }, + { + type: 'checkbox', + label: 'Disable Adding / Removing Rows', + key: 'disableAddingRemovingRows', + tooltip: + 'Check if you want to hide Add Another button and Remove Row button', + weight: 405, + input: true, + }, + { + type: 'checkbox', + label: 'Show key column before value', + key: 'keyBeforeValue', + tooltip: + 'Check if you would like to show the Key before the Value column.', + weight: 406, + input: true, + }, + { + type: 'textfield', + label: 'Add Another Text', + key: 'addAnother', + tooltip: 'Set the text of the Add Another button.', + placeholder: 'Add Another', + weight: 410, + input: true, + customConditional(context) { + return !context.data.disableAddingRemovingRows; + }, + }, ]; diff --git a/src/components/datamap/fixtures/comp1.js b/src/components/datamap/fixtures/comp1.js index 77415b60a6..a774a27c37 100644 --- a/src/components/datamap/fixtures/comp1.js +++ b/src/components/datamap/fixtures/comp1.js @@ -1,13 +1,13 @@ export default { - type: 'datamap', - key: 'properties', - label: 'Properties', - input: true, - valueComponent: { - type: 'textfield', - key: 'value', - label: 'Value', - defaultValue: 'Value', - input: true - } + type: 'datamap', + key: 'properties', + label: 'Properties', + input: true, + valueComponent: { + type: 'textfield', + key: 'value', + label: 'Value', + defaultValue: 'Value', + input: true, + }, }; diff --git a/src/components/datamap/fixtures/formWithConditionalPanel.js b/src/components/datamap/fixtures/formWithConditionalPanel.js index 02e2e07f3e..faf75747ab 100644 --- a/src/components/datamap/fixtures/formWithConditionalPanel.js +++ b/src/components/datamap/fixtures/formWithConditionalPanel.js @@ -1,83 +1,83 @@ export default { - type: 'form', - display: 'form', - components: [ - { - label: 'Checkbox', - tableView: false, - key: 'checkbox', - type: 'checkbox', - input: true - }, - { - label: 'Data Map', - tableView: false, - key: 'dataMap1', - conditional: { - show: true, - conjunction: 'all', - conditions: [ - { - component: 'checkbox', - operator: 'isEqual', - value: 'true' - } - ] - }, - type: 'datamap', - input: true, - valueComponent: { - type: 'textfield', - key: 'value', - label: 'Value', - input: true, - hideLabel: true, - tableView: true - } - }, - { - collapsible: false, - key: 'panel', - conditional: { - show: true, - conjunction: 'all', - conditions: [ - { - component: 'checkbox', - operator: 'isEqual', - value: 'true' - } - ] - }, - type: 'panel', - label: 'Panel', - input: false, - tableView: false, - components: [ + type: 'form', + display: 'form', + components: [ { - label: 'Data Map', - tableView: false, - key: 'dataMap', - type: 'datamap', - input: true, - valueComponent: { - type: 'textfield', - key: 'value', - label: 'Value', + label: 'Checkbox', + tableView: false, + key: 'checkbox', + type: 'checkbox', input: true, - hideLabel: true, - tableView: true - } - } - ] - }, - { - type: 'button', - label: 'Submit', - key: 'submit', - disableOnInvalid: true, - input: true, - tableView: false - } - ], + }, + { + label: 'Data Map', + tableView: false, + key: 'dataMap1', + conditional: { + show: true, + conjunction: 'all', + conditions: [ + { + component: 'checkbox', + operator: 'isEqual', + value: 'true', + }, + ], + }, + type: 'datamap', + input: true, + valueComponent: { + type: 'textfield', + key: 'value', + label: 'Value', + input: true, + hideLabel: true, + tableView: true, + }, + }, + { + collapsible: false, + key: 'panel', + conditional: { + show: true, + conjunction: 'all', + conditions: [ + { + component: 'checkbox', + operator: 'isEqual', + value: 'true', + }, + ], + }, + type: 'panel', + label: 'Panel', + input: false, + tableView: false, + components: [ + { + label: 'Data Map', + tableView: false, + key: 'dataMap', + type: 'datamap', + input: true, + valueComponent: { + type: 'textfield', + key: 'value', + label: 'Value', + input: true, + hideLabel: true, + tableView: true, + }, + }, + ], + }, + { + type: 'button', + label: 'Submit', + key: 'submit', + disableOnInvalid: true, + input: true, + tableView: false, + }, + ], }; diff --git a/src/components/datetime/DateTime.form.js b/src/components/datetime/DateTime.form.js index 5a616bd478..1bbf9cb17a 100644 --- a/src/components/datetime/DateTime.form.js +++ b/src/components/datetime/DateTime.form.js @@ -5,31 +5,34 @@ import DateTimeEditDisplay from './editForm/DateTime.edit.display'; import DateTimeEditTime from './editForm/DateTime.edit.time'; import DateTimeEditValidation from './editForm/DateTime.edit.validation'; -export default function(...extend) { - return Components.baseEditForm([ - { - key: 'display', - components: DateTimeEditDisplay - }, - { - label: 'Date', - key: 'date', - weight: 1, - components: DateTimeEditDate - }, - { - label: 'Time', - key: 'time', - weight: 2, - components: DateTimeEditTime - }, - { - key: 'data', - components: DateTimeEditData - }, - { - key: 'validation', - components: DateTimeEditValidation - }, - ], ...extend); +export default function (...extend) { + return Components.baseEditForm( + [ + { + key: 'display', + components: DateTimeEditDisplay, + }, + { + label: 'Date', + key: 'date', + weight: 1, + components: DateTimeEditDate, + }, + { + label: 'Time', + key: 'time', + weight: 2, + components: DateTimeEditTime, + }, + { + key: 'data', + components: DateTimeEditData, + }, + { + key: 'validation', + components: DateTimeEditValidation, + }, + ], + ...extend, + ); } diff --git a/src/components/datetime/DateTime.js b/src/components/datetime/DateTime.js index 573c1356db..bef101f3ef 100644 --- a/src/components/datetime/DateTime.js +++ b/src/components/datetime/DateTime.js @@ -5,213 +5,281 @@ import { componentValueTypes, getComponentSavedTypes } from '../../utils/utils'; import Input from '../_classes/input/Input'; export default class DateTimeComponent extends Input { - static schema(...extend) { - return Input.schema({ - type: 'datetime', - label: 'Date / Time', - key: 'dateTime', - format: 'yyyy-MM-dd hh:mm a', - useLocaleSettings: false, - allowInput: true, - enableDate: true, - enableTime: true, - defaultValue: '', - defaultDate: '', - displayInTimezone: 'viewer', - timezone: '', - datepickerMode: 'day', - datePicker: { - showWeeks: true, - startingDay: 0, - initDate: '', - minMode: 'day', - maxMode: 'year', - yearRows: 4, - yearColumns: 5, - minDate: null, - maxDate: null - }, - timePicker: { - hourStep: 1, - minuteStep: 1, - showMeridian: true, - readonlyInput: false, - mousewheel: true, - arrowkeys: true - }, - customOptions: {}, - }, ...extend); - } - - static get builderInfo() { - return { - title: 'Date / Time', - group: 'advanced', - icon: 'calendar', - documentation: '/userguide/form-building/advanced-components#date-and-time', - weight: 40, - schema: DateTimeComponent.schema() - }; - } - - static get serverConditionSettings() { - return DateTimeComponent.conditionOperatorsSettings; - } - - static get conditionOperatorsSettings() { - return { - ...super.conditionOperatorsSettings, - operators: ['isDateEqual', 'isNotDateEqual', 'isEmpty', 'isNotEmpty','dateLessThan', 'dateGreaterThan', 'dateLessThanOrEqual','dateGreaterThanOrEqual'], - valueComponent(classComp) { + static schema(...extend) { + return Input.schema( + { + type: 'datetime', + label: 'Date / Time', + key: 'dateTime', + format: 'yyyy-MM-dd hh:mm a', + useLocaleSettings: false, + allowInput: true, + enableDate: true, + enableTime: true, + defaultValue: '', + defaultDate: '', + displayInTimezone: 'viewer', + timezone: '', + datepickerMode: 'day', + datePicker: { + showWeeks: true, + startingDay: 0, + initDate: '', + minMode: 'day', + maxMode: 'year', + yearRows: 4, + yearColumns: 5, + minDate: null, + maxDate: null, + }, + timePicker: { + hourStep: 1, + minuteStep: 1, + showMeridian: true, + readonlyInput: false, + mousewheel: true, + arrowkeys: true, + }, + customOptions: {}, + }, + ...extend, + ); + } + + static get builderInfo() { return { - ...classComp, - type: 'datetime', + title: 'Date / Time', + group: 'advanced', + icon: 'calendar', + documentation: + '/userguide/form-building/advanced-components#date-and-time', + weight: 40, + schema: DateTimeComponent.schema(), + }; + } + + static get serverConditionSettings() { + return DateTimeComponent.conditionOperatorsSettings; + } + + static get conditionOperatorsSettings() { + return { + ...super.conditionOperatorsSettings, + operators: [ + 'isDateEqual', + 'isNotDateEqual', + 'isEmpty', + 'isNotEmpty', + 'dateLessThan', + 'dateGreaterThan', + 'dateLessThanOrEqual', + 'dateGreaterThanOrEqual', + ], + valueComponent(classComp) { + return { + ...classComp, + type: 'datetime', + }; + }, + }; + } + + static savedValueTypes(schema) { + schema = schema || {}; + + return getComponentSavedTypes(schema) || [componentValueTypes.date]; + } + + constructor(component, options, data) { + super(component, options, data); + const timezone = this.component.timezone || this.options.timezone; + const time24hr = !_.get( + this.component, + 'timePicker.showMeridian', + true, + ); + + // Change the format to map to the settings. + if (!this.component.enableDate) { + this.component.format = this.component.format.replace( + /yyyy-MM-dd /g, + '', + ); + } else if ( + this.component.enableDate && + !/[yMd]/.test(this.component.format) && + this.builderMode + ) { + this.component.format = `yyyy-MM-dd ${this.component.format}`; + } + + if (!this.component.enableTime) { + this.component.format = this.component.format.replace( + / hh:mm a$/g, + '', + ); + } else if ( + this.component.enableTime && + !/[mhH]/.test(this.component.format) && + this.builderMode + ) { + this.component.format = `${this.component.format} hh:mm a`; + } else if (time24hr) { + this.component.format = this.component.format.replace( + /hh:mm a$/g, + 'HH:mm', + ); + } else { + this.component.format = this.component.format.replace( + /HH:mm$/g, + 'hh:mm a', + ); + } + + let customOptions = this.component.customOptions || {}; + + if (typeof customOptions === 'string') { + try { + customOptions = JSON.parse(customOptions); + } catch (err) { + console.warn(err.message); + customOptions = {}; + } + } + + /* eslint-disable camelcase */ + this.component.widget = { + type: 'calendar', + timezone, + displayInTimezone: _.get( + this.component, + 'displayInTimezone', + 'viewer', + ), + locale: this.options.language, + useLocaleSettings: _.get( + this.component, + 'useLocaleSettings', + false, + ), + allowInput: _.get(this.component, 'allowInput', true), + mode: 'single', + enableTime: _.get(this.component, 'enableTime', true), + noCalendar: !_.get(this.component, 'enableDate', true), + format: this.component.format, + hourIncrement: _.get(this.component, 'timePicker.hourStep', 1), + minuteIncrement: _.get(this.component, 'timePicker.minuteStep', 5), + time_24hr: time24hr, + readOnly: this.options.readOnly, + minDate: _.get(this.component, 'datePicker.minDate'), + disabledDates: _.get(this.component, 'datePicker.disable'), + disableWeekends: _.get( + this.component, + 'datePicker.disableWeekends', + ), + disableWeekdays: _.get( + this.component, + 'datePicker.disableWeekdays', + ), + disableFunction: _.get( + this.component, + 'datePicker.disableFunction', + ), + maxDate: _.get(this.component, 'datePicker.maxDate'), + ...customOptions, }; - } - }; - } - - static savedValueTypes(schema) { - schema = schema || {}; - - return getComponentSavedTypes(schema) || [componentValueTypes.date]; - } - - constructor(component, options, data) { - super(component, options, data); - const timezone = (this.component.timezone || this.options.timezone); - const time24hr = !_.get(this.component, 'timePicker.showMeridian', true); - - // Change the format to map to the settings. - if (!this.component.enableDate) { - this.component.format = this.component.format.replace(/yyyy-MM-dd /g, ''); - } - else if (this.component.enableDate && !/[yMd]/.test(this.component.format) && this.builderMode) { - this.component.format = `yyyy-MM-dd ${this.component.format}`; - } - - if (!this.component.enableTime) { - this.component.format = this.component.format.replace(/ hh:mm a$/g, ''); - } - else if (this.component.enableTime && !/[mhH]/.test(this.component.format) && this.builderMode) { - this.component.format = `${this.component.format} hh:mm a`; - } - else if (time24hr) { - this.component.format = this.component.format.replace(/hh:mm a$/g, 'HH:mm'); - } - else { - this.component.format = this.component.format.replace(/HH:mm$/g, 'hh:mm a'); - } - - let customOptions = this.component.customOptions || {}; - - if (typeof customOptions === 'string') { - try { - customOptions = JSON.parse(customOptions); - } - catch (err) { - console.warn(err.message); - customOptions = {}; - } - } - - /* eslint-disable camelcase */ - this.component.widget = { - type: 'calendar', - timezone, - displayInTimezone: _.get(this.component, 'displayInTimezone', 'viewer'), - locale: this.options.language, - useLocaleSettings: _.get(this.component, 'useLocaleSettings', false), - allowInput: _.get(this.component, 'allowInput', true), - mode: 'single', - enableTime: _.get(this.component, 'enableTime', true), - noCalendar: !_.get(this.component, 'enableDate', true), - format: this.component.format, - hourIncrement: _.get(this.component, 'timePicker.hourStep', 1), - minuteIncrement: _.get(this.component, 'timePicker.minuteStep', 5), - time_24hr: time24hr, - readOnly: this.options.readOnly, - minDate: _.get(this.component, 'datePicker.minDate'), - disabledDates: _.get(this.component, 'datePicker.disable'), - disableWeekends: _.get(this.component, 'datePicker.disableWeekends'), - disableWeekdays: _.get(this.component, 'datePicker.disableWeekdays'), - disableFunction: _.get(this.component, 'datePicker.disableFunction'), - maxDate: _.get(this.component, 'datePicker.maxDate'), - ...customOptions, - }; - /* eslint-enable camelcase */ - - // Add the validators date. - this.validators.push('date'); - } - - get defaultSchema() { - return DateTimeComponent.schema(); - } - - get defaultValue() { - let defaultValue = super.defaultValue; - if (!defaultValue && this.component.defaultDate) { - defaultValue = FormioUtils.getDateSetting(this.component.defaultDate); - defaultValue = defaultValue ? defaultValue.toISOString() : ''; - } - return defaultValue; - } - - get emptyValue() { - return ''; - } - - get momentFormat() { - return FormioUtils.convertFormatToMoment(this.component.format); - } - - isEmpty(value = this.dataValue) { - if (value && (value.toString() === 'Invalid Date')) { - return true; - } - return super.isEmpty(value); - } - - formatValue(input) { - const result = moment.utc(input).toISOString(); - return result === 'Invalid date' ? input : result; - } - - isEqual(valueA, valueB = this.dataValue) { - return (this.isEmpty(valueA) && this.isEmpty(valueB)) - || moment.utc(valueA).format(this.momentFormat) === moment.utc(valueB).format(this.momentFormat); - } - - createWrapper() { - return false; - } - - checkValidity(data, dirty, rowData) { - if (this.refs.input) { - this.refs.input.forEach((input) => { - if (input.widget && input.widget.enteredDate) { - dirty = true; + /* eslint-enable camelcase */ + + // Add the validators date. + this.validators.push('date'); + } + + get defaultSchema() { + return DateTimeComponent.schema(); + } + + get defaultValue() { + let defaultValue = super.defaultValue; + if (!defaultValue && this.component.defaultDate) { + defaultValue = FormioUtils.getDateSetting( + this.component.defaultDate, + ); + defaultValue = defaultValue ? defaultValue.toISOString() : ''; + } + return defaultValue; + } + + get emptyValue() { + return ''; + } + + get momentFormat() { + return FormioUtils.convertFormatToMoment(this.component.format); + } + + isEmpty(value = this.dataValue) { + if (value && value.toString() === 'Invalid Date') { + return true; } - }); + return super.isEmpty(value); + } + + formatValue(input) { + const result = moment.utc(input).toISOString(); + return result === 'Invalid date' ? input : result; } - return super.checkValidity(data, dirty, rowData); - } - getValueAsString(value) { - let format = FormioUtils.convertFormatToMoment(this.component.format); - format += format.match(/z$/) ? '' : ' z'; - const timezone = this.timezone; - if (value && !this.attached && timezone) { - if (Array.isArray(value) && this.component.multiple) { - return value.map(item => _.trim(FormioUtils.momentDate(item, format, timezone).format(format))).join(', '); - } - return _.trim(FormioUtils.momentDate(value, format, timezone).format(format)); + isEqual(valueA, valueB = this.dataValue) { + return ( + (this.isEmpty(valueA) && this.isEmpty(valueB)) || + moment.utc(valueA).format(this.momentFormat) === + moment.utc(valueB).format(this.momentFormat) + ); } - if (Array.isArray(value) && this.component.multiple) { - return value.map(item => _.trim(moment(item).format(format))).join(', '); + createWrapper() { + return false; + } + + checkValidity(data, dirty, rowData) { + if (this.refs.input) { + this.refs.input.forEach((input) => { + if (input.widget && input.widget.enteredDate) { + dirty = true; + } + }); + } + return super.checkValidity(data, dirty, rowData); + } + + getValueAsString(value) { + let format = FormioUtils.convertFormatToMoment(this.component.format); + format += format.match(/z$/) ? '' : ' z'; + const timezone = this.timezone; + if (value && !this.attached && timezone) { + if (Array.isArray(value) && this.component.multiple) { + return value + .map((item) => + _.trim( + FormioUtils.momentDate( + item, + format, + timezone, + ).format(format), + ), + ) + .join(', '); + } + return _.trim( + FormioUtils.momentDate(value, format, timezone).format(format), + ); + } + + if (Array.isArray(value) && this.component.multiple) { + return value + .map((item) => _.trim(moment(item).format(format))) + .join(', '); + } + return (value ? _.trim(moment(value).format(format)) : value) || ''; } - return (value ? _.trim(moment(value).format(format)) : value) || ''; - } } diff --git a/src/components/datetime/DateTime.unit.js b/src/components/datetime/DateTime.unit.js index 9c39ae042b..03a050d84a 100644 --- a/src/components/datetime/DateTime.unit.js +++ b/src/components/datetime/DateTime.unit.js @@ -5,753 +5,870 @@ import { Formio } from './../../Formio'; import _ from 'lodash'; import 'flatpickr'; import { - comp1, - comp2, - comp3, - comp5, - comp6, - comp7, - comp8, - // comp9, - comp10, - comp11, - comp12 + comp1, + comp2, + comp3, + comp5, + comp6, + comp7, + comp8, + // comp9, + comp10, + comp11, + comp12, } from './fixtures'; -describe('DateTime Component', function() { - it('Should build a date time component', function() { - return Harness.testCreate(DateTimeComponent, comp1).then((dateTime) => dateTime.destroy()); - }); - - it('Test formatting', function(done) { - Harness.testCreate(DateTimeComponent, comp2).then((dateTime) => { - const value = '2020-09-22T00:00:00'; - const formattedValue = '2020-09-22'; - dateTime.setValue(value); - setTimeout(() => { - assert.equal(dateTime.getValueAsString(value), formattedValue, 'getValueAsString should return formatted value'); - dateTime.destroy(); - done(); - }, 250); - }).catch(done); - }); - - it('Should format value', function() { - comp2.format = 'yyyy-MM-dd hh:mm a'; - return Harness.testCreate(DateTimeComponent, comp2) - .then((dateTime) => { - assert.equal(dateTime.getValueAsString('2020-09-18T12:12:00'), '2020-09-18 12:12 PM'); - dateTime.destroy(); - }); - }); - - it('Should not change manually entered value on blur when time is disabled', function(done) { - const form = _.cloneDeep(comp11); - const element = document.createElement('div'); - - Formio.createForm(element, form).then(form => { - const dateTime = form.getComponent('dateTime'); - const blurEvent = new Event('blur'); - - const value = '01-02-2021'; - const input = dateTime.element.querySelector('.input'); - input.value = value; - input.dispatchEvent(blurEvent); - - setTimeout(() => { - assert.equal(input.value, value); - document.innerHTML = ''; - done(); - }, 600); - }).catch(done); - }); - - it('Should allow manual input', function(done) { - const form = _.cloneDeep(comp3); - const element = document.createElement('div'); - - Formio.createForm(element, form).then(form => { - const dateTime = form.getComponent('dateTime'); - const blurEvent = new Event('blur'); - - const value = '2021-04-13 7:00 PM'; - const expectedValueStart = '2021-04-13T19:00:00'; - const input = dateTime.element.querySelector('.input'); - input.value = value; - input.dispatchEvent(blurEvent); - - setTimeout(() => { - assert.equal(dateTime.getValue().startsWith(expectedValueStart), true); - assert.equal(dateTime.dataValue.startsWith(expectedValueStart), true); - - document.innerHTML = ''; - done(); - }, 300); - }).catch(done); - }); - - it('Should allow manual input for date with full month format (like MMMM)', function(done) { - const form = _.cloneDeep(comp12); - const element = document.createElement('div'); - - Formio.createForm(element, form).then(form => { - const dateTime = form.getComponent('dateTime'); - const blurEvent = new Event('blur'); - - const value = 'April 22'; - const expectedValue = 'April/22'; - const input = dateTime.element.querySelector('.input'); - input.value = value; - input.dispatchEvent(blurEvent); - - setTimeout(() => { - assert.equal(input.value, expectedValue); - document.innerHTML = ''; - done(); - }, 300); - }).catch(done); - }); - - it('Should not allow manual input', function(done) { - const form = _.cloneDeep(comp3); - const element = document.createElement('div'); - form.components[0].allowInput = false; - - Formio.createForm(element, form).then(form => { - const dateTime = form.getComponent('dateTime'); - const blurEvent = new Event('blur'); - - const value = '2021-04-13 7:00 PM'; - const input = dateTime.element.querySelector('.input'); - input.value = value; - input.dispatchEvent(blurEvent); - - setTimeout(() => { - assert.equal(dateTime.getValue(), ''); - assert.equal(dateTime.dataValue, ''); - - document.innerHTML = ''; - done(); - }, 300); - }).catch(done); - }); - - it('Should format date correctly', function(done) { - const form = _.cloneDeep(comp3); - const element = document.createElement('div'); - const formatsInitial = [ - { - format: 'yyyy-dd-MM', - inputValue:'2021-15-03 11:10 AM', - setValue:'2021-03-15T00:00:00', - expectedFormattedValue:'2021-15-03' - }, - { - format: 'yyyy-dd', - inputValue:'2021-15-03 11:10 AM', - setValue:'2021-03-15T00:00:00', - expectedFormattedValue:'2021-15' - }, - { - format: 'yyyy', - inputValue:'2021-15-03 11:10 AM', - setValue:'2021-03-15T00:00:00', - expectedFormattedValue:'2021' - }, - { - format: 'dd-MM-yyyy', - inputValue:'15-03-2021 11:10 AM', - setValue:'2021-03-15T00:00:00', - expectedFormattedValue:'15-03-2021' - }, - { - format: 'MM-dd', - inputValue:'03-15-2021 11:10 AM', - setValue:'2021-03-15T00:00:00', - expectedFormattedValue:'03-15' - }, - { - format: 'dd-MM', - inputValue:'15-03-2021 11:10 AM', - setValue:'2021-03-15T00:00:00', - expectedFormattedValue:'15-03' - }, - { - format: 'MM-dd-yyyy', - inputValue:'03-15-2021 11:10 AM', - setValue:'2021-03-15T00:00:00', - expectedFormattedValue:'03-15-2021' - }, - { - format: 'yyyy-MM-dd', - inputValue:'2021-03-15 11:10 AM', - setValue:'2021-03-15T00:00:00', - expectedFormattedValue:'2021-03-15' - }, - { - format: 'dd-MM-yyyy hh:mm', - inputValue:'15-03-2021 11:10 AM', - setValue:'2021-03-15T11:10:00', - expectedFormattedValue:'15-03-2021 11:10' - }, - { - format: 'yyyy-MM-dd a', - inputValue:'2021-03-15 PM', - setValue:'2021-03-15T12:00:00', - expectedFormattedValue:'2021-03-15 PM' - }, - { - format: 'hh', - inputValue:'11:10 AM', - setValue:'2021-01-01T11:00:00', - expectedFormattedValue:'11' - }, - { - format: 'hh:mm a', - inputValue:'11:10 AM 34', - setValue:'2021-01-01T11:10:00', - expectedFormattedValue:'11:10 AM' - }, - { - format: 'mm', - inputValue:'11:10 AM', - setValue:'2021-01-01T00:11:00', - expectedFormattedValue:'11' - }, - ]; - - const getAllFormats = function(formats) { - const separators = ['.', '/']; - - const formatsWithDiffSeparators = separators.reduce((result, separator) => { - const formatWithNewSeparator = formats - .filter(format => { - return format.format.split('-').length > 1; - }) - .map(format => { - return { - ...format, - format: format.format.split('-').join(separator), - inputValue: format.inputValue.split('-').join(separator), - expectedFormattedValue: format.expectedFormattedValue.split('-').join(separator), - }; - }); - - return [...result, ...formatWithNewSeparator]; - }, []); - - return [...formats, ...formatsWithDiffSeparators]; - }; - - const formats = getAllFormats(formatsInitial); - const formComponents = []; - - formats.forEach((format, index) => { - const comp = _.cloneDeep(form.components[0]); - comp.format = format.format; - comp.widget.format = format.format; - comp.key = comp.key + index; - formComponents.push(comp); +describe('DateTime Component', function () { + it('Should build a date time component', function () { + return Harness.testCreate(DateTimeComponent, comp1).then((dateTime) => + dateTime.destroy(), + ); }); - form.components = formComponents; + it('Test formatting', function (done) { + Harness.testCreate(DateTimeComponent, comp2) + .then((dateTime) => { + const value = '2020-09-22T00:00:00'; + const formattedValue = '2020-09-22'; + dateTime.setValue(value); + setTimeout(() => { + assert.equal( + dateTime.getValueAsString(value), + formattedValue, + 'getValueAsString should return formatted value', + ); + dateTime.destroy(); + done(); + }, 250); + }) + .catch(done); + }); + + it('Should format value', function () { + comp2.format = 'yyyy-MM-dd hh:mm a'; + return Harness.testCreate(DateTimeComponent, comp2).then((dateTime) => { + assert.equal( + dateTime.getValueAsString('2020-09-18T12:12:00'), + '2020-09-18 12:12 PM', + ); + dateTime.destroy(); + }); + }); + + it('Should not change manually entered value on blur when time is disabled', function (done) { + const form = _.cloneDeep(comp11); + const element = document.createElement('div'); + + Formio.createForm(element, form) + .then((form) => { + const dateTime = form.getComponent('dateTime'); + const blurEvent = new Event('blur'); + + const value = '01-02-2021'; + const input = dateTime.element.querySelector('.input'); + input.value = value; + input.dispatchEvent(blurEvent); + + setTimeout(() => { + assert.equal(input.value, value); + document.innerHTML = ''; + done(); + }, 600); + }) + .catch(done); + }); + + it('Should allow manual input', function (done) { + const form = _.cloneDeep(comp3); + const element = document.createElement('div'); + + Formio.createForm(element, form) + .then((form) => { + const dateTime = form.getComponent('dateTime'); + const blurEvent = new Event('blur'); + + const value = '2021-04-13 7:00 PM'; + const expectedValueStart = '2021-04-13T19:00:00'; + const input = dateTime.element.querySelector('.input'); + input.value = value; + input.dispatchEvent(blurEvent); + + setTimeout(() => { + assert.equal( + dateTime.getValue().startsWith(expectedValueStart), + true, + ); + assert.equal( + dateTime.dataValue.startsWith(expectedValueStart), + true, + ); + + document.innerHTML = ''; + done(); + }, 300); + }) + .catch(done); + }); - Formio.createForm(element, form).then(form => { - form.components.forEach((comp, index) => { - comp.setValue(formats[index].setValue); - }); + it('Should allow manual input for date with full month format (like MMMM)', function (done) { + const form = _.cloneDeep(comp12); + const element = document.createElement('div'); + + Formio.createForm(element, form) + .then((form) => { + const dateTime = form.getComponent('dateTime'); + const blurEvent = new Event('blur'); + + const value = 'April 22'; + const expectedValue = 'April/22'; + const input = dateTime.element.querySelector('.input'); + input.value = value; + input.dispatchEvent(blurEvent); + + setTimeout(() => { + assert.equal(input.value, expectedValue); + document.innerHTML = ''; + done(); + }, 300); + }) + .catch(done); + }); - setTimeout(() => { - form.components.forEach((comp, index) => { - const input = comp.element.querySelector('.input'); - assert.equal(input.value, formats[index].expectedFormattedValue, 'Should format date/time value after setting value'); + it('Should not allow manual input', function (done) { + const form = _.cloneDeep(comp3); + const element = document.createElement('div'); + form.components[0].allowInput = false; + + Formio.createForm(element, form) + .then((form) => { + const dateTime = form.getComponent('dateTime'); + const blurEvent = new Event('blur'); + + const value = '2021-04-13 7:00 PM'; + const input = dateTime.element.querySelector('.input'); + input.value = value; + input.dispatchEvent(blurEvent); + + setTimeout(() => { + assert.equal(dateTime.getValue(), ''); + assert.equal(dateTime.dataValue, ''); + + document.innerHTML = ''; + done(); + }, 300); + }) + .catch(done); + }); - const blurEvent = new Event('blur'); - input.value = formats[index].inputValue; - input.dispatchEvent(blurEvent); + it('Should format date correctly', function (done) { + const form = _.cloneDeep(comp3); + const element = document.createElement('div'); + const formatsInitial = [ + { + format: 'yyyy-dd-MM', + inputValue: '2021-15-03 11:10 AM', + setValue: '2021-03-15T00:00:00', + expectedFormattedValue: '2021-15-03', + }, + { + format: 'yyyy-dd', + inputValue: '2021-15-03 11:10 AM', + setValue: '2021-03-15T00:00:00', + expectedFormattedValue: '2021-15', + }, + { + format: 'yyyy', + inputValue: '2021-15-03 11:10 AM', + setValue: '2021-03-15T00:00:00', + expectedFormattedValue: '2021', + }, + { + format: 'dd-MM-yyyy', + inputValue: '15-03-2021 11:10 AM', + setValue: '2021-03-15T00:00:00', + expectedFormattedValue: '15-03-2021', + }, + { + format: 'MM-dd', + inputValue: '03-15-2021 11:10 AM', + setValue: '2021-03-15T00:00:00', + expectedFormattedValue: '03-15', + }, + { + format: 'dd-MM', + inputValue: '15-03-2021 11:10 AM', + setValue: '2021-03-15T00:00:00', + expectedFormattedValue: '15-03', + }, + { + format: 'MM-dd-yyyy', + inputValue: '03-15-2021 11:10 AM', + setValue: '2021-03-15T00:00:00', + expectedFormattedValue: '03-15-2021', + }, + { + format: 'yyyy-MM-dd', + inputValue: '2021-03-15 11:10 AM', + setValue: '2021-03-15T00:00:00', + expectedFormattedValue: '2021-03-15', + }, + { + format: 'dd-MM-yyyy hh:mm', + inputValue: '15-03-2021 11:10 AM', + setValue: '2021-03-15T11:10:00', + expectedFormattedValue: '15-03-2021 11:10', + }, + { + format: 'yyyy-MM-dd a', + inputValue: '2021-03-15 PM', + setValue: '2021-03-15T12:00:00', + expectedFormattedValue: '2021-03-15 PM', + }, + { + format: 'hh', + inputValue: '11:10 AM', + setValue: '2021-01-01T11:00:00', + expectedFormattedValue: '11', + }, + { + format: 'hh:mm a', + inputValue: '11:10 AM 34', + setValue: '2021-01-01T11:10:00', + expectedFormattedValue: '11:10 AM', + }, + { + format: 'mm', + inputValue: '11:10 AM', + setValue: '2021-01-01T00:11:00', + expectedFormattedValue: '11', + }, + ]; + + const getAllFormats = function (formats) { + const separators = ['.', '/']; + + const formatsWithDiffSeparators = separators.reduce( + (result, separator) => { + const formatWithNewSeparator = formats + .filter((format) => { + return format.format.split('-').length > 1; + }) + .map((format) => { + return { + ...format, + format: format.format + .split('-') + .join(separator), + inputValue: format.inputValue + .split('-') + .join(separator), + expectedFormattedValue: + format.expectedFormattedValue + .split('-') + .join(separator), + }; + }); + + return [...result, ...formatWithNewSeparator]; + }, + [], + ); + + return [...formats, ...formatsWithDiffSeparators]; + }; + + const formats = getAllFormats(formatsInitial); + const formComponents = []; + + formats.forEach((format, index) => { + const comp = _.cloneDeep(form.components[0]); + comp.format = format.format; + comp.widget.format = format.format; + comp.key = comp.key + index; + formComponents.push(comp); }); - setTimeout(() => { - form.components.forEach((comp, index) => { - const input = comp.element.querySelector('.input'); - assert.equal(input.value, formats[index].expectedFormattedValue, 'Should format date/time value after inputting value'); - }); - - document.innerHTML = ''; - done(); - }, 300); - }, 300); - }).catch(done); - }).timeout(4000); - - it('Should disable weekends', function(done) { - const form = _.cloneDeep(comp3); - const element = document.createElement('div'); - form.components[0].datePicker.disableWeekends = true; - - Formio.createForm(element, form).then(form => { - const dateTime = form.getComponent('dateTime'); - const calendar = dateTime.element.querySelector('.flatpickr-input').widget.calendar; - assert.equal(calendar.config.disableWeekends, true); - - document.innerHTML = ''; - done(); - }).catch(done); - }); - - it('Should disable weekdays', function(done) { - const form = _.cloneDeep(comp3); - const element = document.createElement('div'); - form.components[0].datePicker.disableWeekdays = true; - - Formio.createForm(element, form).then(form => { - const dateTime = form.getComponent('dateTime'); - const calendar = dateTime.element.querySelector('.flatpickr-input').widget.calendar; - assert.equal(calendar.config.disableWeekdays, true); - - document.innerHTML = ''; - done(); - }).catch(done); - }); - - it('Should disable time', function(done) { - const form = _.cloneDeep(comp3); - const element = document.createElement('div'); - form.components[0].enableTime = false; - - Formio.createForm(element, form).then(form => { - const dateTime = form.getComponent('dateTime'); - const calendar = dateTime.element.querySelector('.flatpickr-input').widget.calendar; - assert.equal(calendar.config.enableTime, false); - assert.equal(!!calendar.timeContainer, false); - - document.innerHTML = ''; - done(); - }).catch(done); - }); - - it('Should disable date', function(done) { - const form = _.cloneDeep(comp3); - const element = document.createElement('div'); - form.components[0].enableDate = false; - - Formio.createForm(element, form).then(form => { - const dateTime = form.getComponent('dateTime'); - const calendar = dateTime.element.querySelector('.flatpickr-input').widget.calendar; - assert.equal(!!calendar.daysContainer, false); - - document.innerHTML = ''; - done(); - }).catch(done); - }); - - it('Should enable time', function(done) { - const form = _.cloneDeep(comp3); - const element = document.createElement('div'); - form.components[0].enableTime = true; - - Formio.createForm(element, form).then(form => { - const dateTime = form.getComponent('dateTime'); - const calendar = dateTime.element.querySelector('.flatpickr-input').widget.calendar; - assert.equal(calendar.config.enableTime, true); - assert.equal(!!calendar.timeContainer, true); - - document.innerHTML = ''; - done(); - }).catch(done); - }); - - it('Should enable date', function(done) { - const form = _.cloneDeep(comp3); - const element = document.createElement('div'); - form.components[0].enableDate = true; - - Formio.createForm(element, form).then(form => { - const dateTime = form.getComponent('dateTime'); - const calendar = dateTime.element.querySelector('.flatpickr-input').widget.calendar; - assert.equal(calendar.config.enableDate, true); - assert.equal(!!calendar.daysContainer, true); - - document.innerHTML = ''; - done(); - }).catch(done); - }); - - it('Should not input the date that is disabled', function(done) { - const form = _.cloneDeep(comp3); - const element = document.createElement('div'); - form.components[0].datePicker.disable = '2021-04-15'; - - Formio.createForm(element, form).then(form => { - const dateTime = form.getComponent('dateTime'); - const input = dateTime.element.querySelector('.input'); - - const blurEvent = new Event('blur'); - input.value = '2021-04-15'; - input.dispatchEvent(blurEvent); - - setTimeout(() => { - const input = dateTime.element.querySelector('.input'); - assert.equal(input.value, ''); - assert.equal(dateTime.dataValue, ''); - - document.innerHTML = ''; - done(); - }, 300); - }).catch(done); - }); - - it('Should not input the date that is in disabled range', function(done) { - const form = _.cloneDeep(comp3); - const element = document.createElement('div'); - form.components[0].datePicker.disable = '2021-04-15-2021-04-20'; - - Formio.createForm(element, form).then(form => { - const dateTime = form.getComponent('dateTime'); - const input = dateTime.element.querySelector('.input'); - - const blurEvent = new Event('blur'); - input.value = '2021-04-17'; - input.dispatchEvent(blurEvent); - - setTimeout(() => { - const input = dateTime.element.querySelector('.input'); - assert.equal(input.value, ''); - assert.equal(dateTime.dataValue, ''); - - document.innerHTML = ''; - done(); - }, 300); - }).catch(done); - }); - - it('Should not allow inputting the date that meets condition of "custom disabled date"', function(done) { - const form = _.cloneDeep(comp3); - const element = document.createElement('div'); - form.components[0].datePicker.disableFunction = 'date.getDay() === 2'; - - Formio.createForm(element, form).then(form => { - const dateTime = form.getComponent('dateTime'); - const input = dateTime.element.querySelector('.input'); - - const blurEvent = new Event('blur'); - input.value = '2021-04-06'; - input.dispatchEvent(blurEvent); - - setTimeout(() => { - const input = dateTime.element.querySelector('.input'); - assert.equal(input.value, ''); - assert.equal(dateTime.dataValue, ''); - - document.innerHTML = ''; - done(); - }, 300); - }).catch(done); - }); - - it('Should not allow inputting the date if it is out of min/max date range', function(done) { - const form = _.cloneDeep(comp3); - const element = document.createElement('div'); - form.components[0].datePicker.minDate = '2021-04-04T12:00:00'; - form.components[0].datePicker.maxDate = '2021-04-18T12:00:00'; - - Formio.createForm(element, form).then(form => { - const dateTime = form.getComponent('dateTime'); - const input = dateTime.element.querySelector('.input'); - - const blurEvent = new Event('blur'); - input.value = '2020-04-03'; - input.dispatchEvent(blurEvent); - - setTimeout(() => { - const input = dateTime.element.querySelector('.input'); - assert.equal(input.value, ''); - assert.equal(dateTime.dataValue, ''); - - const blurEvent = new Event('blur'); - input.value = '2022-04-13'; - input.dispatchEvent(blurEvent); - - setTimeout(() => { - const input = dateTime.element.querySelector('.input'); - assert.equal(input.value, ''); - assert.equal(dateTime.dataValue, ''); - - document.innerHTML = ''; - done(); - }, 300); - }, 300); - }).catch(done); - }); - - it('Should set hour and minutes step', function(done) { - const form = _.cloneDeep(comp3); - const element = document.createElement('div'); - form.components[0].timePicker = { hourStep:3, minuteStep:10 }; - - Formio.createForm(element, form).then(form => { - const dateTime = form.getComponent('dateTime'); - const calendar = dateTime.element.querySelector('.flatpickr-input').widget.calendar; - assert.equal(calendar.config.minuteIncrement, 10); - assert.equal(calendar.config.hourIncrement, 3); - - document.innerHTML = ''; - done(); - }).catch(done); - }); - - it('Should allow inputting 24h time', function(done) { - const form = _.cloneDeep(comp3); - const element = document.createElement('div'); - form.components[0].timePicker = { showMeridian: false }; - form.components[0].widget['time_24hr'] = true; - - Formio.createForm(element, form).then(form => { - const dateTime = form.getComponent('dateTime'); - const input = dateTime.element.querySelector('.input'); - - const blurEvent = new Event('blur'); - input.value = '2020-04-03 22:11'; - input.dispatchEvent(blurEvent); - setTimeout(() => { - const input = dateTime.element.querySelector('.input'); - assert.equal(input.value, '2020-04-03 22:11'); - assert.equal(dateTime.dataValue.startsWith('2020-04-03T22:11:00'), true); - - document.innerHTML = ''; - done(); - }, 300); - }).catch(done); - }); - - it('Should not set value if it does not meet minDate validation', function(done) { - const form = _.cloneDeep(comp5); - const element = document.createElement('div'); - - Formio.createForm(element, form).then(form => { - const dateTime = form.getComponent('dateTime'); - dateTime.setValue('2021-05-01T09:00:00'); - - setTimeout(() => { - const input = dateTime.element.querySelector('.input'); - assert.equal(input.value, ''); - - document.innerHTML = ''; - done(); - }, 300); - }).catch(done); - }); - - it('Should set value in readOnly mode even if it does not meet current minDate validation conditions', function(done) { - const form = _.cloneDeep(comp5); - const element = document.createElement('div'); - - Formio.createForm(element, form, { readOnly: true }).then(form => { - const dateTime = form.getComponent('dateTime'); - dateTime.setValue('2021-05-01T09:00:00'); - - setTimeout(() => { - const input = dateTime.element.querySelector('.input'); - assert.equal(input.value, '05/01/21'); - - document.innerHTML = ''; - done(); - }, 300); - }).catch(done); - }); - - it('Should save hours and minutes values on first change', function(done) { - const form = _.cloneDeep(comp6); - const element = document.createElement('div'); - form.components[0].enableDate = false; - - Formio.createForm(element, form).then(form => { - const dateTime = form.getComponent('dateTime'); - const blurEvent = new Event('blur'); - const input = dateTime.element.querySelector('.input'); - input.dispatchEvent(blurEvent); - - setTimeout(() => { - const calendar = dateTime.element.querySelector('.flatpickr-input').widget.calendar; - calendar._input.value = '7:00 PM'; - const expectedValue = 'T19:00:00'; - calendar._input.dispatchEvent(blurEvent); - - setTimeout(() => { - assert.equal(dateTime.dataValue.includes(expectedValue), true); - - document.innerHTML = ''; - done(); - }, 200); - }, 200); - }).catch(done); - }); - - it('Should provide correct value after submission', function(done) { - const form = _.cloneDeep(comp7); - const element = document.createElement('div'); - form.components[0].enableTime = false; - - Formio.createForm(element, form).then(form => { - const dateTime = form.getComponent('dateTime'); - dateTime.setValue('2022-12-21'); - - setTimeout(() => { - const submit = form.getComponent('submit'); - const clickEvent = new Event('click'); - const submitBtn = submit.refs.button; - submitBtn.dispatchEvent(clickEvent); - - setTimeout(() => { - assert.equal(dateTime.dataValue, '2022-12-21'); - done(); - }, 200); - }, 200); - }).catch(done); - }); - - it('Should not highlight the field when it is valid when multiple values and required validation are enabled', function(done) { - const form = _.cloneDeep(comp8); - const element = document.createElement('div'); - - Formio.createForm(element, form).then(form => { - const dateTime = form.getComponent('dateTime'); - const input1 = dateTime.element.querySelectorAll('.input')[0]; - - const blurEvent = new Event('blur'); - input1.value = '2020-04-03'; - input1.dispatchEvent(blurEvent); - - const addAnotherBtn = dateTime.refs.addButton[0]; - const clickEvent = new Event('click'); - addAnotherBtn.dispatchEvent(clickEvent); - - setTimeout(() => { - assert.equal(dateTime.refs.input.length, 2); - - const inputs = dateTime.element.querySelectorAll('.input'); - assert.equal(inputs[0].classList.contains('is-invalid'), false); - assert.equal(inputs[1].classList.contains('is-invalid'), true); - - inputs[1].value = '2020-05-05'; - inputs[1].dispatchEvent(blurEvent); - - setTimeout(() => { - const input2 = dateTime.element.querySelectorAll('.input')[1]; - assert.equal(input2.classList.contains('is-invalid'), false); - - document.innerHTML = ''; - done(); - }, 300); - }, 300); - }).catch(done); - }); - - it('Should provide correct values with time after submission', function(done) { - const form = _.cloneDeep(comp10); - const element = document.createElement('div'); - - Formio.createForm(element, form).then(form => { - const dateTime = form.getComponent('dateTime'); - const textField = form.getComponent('textField'); - - dateTime.setValue('2022-04-01T14:00:00.000'); - textField.setValue('2022-04-01T14:00:00.000'); - - setTimeout(() => { - const submit = form.getComponent('submit'); - const clickEvent = new Event('click'); - const submitBtn = submit.refs.button; - submitBtn.dispatchEvent(clickEvent); - - setTimeout(() => { - const input1 = dateTime.element.querySelector('.input'); - const input2 = textField.element.querySelector('.input'); - - assert.equal(input1.value, '2022-04-01 02:00 PM'); - assert.equal(input2.value, '2022-04-01 02:00 PM'); - done(); - }, 200); - }, 200); - }).catch(done); - }); - - it('Should add date to format if enableDate is true', function(done) { - const form = _.cloneDeep(comp3); - form.components[0].format = 'hh:mm a'; - form.components[0].enableDate = true; - const element = document.createElement('div'); - - Formio.createForm(element, form, { attachMode: 'builder' }).then(form => { - const dateTime = form.getComponent('dateTime'); - assert.equal(dateTime.component.format, 'yyyy-MM-dd hh:mm a'); - done(); - }).catch(done); - }); - - it('Should add time to format if enableTime is true', function(done) { - const form = _.cloneDeep(comp3); - form.components[0].format = 'yyyy-MM-dd'; - form.components[0].enableTime = true; - const element = document.createElement('div'); - - Formio.createForm(element, form, { attachMode: 'builder' }).then(form => { - const dateTime = form.getComponent('dateTime'); - assert.equal(dateTime.component.format, 'yyyy-MM-dd hh:mm a'); - done(); - }).catch(done); - }); - - // it('Should provide correct date in selected timezone after submission', (done) => { - // const form = _.cloneDeep(comp9); - // const element = document.createElement('div'); - - // Formio.createForm(element, form, { readOnly: true }).then(form => { - // const dateTime = form.getComponent('dateTime'); - // const dateTime1 = form.getComponent('dateTime1'); - - // dateTime.setValue('2022-04-01T00:00:00.000'); - // dateTime1.setValue('2022-04-01T00:00:00.000'); - - // document.body.addEventListener('zonesLoaded', () => { - // setTimeout(() => { - // const input = dateTime.element.querySelector('.input'); - // const input1 = dateTime1.element.querySelector('.input'); - - // assert.equal(input.value, '2022-03-31 CDT'); - // assert.equal(input1.value, '2022-04-01 KST'); - // done(); - // }, 100); - // }); - // }).catch(done); - // }); - - // it('Test Shortcut Buttons', (done) => { - // // eslint-disable-next-line no-debugger - // debugger; - // window.flatpickr = Flatpickr; - // window.ShortcutButtonsPlugin = ShortcutButtonsPlugin; - // const formElement = document.createElement('div'); - // const form = new Webform(formElement); - // form.setForm({ display: 'form', type: 'form', components: [comp2] }) - // .then(() => { - // const dateTime = form.components[0]; - // const buttonsWrappers = document.querySelectorAll('.shortcut-buttons-flatpickr-wrapper'); - // const shortcutButtons = buttonsWrappers[buttonsWrappers.length - 1].querySelectorAll('.shortcut-buttons-flatpickr-button'); - - // assert.equal(shortcutButtons.length, 1); - - // const input = dateTime.refs.input[0]; - // Harness.clickElement(dateTime, shortcutButtons[0]); - - // setTimeout(() => { - // input.widget.calendar.close(); - // setTimeout(() => { - // assert.equal(form.data.date, '2020-10-10T00:00:00+00:00'); - // dateTime.destroy(); - // done(); - // }, 250); - // }, 150); - // }).catch(done); - // }); + form.components = formComponents; + + Formio.createForm(element, form) + .then((form) => { + form.components.forEach((comp, index) => { + comp.setValue(formats[index].setValue); + }); + + setTimeout(() => { + form.components.forEach((comp, index) => { + const input = comp.element.querySelector('.input'); + assert.equal( + input.value, + formats[index].expectedFormattedValue, + 'Should format date/time value after setting value', + ); + + const blurEvent = new Event('blur'); + input.value = formats[index].inputValue; + input.dispatchEvent(blurEvent); + }); + + setTimeout(() => { + form.components.forEach((comp, index) => { + const input = comp.element.querySelector('.input'); + assert.equal( + input.value, + formats[index].expectedFormattedValue, + 'Should format date/time value after inputting value', + ); + }); + + document.innerHTML = ''; + done(); + }, 300); + }, 300); + }) + .catch(done); + }).timeout(4000); + + it('Should disable weekends', function (done) { + const form = _.cloneDeep(comp3); + const element = document.createElement('div'); + form.components[0].datePicker.disableWeekends = true; + + Formio.createForm(element, form) + .then((form) => { + const dateTime = form.getComponent('dateTime'); + const calendar = + dateTime.element.querySelector('.flatpickr-input').widget + .calendar; + assert.equal(calendar.config.disableWeekends, true); + + document.innerHTML = ''; + done(); + }) + .catch(done); + }); + + it('Should disable weekdays', function (done) { + const form = _.cloneDeep(comp3); + const element = document.createElement('div'); + form.components[0].datePicker.disableWeekdays = true; + + Formio.createForm(element, form) + .then((form) => { + const dateTime = form.getComponent('dateTime'); + const calendar = + dateTime.element.querySelector('.flatpickr-input').widget + .calendar; + assert.equal(calendar.config.disableWeekdays, true); + + document.innerHTML = ''; + done(); + }) + .catch(done); + }); + + it('Should disable time', function (done) { + const form = _.cloneDeep(comp3); + const element = document.createElement('div'); + form.components[0].enableTime = false; + + Formio.createForm(element, form) + .then((form) => { + const dateTime = form.getComponent('dateTime'); + const calendar = + dateTime.element.querySelector('.flatpickr-input').widget + .calendar; + assert.equal(calendar.config.enableTime, false); + assert.equal(!!calendar.timeContainer, false); + + document.innerHTML = ''; + done(); + }) + .catch(done); + }); + + it('Should disable date', function (done) { + const form = _.cloneDeep(comp3); + const element = document.createElement('div'); + form.components[0].enableDate = false; + + Formio.createForm(element, form) + .then((form) => { + const dateTime = form.getComponent('dateTime'); + const calendar = + dateTime.element.querySelector('.flatpickr-input').widget + .calendar; + assert.equal(!!calendar.daysContainer, false); + + document.innerHTML = ''; + done(); + }) + .catch(done); + }); + + it('Should enable time', function (done) { + const form = _.cloneDeep(comp3); + const element = document.createElement('div'); + form.components[0].enableTime = true; + + Formio.createForm(element, form) + .then((form) => { + const dateTime = form.getComponent('dateTime'); + const calendar = + dateTime.element.querySelector('.flatpickr-input').widget + .calendar; + assert.equal(calendar.config.enableTime, true); + assert.equal(!!calendar.timeContainer, true); + + document.innerHTML = ''; + done(); + }) + .catch(done); + }); + + it('Should enable date', function (done) { + const form = _.cloneDeep(comp3); + const element = document.createElement('div'); + form.components[0].enableDate = true; + + Formio.createForm(element, form) + .then((form) => { + const dateTime = form.getComponent('dateTime'); + const calendar = + dateTime.element.querySelector('.flatpickr-input').widget + .calendar; + assert.equal(calendar.config.enableDate, true); + assert.equal(!!calendar.daysContainer, true); + + document.innerHTML = ''; + done(); + }) + .catch(done); + }); + + it('Should not input the date that is disabled', function (done) { + const form = _.cloneDeep(comp3); + const element = document.createElement('div'); + form.components[0].datePicker.disable = '2021-04-15'; + + Formio.createForm(element, form) + .then((form) => { + const dateTime = form.getComponent('dateTime'); + const input = dateTime.element.querySelector('.input'); + + const blurEvent = new Event('blur'); + input.value = '2021-04-15'; + input.dispatchEvent(blurEvent); + + setTimeout(() => { + const input = dateTime.element.querySelector('.input'); + assert.equal(input.value, ''); + assert.equal(dateTime.dataValue, ''); + + document.innerHTML = ''; + done(); + }, 300); + }) + .catch(done); + }); + + it('Should not input the date that is in disabled range', function (done) { + const form = _.cloneDeep(comp3); + const element = document.createElement('div'); + form.components[0].datePicker.disable = '2021-04-15-2021-04-20'; + + Formio.createForm(element, form) + .then((form) => { + const dateTime = form.getComponent('dateTime'); + const input = dateTime.element.querySelector('.input'); + + const blurEvent = new Event('blur'); + input.value = '2021-04-17'; + input.dispatchEvent(blurEvent); + + setTimeout(() => { + const input = dateTime.element.querySelector('.input'); + assert.equal(input.value, ''); + assert.equal(dateTime.dataValue, ''); + + document.innerHTML = ''; + done(); + }, 300); + }) + .catch(done); + }); + + it('Should not allow inputting the date that meets condition of "custom disabled date"', function (done) { + const form = _.cloneDeep(comp3); + const element = document.createElement('div'); + form.components[0].datePicker.disableFunction = 'date.getDay() === 2'; + + Formio.createForm(element, form) + .then((form) => { + const dateTime = form.getComponent('dateTime'); + const input = dateTime.element.querySelector('.input'); + + const blurEvent = new Event('blur'); + input.value = '2021-04-06'; + input.dispatchEvent(blurEvent); + + setTimeout(() => { + const input = dateTime.element.querySelector('.input'); + assert.equal(input.value, ''); + assert.equal(dateTime.dataValue, ''); + + document.innerHTML = ''; + done(); + }, 300); + }) + .catch(done); + }); + + it('Should not allow inputting the date if it is out of min/max date range', function (done) { + const form = _.cloneDeep(comp3); + const element = document.createElement('div'); + form.components[0].datePicker.minDate = '2021-04-04T12:00:00'; + form.components[0].datePicker.maxDate = '2021-04-18T12:00:00'; + + Formio.createForm(element, form) + .then((form) => { + const dateTime = form.getComponent('dateTime'); + const input = dateTime.element.querySelector('.input'); + + const blurEvent = new Event('blur'); + input.value = '2020-04-03'; + input.dispatchEvent(blurEvent); + + setTimeout(() => { + const input = dateTime.element.querySelector('.input'); + assert.equal(input.value, ''); + assert.equal(dateTime.dataValue, ''); + + const blurEvent = new Event('blur'); + input.value = '2022-04-13'; + input.dispatchEvent(blurEvent); + + setTimeout(() => { + const input = dateTime.element.querySelector('.input'); + assert.equal(input.value, ''); + assert.equal(dateTime.dataValue, ''); + + document.innerHTML = ''; + done(); + }, 300); + }, 300); + }) + .catch(done); + }); + + it('Should set hour and minutes step', function (done) { + const form = _.cloneDeep(comp3); + const element = document.createElement('div'); + form.components[0].timePicker = { hourStep: 3, minuteStep: 10 }; + + Formio.createForm(element, form) + .then((form) => { + const dateTime = form.getComponent('dateTime'); + const calendar = + dateTime.element.querySelector('.flatpickr-input').widget + .calendar; + assert.equal(calendar.config.minuteIncrement, 10); + assert.equal(calendar.config.hourIncrement, 3); + + document.innerHTML = ''; + done(); + }) + .catch(done); + }); + + it('Should allow inputting 24h time', function (done) { + const form = _.cloneDeep(comp3); + const element = document.createElement('div'); + form.components[0].timePicker = { showMeridian: false }; + form.components[0].widget['time_24hr'] = true; + + Formio.createForm(element, form) + .then((form) => { + const dateTime = form.getComponent('dateTime'); + const input = dateTime.element.querySelector('.input'); + + const blurEvent = new Event('blur'); + input.value = '2020-04-03 22:11'; + input.dispatchEvent(blurEvent); + setTimeout(() => { + const input = dateTime.element.querySelector('.input'); + assert.equal(input.value, '2020-04-03 22:11'); + assert.equal( + dateTime.dataValue.startsWith('2020-04-03T22:11:00'), + true, + ); + + document.innerHTML = ''; + done(); + }, 300); + }) + .catch(done); + }); + + it('Should not set value if it does not meet minDate validation', function (done) { + const form = _.cloneDeep(comp5); + const element = document.createElement('div'); + + Formio.createForm(element, form) + .then((form) => { + const dateTime = form.getComponent('dateTime'); + dateTime.setValue('2021-05-01T09:00:00'); + + setTimeout(() => { + const input = dateTime.element.querySelector('.input'); + assert.equal(input.value, ''); + + document.innerHTML = ''; + done(); + }, 300); + }) + .catch(done); + }); + + it('Should set value in readOnly mode even if it does not meet current minDate validation conditions', function (done) { + const form = _.cloneDeep(comp5); + const element = document.createElement('div'); + + Formio.createForm(element, form, { readOnly: true }) + .then((form) => { + const dateTime = form.getComponent('dateTime'); + dateTime.setValue('2021-05-01T09:00:00'); + + setTimeout(() => { + const input = dateTime.element.querySelector('.input'); + assert.equal(input.value, '05/01/21'); + + document.innerHTML = ''; + done(); + }, 300); + }) + .catch(done); + }); + + it('Should save hours and minutes values on first change', function (done) { + const form = _.cloneDeep(comp6); + const element = document.createElement('div'); + form.components[0].enableDate = false; + + Formio.createForm(element, form) + .then((form) => { + const dateTime = form.getComponent('dateTime'); + const blurEvent = new Event('blur'); + const input = dateTime.element.querySelector('.input'); + input.dispatchEvent(blurEvent); + + setTimeout(() => { + const calendar = + dateTime.element.querySelector('.flatpickr-input') + .widget.calendar; + calendar._input.value = '7:00 PM'; + const expectedValue = 'T19:00:00'; + calendar._input.dispatchEvent(blurEvent); + + setTimeout(() => { + assert.equal( + dateTime.dataValue.includes(expectedValue), + true, + ); + + document.innerHTML = ''; + done(); + }, 200); + }, 200); + }) + .catch(done); + }); + + it('Should provide correct value after submission', function (done) { + const form = _.cloneDeep(comp7); + const element = document.createElement('div'); + form.components[0].enableTime = false; + + Formio.createForm(element, form) + .then((form) => { + const dateTime = form.getComponent('dateTime'); + dateTime.setValue('2022-12-21'); + + setTimeout(() => { + const submit = form.getComponent('submit'); + const clickEvent = new Event('click'); + const submitBtn = submit.refs.button; + submitBtn.dispatchEvent(clickEvent); + + setTimeout(() => { + assert.equal(dateTime.dataValue, '2022-12-21'); + done(); + }, 200); + }, 200); + }) + .catch(done); + }); + + it('Should not highlight the field when it is valid when multiple values and required validation are enabled', function (done) { + const form = _.cloneDeep(comp8); + const element = document.createElement('div'); + + Formio.createForm(element, form) + .then((form) => { + const dateTime = form.getComponent('dateTime'); + const input1 = dateTime.element.querySelectorAll('.input')[0]; + + const blurEvent = new Event('blur'); + input1.value = '2020-04-03'; + input1.dispatchEvent(blurEvent); + + const addAnotherBtn = dateTime.refs.addButton[0]; + const clickEvent = new Event('click'); + addAnotherBtn.dispatchEvent(clickEvent); + + setTimeout(() => { + assert.equal(dateTime.refs.input.length, 2); + + const inputs = dateTime.element.querySelectorAll('.input'); + assert.equal( + inputs[0].classList.contains('is-invalid'), + false, + ); + assert.equal( + inputs[1].classList.contains('is-invalid'), + true, + ); + + inputs[1].value = '2020-05-05'; + inputs[1].dispatchEvent(blurEvent); + + setTimeout(() => { + const input2 = + dateTime.element.querySelectorAll('.input')[1]; + assert.equal( + input2.classList.contains('is-invalid'), + false, + ); + + document.innerHTML = ''; + done(); + }, 300); + }, 300); + }) + .catch(done); + }); + + it('Should provide correct values with time after submission', function (done) { + const form = _.cloneDeep(comp10); + const element = document.createElement('div'); + + Formio.createForm(element, form) + .then((form) => { + const dateTime = form.getComponent('dateTime'); + const textField = form.getComponent('textField'); + + dateTime.setValue('2022-04-01T14:00:00.000'); + textField.setValue('2022-04-01T14:00:00.000'); + + setTimeout(() => { + const submit = form.getComponent('submit'); + const clickEvent = new Event('click'); + const submitBtn = submit.refs.button; + submitBtn.dispatchEvent(clickEvent); + + setTimeout(() => { + const input1 = dateTime.element.querySelector('.input'); + const input2 = + textField.element.querySelector('.input'); + + assert.equal(input1.value, '2022-04-01 02:00 PM'); + assert.equal(input2.value, '2022-04-01 02:00 PM'); + done(); + }, 200); + }, 200); + }) + .catch(done); + }); + + it('Should add date to format if enableDate is true', function (done) { + const form = _.cloneDeep(comp3); + form.components[0].format = 'hh:mm a'; + form.components[0].enableDate = true; + const element = document.createElement('div'); + + Formio.createForm(element, form, { attachMode: 'builder' }) + .then((form) => { + const dateTime = form.getComponent('dateTime'); + assert.equal(dateTime.component.format, 'yyyy-MM-dd hh:mm a'); + done(); + }) + .catch(done); + }); + + it('Should add time to format if enableTime is true', function (done) { + const form = _.cloneDeep(comp3); + form.components[0].format = 'yyyy-MM-dd'; + form.components[0].enableTime = true; + const element = document.createElement('div'); + + Formio.createForm(element, form, { attachMode: 'builder' }) + .then((form) => { + const dateTime = form.getComponent('dateTime'); + assert.equal(dateTime.component.format, 'yyyy-MM-dd hh:mm a'); + done(); + }) + .catch(done); + }); + + // it('Should provide correct date in selected timezone after submission', (done) => { + // const form = _.cloneDeep(comp9); + // const element = document.createElement('div'); + + // Formio.createForm(element, form, { readOnly: true }).then(form => { + // const dateTime = form.getComponent('dateTime'); + // const dateTime1 = form.getComponent('dateTime1'); + + // dateTime.setValue('2022-04-01T00:00:00.000'); + // dateTime1.setValue('2022-04-01T00:00:00.000'); + + // document.body.addEventListener('zonesLoaded', () => { + // setTimeout(() => { + // const input = dateTime.element.querySelector('.input'); + // const input1 = dateTime1.element.querySelector('.input'); + + // assert.equal(input.value, '2022-03-31 CDT'); + // assert.equal(input1.value, '2022-04-01 KST'); + // done(); + // }, 100); + // }); + // }).catch(done); + // }); + + // it('Test Shortcut Buttons', (done) => { + // // eslint-disable-next-line no-debugger + // debugger; + // window.flatpickr = Flatpickr; + // window.ShortcutButtonsPlugin = ShortcutButtonsPlugin; + // const formElement = document.createElement('div'); + // const form = new Webform(formElement); + // form.setForm({ display: 'form', type: 'form', components: [comp2] }) + // .then(() => { + // const dateTime = form.components[0]; + // const buttonsWrappers = document.querySelectorAll('.shortcut-buttons-flatpickr-wrapper'); + // const shortcutButtons = buttonsWrappers[buttonsWrappers.length - 1].querySelectorAll('.shortcut-buttons-flatpickr-button'); + + // assert.equal(shortcutButtons.length, 1); + + // const input = dateTime.refs.input[0]; + // Harness.clickElement(dateTime, shortcutButtons[0]); + + // setTimeout(() => { + // input.widget.calendar.close(); + // setTimeout(() => { + // assert.equal(form.data.date, '2020-10-10T00:00:00+00:00'); + // dateTime.destroy(); + // done(); + // }, 250); + // }, 150); + // }).catch(done); + // }); }); diff --git a/src/components/datetime/editForm/DateTime.edit.data.js b/src/components/datetime/editForm/DateTime.edit.data.js index aea049b180..d9c814b22e 100644 --- a/src/components/datetime/editForm/DateTime.edit.data.js +++ b/src/components/datetime/editForm/DateTime.edit.data.js @@ -1,21 +1,24 @@ export default [ - { - type: 'textfield', - input: true, - key: 'defaultDate', - label: 'Default Date', - placeholder: 'moment()', - tooltip: 'You can use Moment.js functions to set the default value to a specific date. For example: \n \n moment().subtract(10, \'days\')', - weight: 6 - },{ - type: 'textarea', - as: 'json', - editor: 'ace', - weight: 28, - input: true, - key: 'customOptions', - label: 'Flatpickr options', - tooltip: 'A raw JSON object to use as options for the Date / Time component (Flatpickr).', - defaultValue: {}, - }, + { + type: 'textfield', + input: true, + key: 'defaultDate', + label: 'Default Date', + placeholder: 'moment()', + tooltip: + "You can use Moment.js functions to set the default value to a specific date. For example: \n \n moment().subtract(10, 'days')", + weight: 6, + }, + { + type: 'textarea', + as: 'json', + editor: 'ace', + weight: 28, + input: true, + key: 'customOptions', + label: 'Flatpickr options', + tooltip: + 'A raw JSON object to use as options for the Date / Time component (Flatpickr).', + defaultValue: {}, + }, ]; diff --git a/src/components/datetime/editForm/DateTime.edit.date.js b/src/components/datetime/editForm/DateTime.edit.date.js index 2c465dd4a3..0617f3f160 100644 --- a/src/components/datetime/editForm/DateTime.edit.date.js +++ b/src/components/datetime/editForm/DateTime.edit.date.js @@ -2,70 +2,75 @@ import Evaluator from '../../../utils/Evaluator'; import EditFormUtils from '../../_classes/component/editForm/utils'; export default [ - { - type: 'checkbox', - input: true, - key: 'enableDate', - label: 'Enable Date Input', - weight: 0, - tooltip: 'Enables date input for this field.' - }, - { - type: 'tags', - input: true, - key: 'datePicker.disable', - label: 'Disable specific dates or dates by range', - placeholder: '(yyyy-MM-dd) or (yyyy-MM-dd - yyyy-MM-dd)', - tooltip: 'Add dates that you want to blacklist. For example: \n \n 2025-02-21', - validate: { - custom: 'if (_.isEmpty(input)) {\n return true;\n}\nconst dates = _.isArray(input) ?\ninput : input.split(component.delimeter);\nconst isValid = _.every(dates, (data) => \n !!data.match(/\\d{4}-\\d{2}-\\d{2}/g));\nvalid = isValid || \'Invalid date\';' + { + type: 'checkbox', + input: true, + key: 'enableDate', + label: 'Enable Date Input', + weight: 0, + tooltip: 'Enables date input for this field.', + }, + { + type: 'tags', + input: true, + key: 'datePicker.disable', + label: 'Disable specific dates or dates by range', + placeholder: '(yyyy-MM-dd) or (yyyy-MM-dd - yyyy-MM-dd)', + tooltip: + 'Add dates that you want to blacklist. For example: \n \n 2025-02-21', + validate: { + custom: "if (_.isEmpty(input)) {\n return true;\n}\nconst dates = _.isArray(input) ?\ninput : input.split(component.delimeter);\nconst isValid = _.every(dates, (data) => \n !!data.match(/\\d{4}-\\d{2}-\\d{2}/g));\nvalid = isValid || 'Invalid date';", + }, + weight: 21, }, - weight: 21 - }, - { - type: 'panel', - title: 'Custom Disabled Dates', - collapsible: true, - collapsed: true, - style: { 'margin-bottom': '10px' }, - key: 'panel-disable-function', - customConditional() { - return !Evaluator.noeval || Evaluator.protectedEval; + { + type: 'panel', + title: 'Custom Disabled Dates', + collapsible: true, + collapsed: true, + style: { 'margin-bottom': '10px' }, + key: 'panel-disable-function', + customConditional() { + return !Evaluator.noeval || Evaluator.protectedEval; + }, + components: [ + EditFormUtils.logicVariablesTable( + 'dateThe date object.', + ), + { + type: 'textarea', + input: true, + editor: 'ace', + key: 'datePicker.disableFunction', + label: 'Disabling dates by a function', + description: + 'For more information check out the Docs', + weight: 22, + }, + { + type: 'htmlelement', + tag: 'div', + content: + '

    Example

    ' + + `
    // Disable all weekends
    date.getDay() === 0 || date.getDay() === 6
    + `, + }, + ], }, - components: [ - EditFormUtils.logicVariablesTable('dateThe date object.'), - { - type: 'textarea', + { + type: 'checkbox', input: true, - editor: 'ace', - key: 'datePicker.disableFunction', - label: 'Disabling dates by a function', - description: 'For more information check out the Docs', - weight: 22 - }, - { - type: 'htmlelement', - tag: 'div', - content: '

    Example

    ' + - `
    // Disable all weekends
    date.getDay() === 0 || date.getDay() === 6
    - ` - } - ] - }, - { - type: 'checkbox', - input: true, - key: 'datePicker.disableWeekends', - label: 'Disable weekends', - tooltip: 'Check to disable weekends', - weight: 23 - }, - { - type: 'checkbox', - input: true, - key: 'datePicker.disableWeekdays', - label: 'Disable weekdays', - tooltip: 'Check to disable weekdays', - weight: 23 - } + key: 'datePicker.disableWeekends', + label: 'Disable weekends', + tooltip: 'Check to disable weekends', + weight: 23, + }, + { + type: 'checkbox', + input: true, + key: 'datePicker.disableWeekdays', + label: 'Disable weekdays', + tooltip: 'Check to disable weekdays', + weight: 23, + }, ]; diff --git a/src/components/datetime/editForm/DateTime.edit.display.js b/src/components/datetime/editForm/DateTime.edit.display.js index cb727ee899..eee9766a23 100644 --- a/src/components/datetime/editForm/DateTime.edit.display.js +++ b/src/components/datetime/editForm/DateTime.edit.display.js @@ -1,98 +1,102 @@ export default [ - { - type: 'select', - input: true, - key: 'displayInTimezone', - label: 'Display in Timezone', - tooltip: 'This will display the captured date time in the select timezone.', - weight: 30, - defaultValue: 'viewer', - dataSrc: 'values', - data: { - values: [ - { label: 'of Viewer', value: 'viewer' }, - { label: 'of Submission', value: 'submission' }, - { label: 'of Location', value: 'location' }, - { label: 'UTC', value: 'utc' } - ] - } - }, - { - type: 'select', - input: true, - key: 'timezone', - label: 'Select Timezone', - tooltip: 'Select the timezone you wish to display this Date', - weight: 31, - lazyLoad: true, - defaultValue: '', - valueProperty: 'name', - dataSrc: 'url', - data: { - url: '{{options.cdnUrl}}/timezones.json' + { + type: 'select', + input: true, + key: 'displayInTimezone', + label: 'Display in Timezone', + tooltip: + 'This will display the captured date time in the select timezone.', + weight: 30, + defaultValue: 'viewer', + dataSrc: 'values', + data: { + values: [ + { label: 'of Viewer', value: 'viewer' }, + { label: 'of Submission', value: 'submission' }, + { label: 'of Location', value: 'location' }, + { label: 'UTC', value: 'utc' }, + ], + }, + }, + { + type: 'select', + input: true, + key: 'timezone', + label: 'Select Timezone', + tooltip: 'Select the timezone you wish to display this Date', + weight: 31, + lazyLoad: true, + defaultValue: '', + valueProperty: 'name', + dataSrc: 'url', + data: { + url: '{{options.cdnUrl}}/timezones.json', + }, + template: '{{ item.label }}', + conditional: { + json: { '===': [{ var: 'data.displayInTimezone' }, 'location'] }, + }, + }, + { + type: 'checkbox', + input: true, + key: 'useLocaleSettings', + label: 'Use Locale Settings', + tooltip: 'Use locale settings to display date and time.', + weight: 51, }, - template: '{{ item.label }}', - conditional: { - json: { '===': [{ var: 'data.displayInTimezone' }, 'location'] } - } - }, - { - type: 'checkbox', - input: true, - key: 'useLocaleSettings', - label: 'Use Locale Settings', - tooltip: 'Use locale settings to display date and time.', - weight: 51 - }, - { - type: 'checkbox', - input: true, - key: 'allowInput', - label: 'Allow Manual Input', - tooltip: 'Check this if you would like to allow the user to manually enter in the date.', - weight: 51 - }, - { - type: 'textfield', - input: true, - key: 'format', - label: 'Format', - placeholder: 'Format', - description: 'Use formats provided by DateParser Codes', - tooltip: 'The date format for displaying the datetime value.', - weight: 52 - }, - { - type: 'editgrid', - input: true, - key: 'shortcutButtons', - label: 'Shortcut Buttons', - description: 'You can specify few buttons which will be shown above the calendar. Use Label to specify the name of the button and onClick to specify which date/time will be set when user clicks the button. E.g, date = new Date()', - templates: { - header: '
    \n
    Label
    \n
    onClick
    \n
    ', - row: '
    \n
    \n {{ flattenedComponents.label.getView(row.label) }}\n
    \n
    \n {{ flattenedComponents.onClick.getView(row.onClick) }}\n
    \n {% if (!instance.disabled) { %}\n
    \n
    \n \n {% if (!instance.hasRemoveButtons || instance.hasRemoveButtons()) { %}\n \n {% } %}\n
    \n
    \n {% } %}\n
    ' + { + type: 'checkbox', + input: true, + key: 'allowInput', + label: 'Allow Manual Input', + tooltip: + 'Check this if you would like to allow the user to manually enter in the date.', + weight: 51, }, - components: [ - { - label: 'Label', - key: 'label', + { type: 'textfield', input: true, - validate: { - required: true - } - }, - { - label: 'onClick', - key: 'onClick', - type: 'textarea', - editor: 'ace', + key: 'format', + label: 'Format', + placeholder: 'Format', + description: + 'Use formats provided by DateParser Codes', + tooltip: 'The date format for displaying the datetime value.', + weight: 52, + }, + { + type: 'editgrid', input: true, - validate: { - required: true - } - } - ], - defaultValue: [] - } + key: 'shortcutButtons', + label: 'Shortcut Buttons', + description: + 'You can specify few buttons which will be shown above the calendar. Use Label to specify the name of the button and onClick to specify which date/time will be set when user clicks the button. E.g, date = new Date()', + templates: { + header: '
    \n
    Label
    \n
    onClick
    \n
    ', + row: '
    \n
    \n {{ flattenedComponents.label.getView(row.label) }}\n
    \n
    \n {{ flattenedComponents.onClick.getView(row.onClick) }}\n
    \n {% if (!instance.disabled) { %}\n
    \n
    \n \n {% if (!instance.hasRemoveButtons || instance.hasRemoveButtons()) { %}\n \n {% } %}\n
    \n
    \n {% } %}\n
    ', + }, + components: [ + { + label: 'Label', + key: 'label', + type: 'textfield', + input: true, + validate: { + required: true, + }, + }, + { + label: 'onClick', + key: 'onClick', + type: 'textarea', + editor: 'ace', + input: true, + validate: { + required: true, + }, + }, + ], + defaultValue: [], + }, ]; diff --git a/src/components/datetime/editForm/DateTime.edit.time.js b/src/components/datetime/editForm/DateTime.edit.time.js index bbd1b6ceb9..e4520f3e5f 100644 --- a/src/components/datetime/editForm/DateTime.edit.time.js +++ b/src/components/datetime/editForm/DateTime.edit.time.js @@ -1,34 +1,36 @@ export default [ - { - type: 'checkbox', - input: true, - key: 'enableTime', - label: 'Enable Time Input', - tooltip: 'Enables time input for this field.', - weight: 0 - }, - { - type: 'number', - input: true, - key: 'timePicker.hourStep', - label: 'Hour Step Size', - tooltip: 'The number of hours to increment/decrement in the time picker.', - weight: 10 - }, - { - type: 'number', - input: true, - key: 'timePicker.minuteStep', - label: 'Minute Step Size', - tooltip: 'The number of minutes to increment/decrement in the time picker.', - weight: 20 - }, - { - type: 'checkbox', - input: true, - key: 'timePicker.showMeridian', - label: '12 Hour Time (AM/PM)', - tooltip: 'Display time in 12 hour time with AM/PM.', - weight: 30 - } + { + type: 'checkbox', + input: true, + key: 'enableTime', + label: 'Enable Time Input', + tooltip: 'Enables time input for this field.', + weight: 0, + }, + { + type: 'number', + input: true, + key: 'timePicker.hourStep', + label: 'Hour Step Size', + tooltip: + 'The number of hours to increment/decrement in the time picker.', + weight: 10, + }, + { + type: 'number', + input: true, + key: 'timePicker.minuteStep', + label: 'Minute Step Size', + tooltip: + 'The number of minutes to increment/decrement in the time picker.', + weight: 20, + }, + { + type: 'checkbox', + input: true, + key: 'timePicker.showMeridian', + label: '12 Hour Time (AM/PM)', + tooltip: 'Display time in 12 hour time with AM/PM.', + weight: 30, + }, ]; diff --git a/src/components/datetime/editForm/DateTime.edit.validation.js b/src/components/datetime/editForm/DateTime.edit.validation.js index 775f3a4ae9..7e709ce029 100644 --- a/src/components/datetime/editForm/DateTime.edit.validation.js +++ b/src/components/datetime/editForm/DateTime.edit.validation.js @@ -1,82 +1,102 @@ export default [ - { - type: 'checkbox', - input: true, - key: 'enableMinDateInput', - label: 'Use Input to add moment.js for minDate', - persistent: false, - weight: 10, - tooltip: 'Enables to use input for moment functions instead of calendar.' - }, - { - type: 'datetime', - input: true, - key: 'datePicker.minDate', - label: 'Use calendar to set minDate', - skipMerge: true, - weight: 10, - tooltip: 'Enables to use calendar to set date.', - customConditional({ data, component }) { - if (component.datePicker && component.datePicker.minDate && component.datePicker.minDate.indexOf('moment') !== -1) { - return false; - } - return !data.enableMinDateInput; + { + type: 'checkbox', + input: true, + key: 'enableMinDateInput', + label: 'Use Input to add moment.js for minDate', + persistent: false, + weight: 10, + tooltip: + 'Enables to use input for moment functions instead of calendar.', }, - }, - { - type: 'textfield', - input: true, - enableTime: false, - key: 'datePicker.minDate', - skipMerge: true, - label: 'Minimum Date', - weight: 10, - tooltip: 'The minimum date that can be picked. You can also use Moment.js functions. For example: \n \n moment().subtract(10, \'days\')', - customConditional({ data, component }) { - if (component.datePicker && component.datePicker.minDate && component.datePicker.minDate.indexOf('moment') !== -1) { - return true; - } - return data.enableMinDateInput; + { + type: 'datetime', + input: true, + key: 'datePicker.minDate', + label: 'Use calendar to set minDate', + skipMerge: true, + weight: 10, + tooltip: 'Enables to use calendar to set date.', + customConditional({ data, component }) { + if ( + component.datePicker && + component.datePicker.minDate && + component.datePicker.minDate.indexOf('moment') !== -1 + ) { + return false; + } + return !data.enableMinDateInput; + }, }, - }, - { - type: 'checkbox', - input: true, - key: 'enableMaxDateInput', - label: 'Use Input to add moment.js for maxDate', - persistent: false, - weight: 20, - tooltip: 'Enables to use input for moment functions instead of calendar.' - }, - { - type: 'datetime', - input: true, - key: 'datePicker.maxDate', - skipMerge: true, - label: 'Use calendar to set maxDate', - weight: 20, - tooltip: 'Enables to use calendar to set date.', - customConditional({ data, component }) { - if (component.datePicker && component.datePicker.maxDate && component.datePicker.maxDate.indexOf('moment') !== -1) { - return false; - } - return !data.enableMaxDateInput; + { + type: 'textfield', + input: true, + enableTime: false, + key: 'datePicker.minDate', + skipMerge: true, + label: 'Minimum Date', + weight: 10, + tooltip: + "The minimum date that can be picked. You can also use Moment.js functions. For example: \n \n moment().subtract(10, 'days')", + customConditional({ data, component }) { + if ( + component.datePicker && + component.datePicker.minDate && + component.datePicker.minDate.indexOf('moment') !== -1 + ) { + return true; + } + return data.enableMinDateInput; + }, }, - }, - { - type: 'textfield', - input: true, - enableTime: false, - key: 'datePicker.maxDate', - skipMerge: true, - label: 'Maximum Date', - tooltip: 'The maximum date that can be picked. You can also use Moment.js functions. For example: \n \n moment().add(10, \'days\')', - weight: 20, - customConditional({ data, component }) { - if (component.datePicker && component.datePicker.maxDate && component.datePicker.maxDate.indexOf('moment') !== -1) { - return true; - } - return data.enableMaxDateInput; + { + type: 'checkbox', + input: true, + key: 'enableMaxDateInput', + label: 'Use Input to add moment.js for maxDate', + persistent: false, + weight: 20, + tooltip: + 'Enables to use input for moment functions instead of calendar.', + }, + { + type: 'datetime', + input: true, + key: 'datePicker.maxDate', + skipMerge: true, + label: 'Use calendar to set maxDate', + weight: 20, + tooltip: 'Enables to use calendar to set date.', + customConditional({ data, component }) { + if ( + component.datePicker && + component.datePicker.maxDate && + component.datePicker.maxDate.indexOf('moment') !== -1 + ) { + return false; + } + return !data.enableMaxDateInput; + }, + }, + { + type: 'textfield', + input: true, + enableTime: false, + key: 'datePicker.maxDate', + skipMerge: true, + label: 'Maximum Date', + tooltip: + "The maximum date that can be picked. You can also use Moment.js functions. For example: \n \n moment().add(10, 'days')", + weight: 20, + customConditional({ data, component }) { + if ( + component.datePicker && + component.datePicker.maxDate && + component.datePicker.maxDate.indexOf('moment') !== -1 + ) { + return true; + } + return data.enableMaxDateInput; + }, }, - } ]; diff --git a/src/components/datetime/fixtures/comp1.js b/src/components/datetime/fixtures/comp1.js index d46462afee..e3cbc314eb 100644 --- a/src/components/datetime/fixtures/comp1.js +++ b/src/components/datetime/fixtures/comp1.js @@ -1,44 +1,42 @@ export default { - 'conditional': { - 'eq': '', - 'when': null, - 'show': '' - }, - 'tags': [ - - ], - 'type': 'datetime', - 'validate': { - 'custom': '', - 'required': false - }, - 'persistent': true, - 'protected': false, - 'timePicker': { - 'arrowkeys': true, - 'mousewheel': true, - 'readonlyInput': false, - 'showMeridian': true, - 'minuteStep': 1, - 'hourStep': 1 - }, - 'datePicker': { - 'datepickerMode': 'day', - 'yearRange': '20', - 'maxMode': 'year', - 'minMode': 'day', - 'initDate': '', - 'startingDay': 0, - 'showWeeks': true - }, - 'datepickerMode': 'day', - 'defaultDate': '', - 'enableTime': true, - 'enableDate': true, - 'format': 'yyyy-MM-dd hh:mm a', - 'placeholder': 'Enter the date', - 'key': 'date', - 'label': 'Date', - 'tableView': true, - 'input': true + conditional: { + eq: '', + when: null, + show: '', + }, + tags: [], + type: 'datetime', + validate: { + custom: '', + required: false, + }, + persistent: true, + protected: false, + timePicker: { + arrowkeys: true, + mousewheel: true, + readonlyInput: false, + showMeridian: true, + minuteStep: 1, + hourStep: 1, + }, + datePicker: { + datepickerMode: 'day', + yearRange: '20', + maxMode: 'year', + minMode: 'day', + initDate: '', + startingDay: 0, + showWeeks: true, + }, + datepickerMode: 'day', + defaultDate: '', + enableTime: true, + enableDate: true, + format: 'yyyy-MM-dd hh:mm a', + placeholder: 'Enter the date', + key: 'date', + label: 'Date', + tableView: true, + input: true, }; diff --git a/src/components/datetime/fixtures/comp10.js b/src/components/datetime/fixtures/comp10.js index 6f4bb58659..7782f599b7 100644 --- a/src/components/datetime/fixtures/comp10.js +++ b/src/components/datetime/fixtures/comp10.js @@ -1,84 +1,84 @@ export default { - _id: '62d5264f8673caaf3e7afc14', - title: 'dateTime2', - name: 'dateTime2', - path: 'datetime2', - type: 'form', - display: 'form', - tags: [], - owner: '6137352490eb201aaff6e79f', - components: [ - { - label: 'Date / Time', - tableView: true, - datePicker: { disableWeekends: false, disableWeekdays: false }, - enableMinDateInput: false, - enableMaxDateInput: false, - key: 'dateTime', - type: 'datetime', - input: true, - widget: { - type: 'calendar', - displayInTimezone: 'viewer', - locale: 'en', - useLocaleSettings: false, - allowInput: true, - mode: 'single', - enableTime: true, - noCalendar: false, - format: 'yyyy-MM-dd hh:mm a', - hourIncrement: 1, - minuteIncrement: 1, - 'time_24hr': false, - minDate: null, - disableWeekends: false, - disableWeekdays: false, - maxDate: null, - }, - }, - { - label: 'Text Field', - widget: { - type: 'calendar', - altInput: true, - allowInput: true, - clickOpens: true, - enableDate: true, - enableTime: true, - mode: 'single', - noCalendar: false, - format: 'yyyy-MM-dd hh:mm a', - dateFormat: 'yyyy-MM-ddTHH:mm:ssZ', - useLocaleSettings: false, - hourIncrement: 1, - minuteIncrement: 5, - 'time_24hr': false, - saveAs: 'text', - displayInTimezone: 'viewer', - locale: 'en', - }, - tableView: true, - key: 'textField', - type: 'textfield', - input: true, - }, - { - type: 'button', - label: 'Submit', - key: 'submit', - disableOnInvalid: true, - input: true, - tableView: false, - }, - ], - settings: {}, - properties: {}, - project: '61555aa912cab1f874bb17fc', - controller: '', - revisions: '', - submissionRevisions: '', - '_vid': 0, - created: '2022-07-18T09:22:23.287Z', - modified: '2022-11-14T13:40:52.276Z', - machineName: 'tmcogwpnxqfxgxy:dateTime2', + _id: '62d5264f8673caaf3e7afc14', + title: 'dateTime2', + name: 'dateTime2', + path: 'datetime2', + type: 'form', + display: 'form', + tags: [], + owner: '6137352490eb201aaff6e79f', + components: [ + { + label: 'Date / Time', + tableView: true, + datePicker: { disableWeekends: false, disableWeekdays: false }, + enableMinDateInput: false, + enableMaxDateInput: false, + key: 'dateTime', + type: 'datetime', + input: true, + widget: { + type: 'calendar', + displayInTimezone: 'viewer', + locale: 'en', + useLocaleSettings: false, + allowInput: true, + mode: 'single', + enableTime: true, + noCalendar: false, + format: 'yyyy-MM-dd hh:mm a', + hourIncrement: 1, + minuteIncrement: 1, + time_24hr: false, + minDate: null, + disableWeekends: false, + disableWeekdays: false, + maxDate: null, + }, + }, + { + label: 'Text Field', + widget: { + type: 'calendar', + altInput: true, + allowInput: true, + clickOpens: true, + enableDate: true, + enableTime: true, + mode: 'single', + noCalendar: false, + format: 'yyyy-MM-dd hh:mm a', + dateFormat: 'yyyy-MM-ddTHH:mm:ssZ', + useLocaleSettings: false, + hourIncrement: 1, + minuteIncrement: 5, + time_24hr: false, + saveAs: 'text', + displayInTimezone: 'viewer', + locale: 'en', + }, + tableView: true, + key: 'textField', + type: 'textfield', + input: true, + }, + { + type: 'button', + label: 'Submit', + key: 'submit', + disableOnInvalid: true, + input: true, + tableView: false, + }, + ], + settings: {}, + properties: {}, + project: '61555aa912cab1f874bb17fc', + controller: '', + revisions: '', + submissionRevisions: '', + _vid: 0, + created: '2022-07-18T09:22:23.287Z', + modified: '2022-11-14T13:40:52.276Z', + machineName: 'tmcogwpnxqfxgxy:dateTime2', }; diff --git a/src/components/datetime/fixtures/comp11.js b/src/components/datetime/fixtures/comp11.js index d296bd3fd1..464b2ef8c4 100644 --- a/src/components/datetime/fixtures/comp11.js +++ b/src/components/datetime/fixtures/comp11.js @@ -1,53 +1,53 @@ export default { - title: '5628', - name: '5628', - path: '5628', - type: 'form', - display: 'form', - components: [ - { - label: 'Date / Time', - format: 'dd-MM-yyyy', - tableView: false, - datePicker: { - disableWeekends: false, - disableWeekdays: false, - }, - enableTime: false, - enableMinDateInput: false, - enableMaxDateInput: false, - key: 'dateTime', - type: 'datetime', - input: true, - widget: { - type: 'calendar', - displayInTimezone: 'viewer', - locale: 'en', - useLocaleSettings: false, - allowInput: true, - mode: 'single', - enableTime: false, - noCalendar: false, - format: 'dd-MM-yyyy', - hourIncrement: 1, - minuteIncrement: 1, - 'time_24hr': false, - minDate: null, - disableWeekends: false, - disableWeekdays: false, - maxDate: null, - }, - }, - { - type: 'button', - label: 'Submit', - key: 'submit', - disableOnInvalid: true, - input: true, - tableView: false, - }, - ], - created: '2022-11-16T10:06:19.656Z', - modified: '2022-11-16T13:25:55.044Z', - machineName: 'aabvnyfwnstnovd:5628', + title: '5628', + name: '5628', + path: '5628', + type: 'form', + display: 'form', + components: [ + { + label: 'Date / Time', + format: 'dd-MM-yyyy', + tableView: false, + datePicker: { + disableWeekends: false, + disableWeekdays: false, + }, + enableTime: false, + enableMinDateInput: false, + enableMaxDateInput: false, + key: 'dateTime', + type: 'datetime', + input: true, + widget: { + type: 'calendar', + displayInTimezone: 'viewer', + locale: 'en', + useLocaleSettings: false, + allowInput: true, + mode: 'single', + enableTime: false, + noCalendar: false, + format: 'dd-MM-yyyy', + hourIncrement: 1, + minuteIncrement: 1, + time_24hr: false, + minDate: null, + disableWeekends: false, + disableWeekdays: false, + maxDate: null, + }, + }, + { + type: 'button', + label: 'Submit', + key: 'submit', + disableOnInvalid: true, + input: true, + tableView: false, + }, + ], + created: '2022-11-16T10:06:19.656Z', + modified: '2022-11-16T13:25:55.044Z', + machineName: 'aabvnyfwnstnovd:5628', }; diff --git a/src/components/datetime/fixtures/comp12.js b/src/components/datetime/fixtures/comp12.js index 212532164c..4cc6fab220 100644 --- a/src/components/datetime/fixtures/comp12.js +++ b/src/components/datetime/fixtures/comp12.js @@ -1,53 +1,53 @@ export default { - _id: '6374b809ef6e5f56bfa91978', - title: 'hhhhhh', - name: 'hhhhhh', - path: 'hhhhhh', - type: 'form', - display: 'form', - components: [ - { - label: 'Date / Time', - format: 'MMMM/yy', - tableView: true, - datePicker: { - disableWeekends: false, - disableWeekdays: false, - }, - enableMinDateInput: false, - enableMaxDateInput: false, - key: 'dateTime', - type: 'datetime', - input: true, - widget: { - type: 'calendar', - displayInTimezone: 'viewer', - locale: 'en', - useLocaleSettings: false, - allowInput: true, - mode: 'single', - enableTime: true, - noCalendar: false, - format: 'MMMM/yy', - hourIncrement: 1, - minuteIncrement: 1, - 'time_24hr': false, - minDate: null, - disableWeekends: false, - disableWeekdays: false, - maxDate: null, - }, - }, - { - type: 'button', - label: 'Submit', - key: 'submit', - disableOnInvalid: true, - input: true, - tableView: false, - }, - ], - created: '2022-11-16T10:14:33.808Z', - modified: '2022-11-18T10:18:51.790Z', - machineName: 'aabvnyfwnstnovd:hhhhhh', + _id: '6374b809ef6e5f56bfa91978', + title: 'hhhhhh', + name: 'hhhhhh', + path: 'hhhhhh', + type: 'form', + display: 'form', + components: [ + { + label: 'Date / Time', + format: 'MMMM/yy', + tableView: true, + datePicker: { + disableWeekends: false, + disableWeekdays: false, + }, + enableMinDateInput: false, + enableMaxDateInput: false, + key: 'dateTime', + type: 'datetime', + input: true, + widget: { + type: 'calendar', + displayInTimezone: 'viewer', + locale: 'en', + useLocaleSettings: false, + allowInput: true, + mode: 'single', + enableTime: true, + noCalendar: false, + format: 'MMMM/yy', + hourIncrement: 1, + minuteIncrement: 1, + time_24hr: false, + minDate: null, + disableWeekends: false, + disableWeekdays: false, + maxDate: null, + }, + }, + { + type: 'button', + label: 'Submit', + key: 'submit', + disableOnInvalid: true, + input: true, + tableView: false, + }, + ], + created: '2022-11-16T10:14:33.808Z', + modified: '2022-11-18T10:18:51.790Z', + machineName: 'aabvnyfwnstnovd:hhhhhh', }; diff --git a/src/components/datetime/fixtures/comp2.js b/src/components/datetime/fixtures/comp2.js index f94004be2e..1a9c26971a 100644 --- a/src/components/datetime/fixtures/comp2.js +++ b/src/components/datetime/fixtures/comp2.js @@ -1,41 +1,41 @@ export default { - 'type': 'datetime', - 'validate': { - 'custom': '', - 'required': false - }, - 'shortcutButtons': [ - { - 'label': 'Today', - 'onClick': 'date = new Date(\'2020-10-10T00:00:00\');' - } - ], - 'persistent': true, - 'protected': false, - 'timePicker': { - 'arrowkeys': true, - 'mousewheel': true, - 'readonlyInput': false, - 'showMeridian': true, - 'minuteStep': 1, - 'hourStep': 1 - }, - 'datePicker': { - 'datepickerMode': 'day', - 'yearRange': '20', - 'maxMode': 'year', - 'minMode': 'day', - 'initDate': '', - 'startingDay': 0, - 'showWeeks': true - }, - 'datepickerMode': 'day', - 'defaultDate': '', - 'enableTime': true, - 'enableDate': true, - 'format': 'yyyy-MM-dd', - 'key': 'date', - 'label': 'Date', - 'tableView': true, - 'input': true + type: 'datetime', + validate: { + custom: '', + required: false, + }, + shortcutButtons: [ + { + label: 'Today', + onClick: "date = new Date('2020-10-10T00:00:00');", + }, + ], + persistent: true, + protected: false, + timePicker: { + arrowkeys: true, + mousewheel: true, + readonlyInput: false, + showMeridian: true, + minuteStep: 1, + hourStep: 1, + }, + datePicker: { + datepickerMode: 'day', + yearRange: '20', + maxMode: 'year', + minMode: 'day', + initDate: '', + startingDay: 0, + showWeeks: true, + }, + datepickerMode: 'day', + defaultDate: '', + enableTime: true, + enableDate: true, + format: 'yyyy-MM-dd', + key: 'date', + label: 'Date', + tableView: true, + input: true, }; diff --git a/src/components/datetime/fixtures/comp3.js b/src/components/datetime/fixtures/comp3.js index d16b4fb3c0..45d0fd164c 100644 --- a/src/components/datetime/fixtures/comp3.js +++ b/src/components/datetime/fixtures/comp3.js @@ -1,38 +1,45 @@ export default { - type: 'form', - components: [ - { - label: 'Date / Time', - tableView: false, - enableMinDateInput: false, - datePicker: { disableWeekends: false, disableWeekdays: false }, - enableMaxDateInput: false, - key: 'dateTime', - type: 'datetime', - input: true, - widget: { - type: 'calendar', - displayInTimezone: 'utc', - locale: 'en', - useLocaleSettings: false, - allowInput: true, - mode: 'single', - enableTime: true, - noCalendar: false, - format: 'yyyy-MM-dd hh:mm a', - hourIncrement: 1, - minuteIncrement: 1, - 'time_24hr': false, - minDate: null, - disableWeekends: false, - disableWeekdays: false, - maxDate: null - } - }, - { label: 'Submit', showValidations: false, tableView: false, key: 'submit', type: 'button', input: true } - ], - title: 'test11', - display: 'form', - name: 'test11', - path: 'test11', + type: 'form', + components: [ + { + label: 'Date / Time', + tableView: false, + enableMinDateInput: false, + datePicker: { disableWeekends: false, disableWeekdays: false }, + enableMaxDateInput: false, + key: 'dateTime', + type: 'datetime', + input: true, + widget: { + type: 'calendar', + displayInTimezone: 'utc', + locale: 'en', + useLocaleSettings: false, + allowInput: true, + mode: 'single', + enableTime: true, + noCalendar: false, + format: 'yyyy-MM-dd hh:mm a', + hourIncrement: 1, + minuteIncrement: 1, + time_24hr: false, + minDate: null, + disableWeekends: false, + disableWeekdays: false, + maxDate: null, + }, + }, + { + label: 'Submit', + showValidations: false, + tableView: false, + key: 'submit', + type: 'button', + input: true, + }, + ], + title: 'test11', + display: 'form', + name: 'test11', + path: 'test11', }; diff --git a/src/components/datetime/fixtures/comp5.js b/src/components/datetime/fixtures/comp5.js index 66afd87745..87b7453e3e 100644 --- a/src/components/datetime/fixtures/comp5.js +++ b/src/components/datetime/fixtures/comp5.js @@ -1,46 +1,53 @@ export default { - _id: '60e41b43e27f1e926447370b', - type: 'form', - components: [ - { - label: 'Date / Time', - format: 'MM/dd/yyyyy', - tableView: false, - enableMinDateInput: true, - datePicker: { - minDate: "moment().startOf('month')", - disableFunction: 'date.getDate() !== 1', - disableWeekends: false, - disableWeekdays: false - }, - enableMaxDateInput: false, - key: 'dateTime', - type: 'datetime', - input: true, - widget: { - type: 'calendar', - displayInTimezone: 'viewer', - locale: 'en', - useLocaleSettings: false, - allowInput: true, - mode: 'single', - enableTime: true, - noCalendar: false, - format: 'MM/dd/yyyyy', - hourIncrement: 1, - minuteIncrement: 1, - 'time_24hr': false, - minDate: "moment().startOf('month')", - disableWeekends: false, - disableWeekdays: false, - disableFunction: 'date.getDate() !== 1', - maxDate: null - } - }, - { type: 'button', label: 'Submit', key: 'submit', disableOnInvalid: true, input: true, tableView: false } - ], - title: 'test', - display: 'form', - name: 'test', - path: 'test', + _id: '60e41b43e27f1e926447370b', + type: 'form', + components: [ + { + label: 'Date / Time', + format: 'MM/dd/yyyyy', + tableView: false, + enableMinDateInput: true, + datePicker: { + minDate: "moment().startOf('month')", + disableFunction: 'date.getDate() !== 1', + disableWeekends: false, + disableWeekdays: false, + }, + enableMaxDateInput: false, + key: 'dateTime', + type: 'datetime', + input: true, + widget: { + type: 'calendar', + displayInTimezone: 'viewer', + locale: 'en', + useLocaleSettings: false, + allowInput: true, + mode: 'single', + enableTime: true, + noCalendar: false, + format: 'MM/dd/yyyyy', + hourIncrement: 1, + minuteIncrement: 1, + time_24hr: false, + minDate: "moment().startOf('month')", + disableWeekends: false, + disableWeekdays: false, + disableFunction: 'date.getDate() !== 1', + maxDate: null, + }, + }, + { + type: 'button', + label: 'Submit', + key: 'submit', + disableOnInvalid: true, + input: true, + tableView: false, + }, + ], + title: 'test', + display: 'form', + name: 'test', + path: 'test', }; diff --git a/src/components/datetime/fixtures/comp6.js b/src/components/datetime/fixtures/comp6.js index 545a20bde4..2693add1c8 100644 --- a/src/components/datetime/fixtures/comp6.js +++ b/src/components/datetime/fixtures/comp6.js @@ -1,47 +1,47 @@ export default { - type: 'form', - components: [ - { - label: 'Date / Time', - format: 'hh:mm a', - tableView: false, - enableDate: false, - enableMinDateInput: false, - datePicker: { disableWeekends: false, disableWeekdays: false }, - enableMaxDateInput: false, - key: 'dateTime', - type: 'datetime', - input: true, - widget: { - type: 'calendar', - displayInTimezone: 'viewer', - locale: 'en', - useLocaleSettings: false, - allowInput: true, - mode: 'single', - enableTime: true, - noCalendar: true, - format: 'hh:mm a', - hourIncrement: 1, - minuteIncrement: 1, - 'time_24hr': false, - minDate: null, - disableWeekends: false, - disableWeekdays: false, - maxDate: null, - }, - }, - { - type: 'button', - label: 'Submit', - key: 'submit', - disableOnInvalid: true, - input: true, - tableView: false, - }, - ], - title: 'FIO-3743', - display: 'form', - name: 'fio3743', - path: 'fio3743', + type: 'form', + components: [ + { + label: 'Date / Time', + format: 'hh:mm a', + tableView: false, + enableDate: false, + enableMinDateInput: false, + datePicker: { disableWeekends: false, disableWeekdays: false }, + enableMaxDateInput: false, + key: 'dateTime', + type: 'datetime', + input: true, + widget: { + type: 'calendar', + displayInTimezone: 'viewer', + locale: 'en', + useLocaleSettings: false, + allowInput: true, + mode: 'single', + enableTime: true, + noCalendar: true, + format: 'hh:mm a', + hourIncrement: 1, + minuteIncrement: 1, + time_24hr: false, + minDate: null, + disableWeekends: false, + disableWeekdays: false, + maxDate: null, + }, + }, + { + type: 'button', + label: 'Submit', + key: 'submit', + disableOnInvalid: true, + input: true, + tableView: false, + }, + ], + title: 'FIO-3743', + display: 'form', + name: 'fio3743', + path: 'fio3743', }; diff --git a/src/components/datetime/fixtures/comp7.js b/src/components/datetime/fixtures/comp7.js index 7cca21c4ca..d5e1e0ac8a 100644 --- a/src/components/datetime/fixtures/comp7.js +++ b/src/components/datetime/fixtures/comp7.js @@ -1,51 +1,51 @@ export default { - _id: '61c1e89cc6eafd71c3175f0d', - title: 'Date Time', - name: 'dateTime', - path: 'datetime', - type: 'form', - display: 'form', - components: [ - { - label: 'Date / Time', - format: 'yyyy-MM-dd', - tableView: true, - datePicker: { disableWeekends: false, disableWeekdays: false }, - enableTime: false, - enableMinDateInput: false, - enableMaxDateInput: false, - key: 'dateTime', - type: 'datetime', - input: true, - widget: { - type: 'calendar', - displayInTimezone: 'viewer', - locale: 'en', - useLocaleSettings: false, - allowInput: true, - mode: 'single', - enableTime: false, - noCalendar: false, - format: 'yyyy-MM-dd', - hourIncrement: 1, - minuteIncrement: 1, - 'time_24hr': false, - minDate: null, - disableWeekends: false, - disableWeekdays: false, - maxDate: null, - }, - }, - { - type: 'button', - label: 'Submit', - key: 'submit', - disableOnInvalid: true, - input: true, - tableView: false, - }, - ], - settings: {}, - properties: {}, - project: '61c101d0792d8ffc9be99694', + _id: '61c1e89cc6eafd71c3175f0d', + title: 'Date Time', + name: 'dateTime', + path: 'datetime', + type: 'form', + display: 'form', + components: [ + { + label: 'Date / Time', + format: 'yyyy-MM-dd', + tableView: true, + datePicker: { disableWeekends: false, disableWeekdays: false }, + enableTime: false, + enableMinDateInput: false, + enableMaxDateInput: false, + key: 'dateTime', + type: 'datetime', + input: true, + widget: { + type: 'calendar', + displayInTimezone: 'viewer', + locale: 'en', + useLocaleSettings: false, + allowInput: true, + mode: 'single', + enableTime: false, + noCalendar: false, + format: 'yyyy-MM-dd', + hourIncrement: 1, + minuteIncrement: 1, + time_24hr: false, + minDate: null, + disableWeekends: false, + disableWeekdays: false, + maxDate: null, + }, + }, + { + type: 'button', + label: 'Submit', + key: 'submit', + disableOnInvalid: true, + input: true, + tableView: false, + }, + ], + settings: {}, + properties: {}, + project: '61c101d0792d8ffc9be99694', }; diff --git a/src/components/datetime/fixtures/comp8.js b/src/components/datetime/fixtures/comp8.js index 92e52a57a2..0e776ddff7 100644 --- a/src/components/datetime/fixtures/comp8.js +++ b/src/components/datetime/fixtures/comp8.js @@ -1,50 +1,53 @@ export default { - 'title': 'test11', - 'name': 'test11', - 'path': 'test11', - 'type': 'form', - 'display': 'form', - 'components': [{ - 'label': 'Date / Time', - 'format': 'yyyy-MM-dd', - 'tableView': false, - 'enableMinDateInput': false, - 'datePicker': { - 'disableWeekends': false, - 'disableWeekdays': false - }, - 'enableMaxDateInput': false, - 'multiple': true, - 'validate': { - 'required': true - }, - 'key': 'dateTime', - 'type': 'datetime', - 'input': true, - 'widget': { - 'type': 'calendar', - 'displayInTimezone': 'viewer', - 'locale': 'en', - 'useLocaleSettings': false, - 'allowInput': true, - 'mode': 'single', - 'enableTime': true, - 'noCalendar': false, - 'format': 'yyyy-MM-dd', - 'hourIncrement': 1, - 'minuteIncrement': 1, - 'time_24hr': false, - 'minDate': null, - 'disableWeekends': false, - 'disableWeekdays': false, - 'maxDate': null - } - }, { - 'type': 'button', - 'label': 'Submit', - 'key': 'submit', - 'disableOnInvalid': true, - 'input': true, - 'tableView': false - }], + title: 'test11', + name: 'test11', + path: 'test11', + type: 'form', + display: 'form', + components: [ + { + label: 'Date / Time', + format: 'yyyy-MM-dd', + tableView: false, + enableMinDateInput: false, + datePicker: { + disableWeekends: false, + disableWeekdays: false, + }, + enableMaxDateInput: false, + multiple: true, + validate: { + required: true, + }, + key: 'dateTime', + type: 'datetime', + input: true, + widget: { + type: 'calendar', + displayInTimezone: 'viewer', + locale: 'en', + useLocaleSettings: false, + allowInput: true, + mode: 'single', + enableTime: true, + noCalendar: false, + format: 'yyyy-MM-dd', + hourIncrement: 1, + minuteIncrement: 1, + time_24hr: false, + minDate: null, + disableWeekends: false, + disableWeekdays: false, + maxDate: null, + }, + }, + { + type: 'button', + label: 'Submit', + key: 'submit', + disableOnInvalid: true, + input: true, + tableView: false, + }, + ], }; diff --git a/src/components/datetime/fixtures/comp9.js b/src/components/datetime/fixtures/comp9.js index ab56c0cd1e..37e0f76279 100644 --- a/src/components/datetime/fixtures/comp9.js +++ b/src/components/datetime/fixtures/comp9.js @@ -1,93 +1,93 @@ export default { - _id: '625e8bb06c97f942effd7b8b', - title: 'Date Time', - name: 'dateTime', - path: 'datetime', - type: 'form', - display: 'form', - components: [ - { - label: 'Date / Time', - displayInTimezone: 'location', - format: 'yyyy-MM-dd', - tableView: false, - datePicker: { - disableWeekends: false, - disableWeekdays: false - }, - enableTime: false, - enableMinDateInput: false, - enableMaxDateInput: false, - key: 'dateTime', - type: 'datetime', - timezone: 'America/Chicago', - input: true, - widget: { - type: 'calendar', - timezone: 'America/Chicago', - displayInTimezone: 'location', - locale: 'en', - useLocaleSettings: false, - allowInput: true, - mode: 'single', - enableTime: false, - noCalendar: false, - format: 'yyyy-MM-dd', - hourIncrement: 1, - minuteIncrement: 1, - 'time_24hr': false, - minDate: null, - disableWeekends: false, - disableWeekdays: false, - maxDate: null - } - }, - { - label: 'Date / Time', - displayInTimezone: 'location', - format: 'yyyy-MM-dd', - tableView: false, - datePicker: { - disableWeekends: false, - disableWeekdays: false - }, - enableTime: false, - enableMinDateInput: false, - enableMaxDateInput: false, - key: 'dateTime1', - type: 'datetime', - timezone: 'Asia/Seoul', - input: true, - widget: { - type: 'calendar', - timezone: 'Asia/Seoul', - displayInTimezone: 'location', - locale: 'en', - useLocaleSettings: false, - allowInput: true, - mode: 'single', - enableTime: false, - noCalendar: false, - format: 'yyyy-MM-dd', - hourIncrement: 1, - minuteIncrement: 1, - 'time_24hr': false, - minDate: null, - disableWeekends: false, - disableWeekdays: false, - maxDate: null - } - }, - { - type: 'button', - label: 'Submit', - key: 'submit', - disableOnInvalid: true, - input: true, - tableView: false - } - ], - settings: {}, - properties: {}, - project: '61c101d0792d8ffc9be99694', + _id: '625e8bb06c97f942effd7b8b', + title: 'Date Time', + name: 'dateTime', + path: 'datetime', + type: 'form', + display: 'form', + components: [ + { + label: 'Date / Time', + displayInTimezone: 'location', + format: 'yyyy-MM-dd', + tableView: false, + datePicker: { + disableWeekends: false, + disableWeekdays: false, + }, + enableTime: false, + enableMinDateInput: false, + enableMaxDateInput: false, + key: 'dateTime', + type: 'datetime', + timezone: 'America/Chicago', + input: true, + widget: { + type: 'calendar', + timezone: 'America/Chicago', + displayInTimezone: 'location', + locale: 'en', + useLocaleSettings: false, + allowInput: true, + mode: 'single', + enableTime: false, + noCalendar: false, + format: 'yyyy-MM-dd', + hourIncrement: 1, + minuteIncrement: 1, + time_24hr: false, + minDate: null, + disableWeekends: false, + disableWeekdays: false, + maxDate: null, + }, + }, + { + label: 'Date / Time', + displayInTimezone: 'location', + format: 'yyyy-MM-dd', + tableView: false, + datePicker: { + disableWeekends: false, + disableWeekdays: false, + }, + enableTime: false, + enableMinDateInput: false, + enableMaxDateInput: false, + key: 'dateTime1', + type: 'datetime', + timezone: 'Asia/Seoul', + input: true, + widget: { + type: 'calendar', + timezone: 'Asia/Seoul', + displayInTimezone: 'location', + locale: 'en', + useLocaleSettings: false, + allowInput: true, + mode: 'single', + enableTime: false, + noCalendar: false, + format: 'yyyy-MM-dd', + hourIncrement: 1, + minuteIncrement: 1, + time_24hr: false, + minDate: null, + disableWeekends: false, + disableWeekdays: false, + maxDate: null, + }, + }, + { + type: 'button', + label: 'Submit', + key: 'submit', + disableOnInvalid: true, + input: true, + tableView: false, + }, + ], + settings: {}, + properties: {}, + project: '61c101d0792d8ffc9be99694', }; diff --git a/src/components/datetime/fixtures/index.js b/src/components/datetime/fixtures/index.js index c8c6e0a000..d53a95c9aa 100644 --- a/src/components/datetime/fixtures/index.js +++ b/src/components/datetime/fixtures/index.js @@ -9,4 +9,16 @@ import comp9 from './comp9'; import comp10 from './comp10'; import comp11 from './comp11'; import comp12 from './comp12'; -export { comp1, comp10, comp11, comp12, comp2, comp3, comp5, comp6, comp7, comp8, comp9 }; +export { + comp1, + comp10, + comp11, + comp12, + comp2, + comp3, + comp5, + comp6, + comp7, + comp8, + comp9, +}; diff --git a/src/components/datetime/fixtures/values.js b/src/components/datetime/fixtures/values.js index 5dd0c45f43..cadc7f2e07 100644 --- a/src/components/datetime/fixtures/values.js +++ b/src/components/datetime/fixtures/values.js @@ -1,5 +1 @@ -export default [ - '2019-11-06T18:00:00.000', - '2019-11-25T20:11:21.887', -]; - +export default ['2019-11-06T18:00:00.000', '2019-11-25T20:11:21.887']; diff --git a/src/components/day/Day.form.js b/src/components/day/Day.form.js index 928696ad30..30f8208df9 100644 --- a/src/components/day/Day.form.js +++ b/src/components/day/Day.form.js @@ -6,37 +6,40 @@ import DayEditDay from './editForm/Day.edit.day'; import DayEditMonth from './editForm/Day.edit.month'; import DayEditYear from './editForm/Day.edit.year'; -export default function(...extend) { - return Components.baseEditForm([ - { - key: 'display', - components: DayEditDisplay - }, - { - key: 'data', - components: DayEditData, - }, - { - key: 'validation', - components: DayEditValidation - }, - { - key: 'day', - label: 'Day', - weight: 3, - components: DayEditDay - }, - { - key: 'month', - label: 'Month', - weight: 3, - components: DayEditMonth - }, - { - key: 'year', - label: 'Year', - weight: 3, - components: DayEditYear - }, - ], ...extend); +export default function (...extend) { + return Components.baseEditForm( + [ + { + key: 'display', + components: DayEditDisplay, + }, + { + key: 'data', + components: DayEditData, + }, + { + key: 'validation', + components: DayEditValidation, + }, + { + key: 'day', + label: 'Day', + weight: 3, + components: DayEditDay, + }, + { + key: 'month', + label: 'Month', + weight: 3, + components: DayEditMonth, + }, + { + key: 'year', + label: 'Year', + weight: 3, + components: DayEditYear, + }, + ], + ...extend, + ); } diff --git a/src/components/day/Day.js b/src/components/day/Day.js index add4bb960d..fa0e5e9334 100644 --- a/src/components/day/Day.js +++ b/src/components/day/Day.js @@ -1,643 +1,764 @@ import _ from 'lodash'; import moment from 'moment'; import Field from '../_classes/field/Field'; -import { boolValue, componentValueTypes, getComponentSavedTypes, getLocaleDateFormatInfo } from '../../utils/utils'; +import { + boolValue, + componentValueTypes, + getComponentSavedTypes, + getLocaleDateFormatInfo, +} from '../../utils/utils'; export default class DayComponent extends Field { - static schema(...extend) { - return Field.schema({ - type: 'day', - label: 'Day', - key: 'day', - fields: { - day: { - type: 'number', - placeholder: '', - required: false - }, - month: { - type: 'select', - placeholder: '', - required: false - }, - year: { - type: 'number', - placeholder: '', - required: false - } - }, - dayFirst: false - }, ...extend); - } - - static get builderInfo() { - return { - title: 'Day', - group: 'advanced', - icon: 'calendar', - documentation: '/userguide/form-building/advanced-components#day', - weight: 50, - schema: DayComponent.schema() - }; - } - - static get conditionOperatorsSettings() { - return { - ...super.conditionOperatorsSettings, - operators: ['isDateEqual', 'isNotDateEqual', 'isEmpty', 'isNotEmpty','dateLessThan', 'dateGreaterThan', 'dateLessThanOrEqual','dateGreaterThanOrEqual'], - }; - } - - static savedValueTypes(schema) { - schema = schema || {}; - return getComponentSavedTypes(schema) || [componentValueTypes.string]; - } - - constructor(component, options, data) { - if (component.maxDate && component.maxDate.indexOf('moment(') === -1) { - component.maxDate = moment(component.maxDate, 'YYYY-MM-DD').toISOString(); - } - if (component.minDate && component.minDate.indexOf('moment(') === -1) { - component.minDate = moment(component.minDate, 'YYYY-MM-DD').toISOString(); - } - super(component, options, data); - } - - static get serverConditionSettings() { - return DayComponent.conditionOperatorsSettings; - } - - /** - * The empty value for day component. - * - * @return {'00/00/0000'} - */ - get emptyValue() { - return '00/00/0000'; - } - - get valueMask() { - return /^\d{2}\/\d{2}\/\d{4}$/; - } - - get dayRequired() { - return this.showDay && _.get(this.component, 'fields.day.required', false); - } - - get showDay() { - return !_.get(this.component, 'fields.day.hide', false); - } - - get monthRequired() { - return this.showMonth && _.get(this.component, 'fields.month.required', false); - } - - get showMonth() { - return !_.get(this.component, 'fields.month.hide', false); - } - - get yearRequired() { - return this.showYear && _.get(this.component, 'fields.year.required', false); - } - - get showYear() { - return !_.get(this.component, 'fields.year.hide', false); - } - - get defaultSchema() { - return DayComponent.schema(); - } - - get shouldDisabled() { - return super.shouldDisabled || this.parentDisabled; - } - - get inputInfo() { - const info = super.elementInfo(); - info.type = 'input'; - info.attr.type = 'hidden'; - info.changeEvent = 'input'; - return info; - } - - inputDefinition(name) { - let min, max; - if (name === 'day') { - min = 1; - max = 31; - } - if (name === 'month') { - min = 1; - max = 12; - } - if (name === 'year') { - min = _.get(this.component, 'fields.year.minYear', 1900) || 1900; - max = _.get(this.component, 'fields.year.maxYear', 2030) || 1900; - } - return { - type: 'input', - ref: name, - attr: { - id: `${this.component.key}-${name}`, - class: `form-control ${this.transform('class', `formio-day-component-${name}`)}`, - type: this.component.fields[name].type === 'select' ? 'select' : 'number', - placeholder: this.component.fields[name].placeholder, - step: 1, - min, - max, - } - }; - } - - selectDefinition(name) { - return { - multiple: false, - ref: name, - widget: 'html5', - attr: { - id: `${this.component.key}-${name}`, - class: 'form-control', - name, - lang: this.options.language - } - }; - } - - get days() { - if (this._days) { - return this._days; - } - this._days = [ - { value: '', label: _.get(this.component, 'fields.day.placeholder', '') } - ]; - for (let x = 1; x <= 31; x++) { - this._days.push({ - value: x, - label: x.toString() - }); - } - return this._days; - } - - get months() { - if (this._months) { - return this._months; - } - this._months = [ - { - value: '', - label: _.get(this.component, 'fields.month.placeholder') || (this.hideInputLabels ? this.t('Month') : '') - }, - { value: 1, label: 'January' }, - { value: 2, label: 'February' }, - { value: 3, label: 'March' }, - { value: 4, label: 'April' }, - { value: 5, label: 'May' }, - { value: 6, label: 'June' }, - { value: 7, label: 'July' }, - { value: 8, label: 'August' }, - { value: 9, label: 'September' }, - { value: 10, label: 'October' }, - { value: 11, label: 'November' }, - { value: 12, label: 'December' } - ]; - return this._months; - } - - get years() { - if (this._years) { - return this._years; - } - this._years = [ - { value: '', label: _.get(this.component, 'fields.year.placeholder', '') } - ]; - const minYears = _.get(this.component, 'fields.year.minYear', 1900) || 1900; - const maxYears = _.get(this.component, 'fields.year.maxYear', 2030) || 2030; - for (let x = minYears; x <= maxYears; x++) { - this._years.push({ - value: x, - label: x.toString() - }); - } - return this._years; - } - - setErrorClasses(elements, dirty, hasError) { - super.setErrorClasses(elements, dirty, hasError); - super.setErrorClasses([this.refs.day, this.refs.month, this.refs.year], dirty, hasError); - } - - removeInputError(elements) { - super.removeInputError([this.refs.day, this.refs.month, this.refs.year]); - super.removeInputError(elements); - } - - init() { - super.init(); - this.validators = this.validators.concat(['day', 'maxDate', 'minDate', 'minYear', 'maxYear']); - - const minYear = this.component.fields.year.minYear; - const maxYear = this.component.fields.year.maxYear; - this.component.maxYear = maxYear; - this.component.minYear = minYear; - - const dateFormatInfo = getLocaleDateFormatInfo(this.options.language); - this.dayFirst = this.component.useLocaleSettings - ? dateFormatInfo.dayFirst - : this.component.dayFirst; - } - - render() { - if (this.isHtmlRenderMode()) { - return super.render(this.renderTemplate('input')); - } - - return super.render(this.renderTemplate('day', { - dayFirst: this.dayFirst, - showDay: this.showDay, - showMonth: this.showMonth, - showYear: this.showYear, - day: this.renderField('day'), - month: this.renderField('month'), - year: this.renderField('year'), - })); - } - - renderField(name) { - if (this.component.fields[name].type === 'select') { - return this.renderTemplate('select', { - input: this.selectDefinition(name), - selectOptions: this[`${name}s`].reduce((html, option) => - html + this.renderTemplate('selectOption', { - option, - selected: false, - attrs: {} - }), '' - ), - }); - } - else { - return this.renderTemplate('input', { - prefix: this.prefix, - suffix: this.suffix, - input: this.inputDefinition(name) - }); - } - } - - attach(element) { - this.loadRefs(element, { day: 'single', month: 'single', year: 'single', input: 'multiple' }); - const superAttach = super.attach(element); - - const updateValueAndSaveFocus = (element, name) => () => { - try { - this.saveCaretPosition(element, name); - } - catch (err) { - console.warn('An error occurred while trying to save caret position', err); - } - this.updateValue(null, { - modified: true, - }); - }; - - if (this.shouldDisabled) { - this.setDisabled(this.refs.day, true); - this.setDisabled(this.refs.month, true); - this.setDisabled(this.refs.year, true); - if (this.refs.input) { - this.refs.input.forEach((input) => this.setDisabled(input, true)); - } - } - else { - this.addEventListener(this.refs.day, 'input', updateValueAndSaveFocus(this.refs.day, 'day')); - // TODO: Need to rework this to work with day select as well. - // Change day max input when month changes. - this.addEventListener(this.refs.month, 'input', () => { - const maxDay = this.refs.year ? parseInt( - new Date(this.refs.year.value, this.refs.month.value, 0).getDate(), - 10 - ) - : ''; - const day = this.getFieldValue('day'); - if (!this.component.fields.day.hide && maxDay) { - this.refs.day.max = maxDay; - } - if (maxDay && day > maxDay) { - this.refs.day.value = this.refs.day.max; - } - updateValueAndSaveFocus(this.refs.month, 'month')(); - }); - this.addEventListener(this.refs.year, 'input', updateValueAndSaveFocus(this.refs.year, 'year')); - this.addEventListener(this.refs.input, this.info.changeEvent, () => this.updateValue(null, { - modified: true - })); - [this.refs.day, this.refs.month, this.refs.year].filter((element) => !!element).forEach((element) => { - super.addFocusBlurEvents(element); - }); - } - this.setValue(this.dataValue); - // Force the disabled state with getters and setters. - this.disabled = this.shouldDisabled; - return superAttach; - } - - validateRequired(setting, value) { - const { day, month, year } = this.parts; - if (this.dayRequired && !day) { - return false; - } - - if (this.monthRequired && !month) { - return false; - } - - if (this.yearRequired && !year) { - return false; - } - - if (!boolValue(setting)) { - return true; - } - return !this.isEmpty(value); - } - - set disabled(disabled) { - super.disabled = disabled; - if (!this.refs.year || !this.refs.month || !this.refs.day) { - return; - } - if (disabled) { - this.refs.year.setAttribute('disabled', 'disabled'); - this.refs.month.setAttribute('disabled', 'disabled'); - this.refs.day.setAttribute('disabled', 'disabled'); - } - else { - this.refs.year.removeAttribute('disabled'); - this.refs.month.removeAttribute('disabled'); - this.refs.day.removeAttribute('disabled'); - } - } - - normalizeValue(value) { - if (!value || this.valueMask.test(value)) { - return value; - } - const dateParts = []; - const valueParts = value.split('/'); - const [DAY, MONTH, YEAR] = this.component.dayFirst ? [0, 1, 2] : [1, 0, 2]; - const defaultValue = this.component.defaultValue ? this.component.defaultValue.split('/') : ''; - - const getNextPart = (shouldTake, defaultValue) => - dateParts.push(shouldTake ? valueParts.shift() : defaultValue); - - if (this.dayFirst) { - getNextPart(this.showDay, defaultValue ? defaultValue[DAY] : '00'); - } - - getNextPart(this.showMonth, defaultValue ? defaultValue[MONTH] : '00'); - - if (!this.dayFirst) { - getNextPart(this.showDay, defaultValue ? defaultValue[DAY] : '00'); - } - - getNextPart(this.showYear, defaultValue ? defaultValue[YEAR] : '0000'); - - return dateParts.join('/'); - } - - /** - * Set the value at a specific index. - * - * @param index - * @param value - */ - setValueAt(index, value) { - // temporary solution to avoid input reset - // on invalid date. - if (!value || value === 'Invalid date') { - return null; + static schema(...extend) { + return Field.schema( + { + type: 'day', + label: 'Day', + key: 'day', + fields: { + day: { + type: 'number', + placeholder: '', + required: false, + }, + month: { + type: 'select', + placeholder: '', + required: false, + }, + year: { + type: 'number', + placeholder: '', + required: false, + }, + }, + dayFirst: false, + }, + ...extend, + ); + } + + static get builderInfo() { + return { + title: 'Day', + group: 'advanced', + icon: 'calendar', + documentation: '/userguide/form-building/advanced-components#day', + weight: 50, + schema: DayComponent.schema(), + }; + } + + static get conditionOperatorsSettings() { + return { + ...super.conditionOperatorsSettings, + operators: [ + 'isDateEqual', + 'isNotDateEqual', + 'isEmpty', + 'isNotEmpty', + 'dateLessThan', + 'dateGreaterThan', + 'dateLessThanOrEqual', + 'dateGreaterThanOrEqual', + ], + }; + } + + static savedValueTypes(schema) { + schema = schema || {}; + return getComponentSavedTypes(schema) || [componentValueTypes.string]; + } + + constructor(component, options, data) { + if (component.maxDate && component.maxDate.indexOf('moment(') === -1) { + component.maxDate = moment( + component.maxDate, + 'YYYY-MM-DD', + ).toISOString(); + } + if (component.minDate && component.minDate.indexOf('moment(') === -1) { + component.minDate = moment( + component.minDate, + 'YYYY-MM-DD', + ).toISOString(); + } + super(component, options, data); } - const parts = value.split('/'); - let day; - if (this.component.dayFirst) { - day = parts.shift(); + + static get serverConditionSettings() { + return DayComponent.conditionOperatorsSettings; } - const month = parts.shift(); - if (!this.component.dayFirst) { - day = parts.shift(); + + /** + * The empty value for day component. + * + * @return {'00/00/0000'} + */ + get emptyValue() { + return '00/00/0000'; } - const year = parts.shift(); - if (this.refs.day && this.showDay) { - this.refs.day.value = day === '00' ? '' : parseInt(day, 10); + get valueMask() { + return /^\d{2}\/\d{2}\/\d{4}$/; } - if (this.refs.month && this.showMonth) { - this.refs.month.value = month === '00' ? '' : parseInt(month, 10); + + get dayRequired() { + return ( + this.showDay && _.get(this.component, 'fields.day.required', false) + ); } - if (this.refs.year && this.showYear) { - this.refs.year.value = year === '0000' ? '' : parseInt(year, 10); + + get showDay() { + return !_.get(this.component, 'fields.day.hide', false); } - } - getFieldValue(name) { - const parts = this.dataValue ? this.dataValue.split('/') : []; - let val = 0; + get monthRequired() { + return ( + this.showMonth && + _.get(this.component, 'fields.month.required', false) + ); + } - switch (name) { - case 'month': - val = parts[this.dayFirst ? 1 : 0]; - break; - case 'day': - val = parts[this.dayFirst ? 0 : 1]; - break; - case 'year': - val = parts[2]; - break; + get showMonth() { + return !_.get(this.component, 'fields.month.hide', false); } - val = parseInt(val, 10); - return (!_.isNaN(val) && _.isNumber(val)) ? val : 0; - } + get yearRequired() { + return ( + this.showYear && + _.get(this.component, 'fields.year.required', false) + ); + } - get parts() { - return { - day: this.getFieldValue('day'), - month: this.getFieldValue('month'), - year: this.getFieldValue('year'), - }; - } + get showYear() { + return !_.get(this.component, 'fields.year.hide', false); + } - /** - * Get the format for the value string. - * @returns {string} - */ - get format() { - let format = ''; - if (this.component.dayFirst && this.showDay) { - format += 'D/'; + get defaultSchema() { + return DayComponent.schema(); } - if (this.showMonth) { - format += 'M/'; + + get shouldDisabled() { + return super.shouldDisabled || this.parentDisabled; } - if (!this.component.dayFirst && this.showDay) { - format += 'D/'; + + get inputInfo() { + const info = super.elementInfo(); + info.type = 'input'; + info.attr.type = 'hidden'; + info.changeEvent = 'input'; + return info; } - if (this.showYear) { - format += 'YYYY'; - return format; + + inputDefinition(name) { + let min, max; + if (name === 'day') { + min = 1; + max = 31; + } + if (name === 'month') { + min = 1; + max = 12; + } + if (name === 'year') { + min = _.get(this.component, 'fields.year.minYear', 1900) || 1900; + max = _.get(this.component, 'fields.year.maxYear', 2030) || 1900; + } + return { + type: 'input', + ref: name, + attr: { + id: `${this.component.key}-${name}`, + class: `form-control ${this.transform( + 'class', + `formio-day-component-${name}`, + )}`, + type: + this.component.fields[name].type === 'select' + ? 'select' + : 'number', + placeholder: this.component.fields[name].placeholder, + step: 1, + min, + max, + }, + }; + } + + selectDefinition(name) { + return { + multiple: false, + ref: name, + widget: 'html5', + attr: { + id: `${this.component.key}-${name}`, + class: 'form-control', + name, + lang: this.options.language, + }, + }; + } + + get days() { + if (this._days) { + return this._days; + } + this._days = [ + { + value: '', + label: _.get(this.component, 'fields.day.placeholder', ''), + }, + ]; + for (let x = 1; x <= 31; x++) { + this._days.push({ + value: x, + label: x.toString(), + }); + } + return this._days; + } + + get months() { + if (this._months) { + return this._months; + } + this._months = [ + { + value: '', + label: + _.get(this.component, 'fields.month.placeholder') || + (this.hideInputLabels ? this.t('Month') : ''), + }, + { value: 1, label: 'January' }, + { value: 2, label: 'February' }, + { value: 3, label: 'March' }, + { value: 4, label: 'April' }, + { value: 5, label: 'May' }, + { value: 6, label: 'June' }, + { value: 7, label: 'July' }, + { value: 8, label: 'August' }, + { value: 9, label: 'September' }, + { value: 10, label: 'October' }, + { value: 11, label: 'November' }, + { value: 12, label: 'December' }, + ]; + return this._months; + } + + get years() { + if (this._years) { + return this._years; + } + this._years = [ + { + value: '', + label: _.get(this.component, 'fields.year.placeholder', ''), + }, + ]; + const minYears = + _.get(this.component, 'fields.year.minYear', 1900) || 1900; + const maxYears = + _.get(this.component, 'fields.year.maxYear', 2030) || 2030; + for (let x = minYears; x <= maxYears; x++) { + this._years.push({ + value: x, + label: x.toString(), + }); + } + return this._years; + } + + setErrorClasses(elements, dirty, hasError) { + super.setErrorClasses(elements, dirty, hasError); + super.setErrorClasses( + [this.refs.day, this.refs.month, this.refs.year], + dirty, + hasError, + ); + } + + removeInputError(elements) { + super.removeInputError([ + this.refs.day, + this.refs.month, + this.refs.year, + ]); + super.removeInputError(elements); + } + + init() { + super.init(); + this.validators = this.validators.concat([ + 'day', + 'maxDate', + 'minDate', + 'minYear', + 'maxYear', + ]); + + const minYear = this.component.fields.year.minYear; + const maxYear = this.component.fields.year.maxYear; + this.component.maxYear = maxYear; + this.component.minYear = minYear; + + const dateFormatInfo = getLocaleDateFormatInfo(this.options.language); + this.dayFirst = this.component.useLocaleSettings + ? dateFormatInfo.dayFirst + : this.component.dayFirst; + } + + render() { + if (this.isHtmlRenderMode()) { + return super.render(this.renderTemplate('input')); + } + + return super.render( + this.renderTemplate('day', { + dayFirst: this.dayFirst, + showDay: this.showDay, + showMonth: this.showMonth, + showYear: this.showYear, + day: this.renderField('day'), + month: this.renderField('month'), + year: this.renderField('year'), + }), + ); + } + + renderField(name) { + if (this.component.fields[name].type === 'select') { + return this.renderTemplate('select', { + input: this.selectDefinition(name), + selectOptions: this[`${name}s`].reduce( + (html, option) => + html + + this.renderTemplate('selectOption', { + option, + selected: false, + attrs: {}, + }), + '', + ), + }); + } else { + return this.renderTemplate('input', { + prefix: this.prefix, + suffix: this.suffix, + input: this.inputDefinition(name), + }); + } } - else { - // Trim off the "/" from the end of the format string. - return format.length ? format.substring(0, format.length - 1) : format; + + attach(element) { + this.loadRefs(element, { + day: 'single', + month: 'single', + year: 'single', + input: 'multiple', + }); + const superAttach = super.attach(element); + + const updateValueAndSaveFocus = (element, name) => () => { + try { + this.saveCaretPosition(element, name); + } catch (err) { + console.warn( + 'An error occurred while trying to save caret position', + err, + ); + } + this.updateValue(null, { + modified: true, + }); + }; + + if (this.shouldDisabled) { + this.setDisabled(this.refs.day, true); + this.setDisabled(this.refs.month, true); + this.setDisabled(this.refs.year, true); + if (this.refs.input) { + this.refs.input.forEach((input) => + this.setDisabled(input, true), + ); + } + } else { + this.addEventListener( + this.refs.day, + 'input', + updateValueAndSaveFocus(this.refs.day, 'day'), + ); + // TODO: Need to rework this to work with day select as well. + // Change day max input when month changes. + this.addEventListener(this.refs.month, 'input', () => { + const maxDay = this.refs.year + ? parseInt( + new Date( + this.refs.year.value, + this.refs.month.value, + 0, + ).getDate(), + 10, + ) + : ''; + const day = this.getFieldValue('day'); + if (!this.component.fields.day.hide && maxDay) { + this.refs.day.max = maxDay; + } + if (maxDay && day > maxDay) { + this.refs.day.value = this.refs.day.max; + } + updateValueAndSaveFocus(this.refs.month, 'month')(); + }); + this.addEventListener( + this.refs.year, + 'input', + updateValueAndSaveFocus(this.refs.year, 'year'), + ); + this.addEventListener(this.refs.input, this.info.changeEvent, () => + this.updateValue(null, { + modified: true, + }), + ); + [this.refs.day, this.refs.month, this.refs.year] + .filter((element) => !!element) + .forEach((element) => { + super.addFocusBlurEvents(element); + }); + } + this.setValue(this.dataValue); + // Force the disabled state with getters and setters. + this.disabled = this.shouldDisabled; + return superAttach; } - } - /** - * Return the date for this component. - * - * @param value - * @return {*} - */ - getDate(value) { - let defaults = [], day, month, year; - // Map positions to identifiers to get default values for each part of day - const [DAY, MONTH, YEAR] = this.component.dayFirst ? [0, 1, 2] : [1, 0, 2]; - const defaultValue = value || this.component.defaultValue; - if (defaultValue) { - defaults = defaultValue.split('/').map(x => parseInt(x, 10)); - } - - if (this.showDay && this.refs.day) { - day = parseInt(this.refs.day.value, 10); - } - if (day === undefined || _.isNaN(day)) { - day = defaults[DAY] && !_.isNaN(defaults[DAY]) ? defaults[DAY] : 0; - } - - if (this.showMonth && this.refs.month) { - // Months are 0 indexed. - month = parseInt(this.refs.month.value, 10); - } - if (month === undefined || _.isNaN(month)) { - month = defaults[MONTH] && !_.isNaN(defaults[MONTH]) ? defaults[MONTH] : 0; - } - - if (this.showYear && this.refs.year) { - year = parseInt(this.refs.year.value); - } - if (year === undefined || _.isNaN(year)) { - year = defaults[YEAR] && !_.isNaN(defaults[YEAR]) ? defaults[YEAR] : 0; - } - - let result; - if (!day && !month && !year) { - return null; - } - - // add trailing zeros if the data is showed - day = this.showDay ? day.toString().padStart(2, 0) : ''; - month = this.showMonth ? month.toString().padStart(2, 0) : ''; - year = this.showYear ? year.toString().padStart(4, 0) : ''; - - if (this.component.dayFirst) { - result = `${day}${this.showDay && this.showMonth || this.showDay && this.showYear ? '/' : ''}${month}${this.showMonth && this.showYear ? '/' : ''}${year}`; - } - else { - result = `${month}${this.showDay && this.showMonth || this.showMonth && this.showYear ? '/' : ''}${day}${this.showDay && this.showYear ? '/' : ''}${year}`; - } - - return result; - } - - /** - * Return the date object for this component. - * @returns {Date} - */ - get date() { - return this.getDate(); - } - - /** - * Return the raw value. - * - * @returns {Date} - */ - get validationValue() { - return this.dataValue; - } - - getValue() { - const result = super.getValue(); - return (!result) ? this.dataValue : result; - } - - /** - * Get the value at a specific index. - * - * @param index - * @returns {*} - */ - getValueAt(index) { - const date = this.date || this.emptyValue; - if (date) { - this.refs.input[index].value = date; - return this.refs.input[index].value; - } - else { - this.refs.input[index].value = ''; - return null; - } - } + validateRequired(setting, value) { + const { day, month, year } = this.parts; + if (this.dayRequired && !day) { + return false; + } + + if (this.monthRequired && !month) { + return false; + } - /** - * Get the input value of the date. - * - * @param value - * @return {null} - */ - getValueAsString(value) { - return this.getDate(value) || ''; - } + if (this.yearRequired && !year) { + return false; + } - focus(field) { - if (field && typeof field === 'string' && this.refs[field]) { - this.refs[field].focus(); + if (!boolValue(setting)) { + return true; + } + return !this.isEmpty(value); } - else if (this.dayFirst && this.showDay || !this.dayFirst && !this.showMonth && this.showDay) { - this.refs.day?.focus(); + + set disabled(disabled) { + super.disabled = disabled; + if (!this.refs.year || !this.refs.month || !this.refs.day) { + return; + } + if (disabled) { + this.refs.year.setAttribute('disabled', 'disabled'); + this.refs.month.setAttribute('disabled', 'disabled'); + this.refs.day.setAttribute('disabled', 'disabled'); + } else { + this.refs.year.removeAttribute('disabled'); + this.refs.month.removeAttribute('disabled'); + this.refs.day.removeAttribute('disabled'); + } } - else if (this.dayFirst && !this.showDay && this.showMonth || !this.dayFirst && this.showMonth) { - this.refs.month?.focus(); + + normalizeValue(value) { + if (!value || this.valueMask.test(value)) { + return value; + } + const dateParts = []; + const valueParts = value.split('/'); + const [DAY, MONTH, YEAR] = this.component.dayFirst + ? [0, 1, 2] + : [1, 0, 2]; + const defaultValue = this.component.defaultValue + ? this.component.defaultValue.split('/') + : ''; + + const getNextPart = (shouldTake, defaultValue) => + dateParts.push(shouldTake ? valueParts.shift() : defaultValue); + + if (this.dayFirst) { + getNextPart(this.showDay, defaultValue ? defaultValue[DAY] : '00'); + } + + getNextPart(this.showMonth, defaultValue ? defaultValue[MONTH] : '00'); + + if (!this.dayFirst) { + getNextPart(this.showDay, defaultValue ? defaultValue[DAY] : '00'); + } + + getNextPart(this.showYear, defaultValue ? defaultValue[YEAR] : '0000'); + + return dateParts.join('/'); } - else if (!this.showDay && !this.showDay && this.showYear) { - this.refs.year?.focus(); + + /** + * Set the value at a specific index. + * + * @param index + * @param value + */ + setValueAt(index, value) { + // temporary solution to avoid input reset + // on invalid date. + if (!value || value === 'Invalid date') { + return null; + } + const parts = value.split('/'); + let day; + if (this.component.dayFirst) { + day = parts.shift(); + } + const month = parts.shift(); + if (!this.component.dayFirst) { + day = parts.shift(); + } + const year = parts.shift(); + + if (this.refs.day && this.showDay) { + this.refs.day.value = day === '00' ? '' : parseInt(day, 10); + } + if (this.refs.month && this.showMonth) { + this.refs.month.value = month === '00' ? '' : parseInt(month, 10); + } + if (this.refs.year && this.showYear) { + this.refs.year.value = year === '0000' ? '' : parseInt(year, 10); + } + } + + getFieldValue(name) { + const parts = this.dataValue ? this.dataValue.split('/') : []; + let val = 0; + + switch (name) { + case 'month': + val = parts[this.dayFirst ? 1 : 0]; + break; + case 'day': + val = parts[this.dayFirst ? 0 : 1]; + break; + case 'year': + val = parts[2]; + break; + } + + val = parseInt(val, 10); + return !_.isNaN(val) && _.isNumber(val) ? val : 0; } - } - restoreCaretPosition() { - if (this.root?.currentSelection) { - const { selection, index } = this.root.currentSelection; - if (this.refs[index]) { - const input = this.refs[index]; - const isInputRangeSelectable = (i) => /text|search|password|tel|url/i.test(i?.type || ''); - if (isInputRangeSelectable(input)) { - input.setSelectionRange(...selection); - } - } - } - } - - isPartialDay(value) { - if (!value) { - return false; - } - const [DAY, MONTH, YEAR] = this.component.dayFirst ? [0, 1, 2] : [1, 0, 2]; - const values = value.split('/'); - return (values[DAY] === '00' || values[MONTH] === '00' || values[YEAR] === '0000'); - } - - getValidationFormat() { - return this.dayFirst ? 'DD-MM-YYYY' : 'MM-DD-YYYY'; - } + get parts() { + return { + day: this.getFieldValue('day'), + month: this.getFieldValue('month'), + year: this.getFieldValue('year'), + }; + } + + /** + * Get the format for the value string. + * @returns {string} + */ + get format() { + let format = ''; + if (this.component.dayFirst && this.showDay) { + format += 'D/'; + } + if (this.showMonth) { + format += 'M/'; + } + if (!this.component.dayFirst && this.showDay) { + format += 'D/'; + } + if (this.showYear) { + format += 'YYYY'; + return format; + } else { + // Trim off the "/" from the end of the format string. + return format.length + ? format.substring(0, format.length - 1) + : format; + } + } + + /** + * Return the date for this component. + * + * @param value + * @return {*} + */ + getDate(value) { + let defaults = [], + day, + month, + year; + // Map positions to identifiers to get default values for each part of day + const [DAY, MONTH, YEAR] = this.component.dayFirst + ? [0, 1, 2] + : [1, 0, 2]; + const defaultValue = value || this.component.defaultValue; + if (defaultValue) { + defaults = defaultValue.split('/').map((x) => parseInt(x, 10)); + } + + if (this.showDay && this.refs.day) { + day = parseInt(this.refs.day.value, 10); + } + if (day === undefined || _.isNaN(day)) { + day = defaults[DAY] && !_.isNaN(defaults[DAY]) ? defaults[DAY] : 0; + } + + if (this.showMonth && this.refs.month) { + // Months are 0 indexed. + month = parseInt(this.refs.month.value, 10); + } + if (month === undefined || _.isNaN(month)) { + month = + defaults[MONTH] && !_.isNaN(defaults[MONTH]) + ? defaults[MONTH] + : 0; + } + + if (this.showYear && this.refs.year) { + year = parseInt(this.refs.year.value); + } + if (year === undefined || _.isNaN(year)) { + year = + defaults[YEAR] && !_.isNaN(defaults[YEAR]) ? defaults[YEAR] : 0; + } + + let result; + if (!day && !month && !year) { + return null; + } + + // add trailing zeros if the data is showed + day = this.showDay ? day.toString().padStart(2, 0) : ''; + month = this.showMonth ? month.toString().padStart(2, 0) : ''; + year = this.showYear ? year.toString().padStart(4, 0) : ''; + + if (this.component.dayFirst) { + result = `${day}${ + (this.showDay && this.showMonth) || + (this.showDay && this.showYear) + ? '/' + : '' + }${month}${this.showMonth && this.showYear ? '/' : ''}${year}`; + } else { + result = `${month}${ + (this.showDay && this.showMonth) || + (this.showMonth && this.showYear) + ? '/' + : '' + }${day}${this.showDay && this.showYear ? '/' : ''}${year}`; + } + + return result; + } + + /** + * Return the date object for this component. + * @returns {Date} + */ + get date() { + return this.getDate(); + } + + /** + * Return the raw value. + * + * @returns {Date} + */ + get validationValue() { + return this.dataValue; + } + + getValue() { + const result = super.getValue(); + return !result ? this.dataValue : result; + } + + /** + * Get the value at a specific index. + * + * @param index + * @returns {*} + */ + getValueAt(index) { + const date = this.date || this.emptyValue; + if (date) { + this.refs.input[index].value = date; + return this.refs.input[index].value; + } else { + this.refs.input[index].value = ''; + return null; + } + } + + /** + * Get the input value of the date. + * + * @param value + * @return {null} + */ + getValueAsString(value) { + return this.getDate(value) || ''; + } + + focus(field) { + if (field && typeof field === 'string' && this.refs[field]) { + this.refs[field].focus(); + } else if ( + (this.dayFirst && this.showDay) || + (!this.dayFirst && !this.showMonth && this.showDay) + ) { + this.refs.day?.focus(); + } else if ( + (this.dayFirst && !this.showDay && this.showMonth) || + (!this.dayFirst && this.showMonth) + ) { + this.refs.month?.focus(); + } else if (!this.showDay && !this.showDay && this.showYear) { + this.refs.year?.focus(); + } + } + + restoreCaretPosition() { + if (this.root?.currentSelection) { + const { selection, index } = this.root.currentSelection; + if (this.refs[index]) { + const input = this.refs[index]; + const isInputRangeSelectable = (i) => + /text|search|password|tel|url/i.test(i?.type || ''); + if (isInputRangeSelectable(input)) { + input.setSelectionRange(...selection); + } + } + } + } + + isPartialDay(value) { + if (!value) { + return false; + } + const [DAY, MONTH, YEAR] = this.component.dayFirst + ? [0, 1, 2] + : [1, 0, 2]; + const values = value.split('/'); + return ( + values[DAY] === '00' || + values[MONTH] === '00' || + values[YEAR] === '0000' + ); + } + + getValidationFormat() { + return this.dayFirst ? 'DD-MM-YYYY' : 'MM-DD-YYYY'; + } } diff --git a/src/components/day/Day.unit.js b/src/components/day/Day.unit.js index e198fed016..f5ac58c514 100644 --- a/src/components/day/Day.unit.js +++ b/src/components/day/Day.unit.js @@ -4,269 +4,290 @@ import assert from 'power-assert'; import Harness from '../../../test/harness'; import DayComponent from './Day'; -import { - comp1, - comp2, - comp3, - comp4, - comp5, - comp6, -} from './fixtures'; +import { comp1, comp2, comp3, comp4, comp5, comp6 } from './fixtures'; import PanelComponent from '../panel/Panel'; -describe('Day Component', function() { - it('Should build a day component', function() { - return Harness.testCreate(DayComponent, comp1).then((component) => { - Harness.testElements(component, 'input[type="number"]', 2); - Harness.testElements(component, 'select', 1); +describe('Day Component', function () { + it('Should build a day component', function () { + return Harness.testCreate(DayComponent, comp1).then((component) => { + Harness.testElements(component, 'input[type="number"]', 2); + Harness.testElements(component, 'select', 1); + }); }); - }); - - it('Should change the max day when the month changes', function(done) { - Harness.testCreate(DayComponent, comp1).then((component) => { - Harness.testElements(component, 'option', 13); - assert(!!component.refs.year, 'There should be a year'); - // Set the year to a non-leap year. - component.refs.year.value = 2017; - component.setSelectValue(component.refs.month, '1'); - component.refs.month.dispatchEvent(new Event('input')); - assert.equal(component.refs.day.max, '31'); - - component.setSelectValue(component.refs.month, '2'); - component.refs.month.dispatchEvent(new Event('input')); - assert.equal(component.refs.day.max, '28'); - - component.setSelectValue(component.refs.month, '3'); - component.refs.month.dispatchEvent(new Event('input')); - assert.equal(component.refs.day.max, '31'); - - component.setSelectValue(component.refs.month, '4'); - component.refs.month.dispatchEvent(new Event('input')); - assert.equal(component.refs.day.max, '30'); - - // Set to a leap year. - component.refs.year.value = 2020; - component.setSelectValue(component.refs.month, '1'); - component.refs.month.dispatchEvent(new Event('input')); - assert.equal(component.refs.day.max, '31'); - - component.setSelectValue(component.refs.month, '2'); - component.refs.month.dispatchEvent(new Event('input')); - assert.equal(component.refs.day.max, '29'); - - component.setSelectValue(component.refs.month, '3'); - component.refs.month.dispatchEvent(new Event('input')); - assert.equal(component.refs.day.max, '31'); - - component.setSelectValue(component.refs.month, '4'); - component.refs.month.dispatchEvent(new Event('input')); - assert.equal(component.refs.day.max, '30'); - - done(); - }); - }); - - it('Should put the month select first', function(done) { - Harness.testCreate(DayComponent, comp1).then((component) => { - const inputs = Harness.testElements(component, '.form-control', 4); - assert.equal(inputs[0].id, `${component.component.key}-month`); - assert.equal(inputs[1].id, `${component.component.key}-day`); - assert.equal(inputs[2].id, `${component.component.key}-year`); - component.setValue('03/20/2017'); - assert.equal(component.getValue(), '03/20/2017'); - done(); - }); - }); - - it('Should put the day select first on configuration', function(done) { - comp1.dayFirst = true; - Harness.testCreate(DayComponent, comp1).then((component) => { - const inputs = Harness.testElements(component, '.form-control', 4); - assert.equal(inputs[0].id, `${component.component.key}-day`); - assert.equal(inputs[1].id, `${component.component.key}-month`); - assert.equal(inputs[2].id, `${component.component.key}-year`); - component.setValue('20/03/2017'); - assert.equal(component.getValue(), '20/03/2017'); - done(); - }); - }); - - it('Should not allow invalid days', function(done) { - comp1.dayFirst = false; - Harness.testCreate(DayComponent, comp1).then((component) => { - component.on('componentError', (err) => { - assert.equal(err.message, 'Date is not a valid day.'); - assert.equal(err.component.key, 'date'); - done(); - }); - - component.on('componentChange', () => { - component.checkValidity(); - }); - - component.setValue('3/40/2017'); - }); - }); - it('Should ignore invalid months and use zeros as default', function(done) { - comp1.dayFirst = false; + it('Should change the max day when the month changes', function (done) { + Harness.testCreate(DayComponent, comp1).then((component) => { + Harness.testElements(component, 'option', 13); + assert(!!component.refs.year, 'There should be a year'); + // Set the year to a non-leap year. + component.refs.year.value = 2017; + component.setSelectValue(component.refs.month, '1'); + component.refs.month.dispatchEvent(new Event('input')); + assert.equal(component.refs.day.max, '31'); + + component.setSelectValue(component.refs.month, '2'); + component.refs.month.dispatchEvent(new Event('input')); + assert.equal(component.refs.day.max, '28'); + + component.setSelectValue(component.refs.month, '3'); + component.refs.month.dispatchEvent(new Event('input')); + assert.equal(component.refs.day.max, '31'); + + component.setSelectValue(component.refs.month, '4'); + component.refs.month.dispatchEvent(new Event('input')); + assert.equal(component.refs.day.max, '30'); + + // Set to a leap year. + component.refs.year.value = 2020; + component.setSelectValue(component.refs.month, '1'); + component.refs.month.dispatchEvent(new Event('input')); + assert.equal(component.refs.day.max, '31'); + + component.setSelectValue(component.refs.month, '2'); + component.refs.month.dispatchEvent(new Event('input')); + assert.equal(component.refs.day.max, '29'); + + component.setSelectValue(component.refs.month, '3'); + component.refs.month.dispatchEvent(new Event('input')); + assert.equal(component.refs.day.max, '31'); + + component.setSelectValue(component.refs.month, '4'); + component.refs.month.dispatchEvent(new Event('input')); + assert.equal(component.refs.day.max, '30'); - Harness.testCreate(DayComponent, comp1).then((component) => { - component.setValue('15/20/2017'); - assert.equal(component.getValue(), '00/20/2017'); - done(); - }); - }); - - it('Should keep day value when switching months', function(done) { - Harness.testCreate(DayComponent, comp1).then((component) => { - component.setValue('01/05/2018'); - assert.equal(component.getValue(), '01/05/2018'); - component.on('componentChange', () => { - assert.equal(component.getValue(), '02/05/2018'); - done(); - }); - component.refs.month.value = 2; - component.refs.month.dispatchEvent(new Event('input')); + done(); + }); }); - }); - - it('Should adjust day value when day is great then maxDay of month', function(done) { - Harness.testCreate(DayComponent, comp1).then((component) => { - component.setValue('01/31/2018'); - assert.equal(component.getValue(), '01/31/2018'); - component.on('componentChange', () => { - assert.equal(component.getValue(), '02/28/2018'); - done(); - }); - component.refs.month.value = 2; - component.refs.month.dispatchEvent(new Event('input')); + + it('Should put the month select first', function (done) { + Harness.testCreate(DayComponent, comp1).then((component) => { + const inputs = Harness.testElements(component, '.form-control', 4); + assert.equal(inputs[0].id, `${component.component.key}-month`); + assert.equal(inputs[1].id, `${component.component.key}-day`); + assert.equal(inputs[2].id, `${component.component.key}-year`); + component.setValue('03/20/2017'); + assert.equal(component.getValue(), '03/20/2017'); + done(); + }); }); - }); - - it('Should validate required fields', function(done) { - Harness.testCreate(DayComponent, comp2).then((component) => { - component.pristine = false; - const valid = () => component.checkValidity(component.data, true); - const tests = { - '12/18/2018': true, - '12/00/0000': false, - '00/18/0000': false, - '00/00/2018': false, - '01/05/2018': true, - }; - - for (const v in tests) { - component.setValue(v); - assert.equal(valid(), tests[v]); - } - - done(); + + it('Should put the day select first on configuration', function (done) { + comp1.dayFirst = true; + Harness.testCreate(DayComponent, comp1).then((component) => { + const inputs = Harness.testElements(component, '.form-control', 4); + assert.equal(inputs[0].id, `${component.component.key}-day`); + assert.equal(inputs[1].id, `${component.component.key}-month`); + assert.equal(inputs[2].id, `${component.component.key}-year`); + component.setValue('20/03/2017'); + assert.equal(component.getValue(), '20/03/2017'); + done(); + }); }); - }); - it('Should properly validate min-max dates when dayFirst is checked', function(done) { - Harness.testCreate(DayComponent, comp3).then((component) => { - component.setValue('01/02/2020'); - assert(!component.checkValidity(component.data, true), 'Component should not be valid'); + it('Should not allow invalid days', function (done) { + comp1.dayFirst = false; + Harness.testCreate(DayComponent, comp1).then((component) => { + component.on('componentError', (err) => { + assert.equal(err.message, 'Date is not a valid day.'); + assert.equal(err.component.key, 'date'); + done(); + }); + + component.on('componentChange', () => { + component.checkValidity(); + }); + + component.setValue('3/40/2017'); + }); + }); - component.setValue('04/01/2021'); - assert(!component.checkValidity(component.data, true), 'Component should not be valid'); + it('Should ignore invalid months and use zeros as default', function (done) { + comp1.dayFirst = false; - component.setValue('03/01/2021'); - assert(component.checkValidity(component.data, true), 'Component should be valid'); + Harness.testCreate(DayComponent, comp1).then((component) => { + component.setValue('15/20/2017'); + assert.equal(component.getValue(), '00/20/2017'); + done(); + }); + }); - component.setValue('01/03/2020'); - assert(component.checkValidity(component.data, true), 'Component should be valid'); - done(); + it('Should keep day value when switching months', function (done) { + Harness.testCreate(DayComponent, comp1).then((component) => { + component.setValue('01/05/2018'); + assert.equal(component.getValue(), '01/05/2018'); + component.on('componentChange', () => { + assert.equal(component.getValue(), '02/05/2018'); + done(); + }); + component.refs.month.value = 2; + component.refs.month.dispatchEvent(new Event('input')); + }); }); - }); - it('Should disable day component if parent component is disabled', function(done) { - Harness.testCreate(PanelComponent, comp4).then((component) => { - Harness.testElements(component, '[disabled]', 4); - done(); + it('Should adjust day value when day is great then maxDay of month', function (done) { + Harness.testCreate(DayComponent, comp1).then((component) => { + component.setValue('01/31/2018'); + assert.equal(component.getValue(), '01/31/2018'); + component.on('componentChange', () => { + assert.equal(component.getValue(), '02/28/2018'); + done(); + }); + component.refs.month.value = 2; + component.refs.month.dispatchEvent(new Event('input')); + }); }); - }); - - it('Should use the default day value if the day field is hidden', function(done) { - comp1.dayFirst = false; - comp1.defaultValue = '00/01/0000'; - comp1.fields.day.hide = true; - Harness.testCreate(DayComponent, comp1).then((component) => { - component.setValue('12/2023'); - assert.equal(component.data.date, '12/01/2023'); - done(); + + it('Should validate required fields', function (done) { + Harness.testCreate(DayComponent, comp2).then((component) => { + component.pristine = false; + const valid = () => component.checkValidity(component.data, true); + const tests = { + '12/18/2018': true, + '12/00/0000': false, + '00/18/0000': false, + '00/00/2018': false, + '01/05/2018': true, + }; + + for (const v in tests) { + component.setValue(v); + assert.equal(valid(), tests[v]); + } + + done(); + }); }); - comp1.fields.day.hide = false; - }); - - it('Should use the default month value if the month field is hidden', function(done) { - comp1.defaultValue = '03/00/0000'; - comp1.fields.month.hide = true; - Harness.testCreate(DayComponent, comp1).then((component) => { - component.setValue('12/2023'); - assert.equal(component.data.date, '03/12/2023'); - done(); + + it('Should properly validate min-max dates when dayFirst is checked', function (done) { + Harness.testCreate(DayComponent, comp3).then((component) => { + component.setValue('01/02/2020'); + assert( + !component.checkValidity(component.data, true), + 'Component should not be valid', + ); + + component.setValue('04/01/2021'); + assert( + !component.checkValidity(component.data, true), + 'Component should not be valid', + ); + + component.setValue('03/01/2021'); + assert( + component.checkValidity(component.data, true), + 'Component should be valid', + ); + + component.setValue('01/03/2020'); + assert( + component.checkValidity(component.data, true), + 'Component should be valid', + ); + done(); + }); }); - comp1.fields.month.hide = false; - }); - - it('Should use the default year value if the year field is hidden', function(done) { - comp1.defaultValue = '00/00/2023'; - comp1.fields.year.hide = true; - Harness.testCreate(DayComponent, comp1).then((component) => { - component.setValue('12/21'); - assert.equal(component.data.date, '12/21/2023'); - done(); + + it('Should disable day component if parent component is disabled', function (done) { + Harness.testCreate(PanelComponent, comp4).then((component) => { + Harness.testElements(component, '[disabled]', 4); + done(); + }); }); - comp1.fields.year.hide = false; - }); - it('OnBlur validation should work properly with Day component', function(done) { - const element = document.createElement('div'); + it('Should use the default day value if the day field is hidden', function (done) { + comp1.dayFirst = false; + comp1.defaultValue = '00/01/0000'; + comp1.fields.day.hide = true; + Harness.testCreate(DayComponent, comp1).then((component) => { + component.setValue('12/2023'); + assert.equal(component.data.date, '12/01/2023'); + done(); + }); + comp1.fields.day.hide = false; + }); - Formio.createForm(element, comp5).then(form => { - const dayComponent = form.components[0]; - dayComponent.setValue('03/12/2023'); + it('Should use the default month value if the month field is hidden', function (done) { + comp1.defaultValue = '03/00/0000'; + comp1.fields.month.hide = true; + Harness.testCreate(DayComponent, comp1).then((component) => { + component.setValue('12/2023'); + assert.equal(component.data.date, '03/12/2023'); + done(); + }); + comp1.fields.month.hide = false; + }); - setTimeout(() => { - dayComponent.refs.day.focus(); - dayComponent.refs.day.value = ''; - dayComponent.refs.day.dispatchEvent(new Event('input')); + it('Should use the default year value if the year field is hidden', function (done) { + comp1.defaultValue = '00/00/2023'; + comp1.fields.year.hide = true; + Harness.testCreate(DayComponent, comp1).then((component) => { + component.setValue('12/21'); + assert.equal(component.data.date, '12/21/2023'); + done(); + }); + comp1.fields.year.hide = false; + }); - setTimeout(() => { - assert(!dayComponent.error, 'Day should be valid while changing'); - dayComponent.refs.day.dispatchEvent(new Event('blur')); + it('OnBlur validation should work properly with Day component', function (done) { + const element = document.createElement('div'); + + Formio.createForm(element, comp5) + .then((form) => { + const dayComponent = form.components[0]; + dayComponent.setValue('03/12/2023'); + + setTimeout(() => { + dayComponent.refs.day.focus(); + dayComponent.refs.day.value = ''; + dayComponent.refs.day.dispatchEvent(new Event('input')); + + setTimeout(() => { + assert( + !dayComponent.error, + 'Day should be valid while changing', + ); + dayComponent.refs.day.dispatchEvent(new Event('blur')); + + setTimeout(() => { + assert( + dayComponent.error, + 'Should set error after Day component was blurred', + ); + done(); + }, 200); + }, 200); + }, 200); + }) + .catch(done); + }); - setTimeout(() => { - assert(dayComponent.error, 'Should set error after Day component was blurred'); - done(); - }, 200); - }, 200); - }, 200); - }).catch(done); - }); - - it('Should restore focus after redraw', function(done) { - const element = document.createElement('div'); - document.body.appendChild(element); - Formio.createForm(element, comp6).then(form => { - const textField = form.getComponent(['textField']); - textField.setValue('test'); - - setTimeout(() => { - const day = form.getComponent(['day']); - document.querySelector('select.form-control').focus(); - day.refs.month.value = 2; - day.refs.month.dispatchEvent(new Event('input')); - - setTimeout(() => { - console.log(global.document.activeElement, day.refs.month); - assert(global.document.activeElement === day.refs.month, 'Should keep focus on the year select'); - done(); - }, 200); - }, 500); - }).catch(done); - }); + it('Should restore focus after redraw', function (done) { + const element = document.createElement('div'); + document.body.appendChild(element); + Formio.createForm(element, comp6) + .then((form) => { + const textField = form.getComponent(['textField']); + textField.setValue('test'); + + setTimeout(() => { + const day = form.getComponent(['day']); + document.querySelector('select.form-control').focus(); + day.refs.month.value = 2; + day.refs.month.dispatchEvent(new Event('input')); + + setTimeout(() => { + console.log( + global.document.activeElement, + day.refs.month, + ); + assert( + global.document.activeElement === day.refs.month, + 'Should keep focus on the year select', + ); + done(); + }, 200); + }, 500); + }) + .catch(done); + }); }); diff --git a/src/components/day/editForm/Day.edit.data.js b/src/components/day/editForm/Day.edit.data.js index c58832a68b..86bb5e76be 100644 --- a/src/components/day/editForm/Day.edit.data.js +++ b/src/components/day/editForm/Day.edit.data.js @@ -1,6 +1,6 @@ export default [ - { - key: 'multiple', - ignore: true, - }, + { + key: 'multiple', + ignore: true, + }, ]; diff --git a/src/components/day/editForm/Day.edit.day.js b/src/components/day/editForm/Day.edit.day.js index 22525ef293..99faa180d1 100644 --- a/src/components/day/editForm/Day.edit.day.js +++ b/src/components/day/editForm/Day.edit.day.js @@ -1,46 +1,47 @@ export default [ - { - wieght: 200, - type: 'select', - datasrc: 'values', - key: 'fields.day.type', - label: 'Type', - data: { - values: [ - { - label: 'Number', - value: 'number' + { + wieght: 200, + type: 'select', + datasrc: 'values', + key: 'fields.day.type', + label: 'Type', + data: { + values: [ + { + label: 'Number', + value: 'number', + }, + { + label: 'Select', + value: 'select', + }, + ], }, - { - label: 'Select', - value: 'select' - }, - ] - } - }, - { - weight: 210, - type: 'textfield', - input: true, - key: 'fields.day.placeholder', - label: 'Placeholder', - placeholder: 'Day Placeholder', - tooltip: 'The placeholder text that will appear when Day field is empty.' - }, - { - weight: 215, - type: 'checkbox', - label: 'Hidden', - tooltip: 'Hide the Day part of the component.', - key: 'fields.day.hide', - input: true - }, - { - weight: 214, - type: 'checkbox', - label: 'Day First', - tooltip: 'Display the Day field before the Month field.', - key: 'dayFirst', - input: true - }, + }, + { + weight: 210, + type: 'textfield', + input: true, + key: 'fields.day.placeholder', + label: 'Placeholder', + placeholder: 'Day Placeholder', + tooltip: + 'The placeholder text that will appear when Day field is empty.', + }, + { + weight: 215, + type: 'checkbox', + label: 'Hidden', + tooltip: 'Hide the Day part of the component.', + key: 'fields.day.hide', + input: true, + }, + { + weight: 214, + type: 'checkbox', + label: 'Day First', + tooltip: 'Display the Day field before the Month field.', + key: 'dayFirst', + input: true, + }, ]; diff --git a/src/components/day/editForm/Day.edit.display.js b/src/components/day/editForm/Day.edit.display.js index 6b4d10a6b9..2a8a88899c 100644 --- a/src/components/day/editForm/Day.edit.display.js +++ b/src/components/day/editForm/Day.edit.display.js @@ -1,44 +1,45 @@ export default [ - { - key: 'labelPosition', - ignore: true - }, - { - weight: 15, - type: 'checkbox', - label: 'Hide Input Labels', - tooltip: 'Hide the labels of component inputs. This allows you to show the labels in the form builder, but not when it is rendered.', - key: 'hideInputLabels', - input: true - }, - { - type: 'select', - input: true, - key: 'inputsLabelPosition', - label: 'Inputs Label Position', - tooltip: 'Position for the labels for inputs for this field.', - weight: 40, - defaultValue: 'top', - dataSrc: 'values', - data: { - values: [ - { label: 'Top', value: 'top' }, - { label: 'Left', value: 'left' }, - { label: 'Right', value: 'right' }, - { label: 'Bottom', value: 'bottom' } - ] - } - }, - { - key: 'placeholder', - ignore: true - }, - { - weight: 213, - type: 'checkbox', - label: 'Use Locale Settings', - tooltip: 'Use locale settings to display day.', - key: 'useLocaleSettings', - input: true - }, + { + key: 'labelPosition', + ignore: true, + }, + { + weight: 15, + type: 'checkbox', + label: 'Hide Input Labels', + tooltip: + 'Hide the labels of component inputs. This allows you to show the labels in the form builder, but not when it is rendered.', + key: 'hideInputLabels', + input: true, + }, + { + type: 'select', + input: true, + key: 'inputsLabelPosition', + label: 'Inputs Label Position', + tooltip: 'Position for the labels for inputs for this field.', + weight: 40, + defaultValue: 'top', + dataSrc: 'values', + data: { + values: [ + { label: 'Top', value: 'top' }, + { label: 'Left', value: 'left' }, + { label: 'Right', value: 'right' }, + { label: 'Bottom', value: 'bottom' }, + ], + }, + }, + { + key: 'placeholder', + ignore: true, + }, + { + weight: 213, + type: 'checkbox', + label: 'Use Locale Settings', + tooltip: 'Use locale settings to display day.', + key: 'useLocaleSettings', + input: true, + }, ]; diff --git a/src/components/day/editForm/Day.edit.month.js b/src/components/day/editForm/Day.edit.month.js index 5d8f609b78..bbea3cc687 100644 --- a/src/components/day/editForm/Day.edit.month.js +++ b/src/components/day/editForm/Day.edit.month.js @@ -1,38 +1,39 @@ export default [ - { - wieght: 200, - type: 'select', - datasrc: 'values', - key: 'fields.month.type', - label: 'Type of input', - data: { - values: [ - { - label: 'Number', - value: 'number' + { + wieght: 200, + type: 'select', + datasrc: 'values', + key: 'fields.month.type', + label: 'Type of input', + data: { + values: [ + { + label: 'Number', + value: 'number', + }, + { + label: 'Select', + value: 'select', + }, + ], }, - { - label: 'Select', - value: 'select' - }, - ] - } - }, - { - weight: 210, - type: 'textfield', - input: true, - key: 'fields.month.placeholder', - label: 'Placeholder', - placeholder: 'Month Placeholder', - tooltip: 'The placeholder text that will appear when Month field is empty.' - }, - { - weight: 215, - type: 'checkbox', - label: 'Hidden', - tooltip: 'Hide the Month part of the component.', - key: 'fields.month.hide', - input: true - }, + }, + { + weight: 210, + type: 'textfield', + input: true, + key: 'fields.month.placeholder', + label: 'Placeholder', + placeholder: 'Month Placeholder', + tooltip: + 'The placeholder text that will appear when Month field is empty.', + }, + { + weight: 215, + type: 'checkbox', + label: 'Hidden', + tooltip: 'Hide the Month part of the component.', + key: 'fields.month.hide', + input: true, + }, ]; diff --git a/src/components/day/editForm/Day.edit.validation.js b/src/components/day/editForm/Day.edit.validation.js index 81537a4b96..cb5db891a9 100644 --- a/src/components/day/editForm/Day.edit.validation.js +++ b/src/components/day/editForm/Day.edit.validation.js @@ -1,52 +1,57 @@ export default [ - { - key: 'validate.required', - ignore: true - }, - { - key: 'validate.unique', - ignore: true - }, - { - weight: 0, - type: 'checkbox', - label: 'Require Day', - tooltip: 'A required field must be filled in before the form can be submitted.', - key: 'fields.day.required', - input: true - }, - { - weight: 10, - type: 'checkbox', - label: 'Require Month', - tooltip: 'A required field must be filled in before the form can be submitted.', - key: 'fields.month.required', - input: true - }, - { - weight: 20, - type: 'checkbox', - label: 'Require Year', - tooltip: 'A required field must be filled in before the form can be submitted.', - key: 'fields.year.required', - input: true - }, - { - weight: 40, - type: 'textfield', - label: 'Minimum Day', - placeholder: 'yyyy-MM-dd', - tooltip: 'A minimum date that can be set. You can also use Moment.js functions. For example: \n \n moment().subtract(10, \'days\')', - key: 'minDate', - input: true, - }, - { - weight: 30, - type: 'textfield', - label: 'Maximum Day', - placeholder: 'yyyy-MM-dd', - tooltip: 'A maximum day that can be set. You can also use Moment.js functions. For example: \n \n moment().add(10, \'days\')', - key: 'maxDate', - input: true, - }, + { + key: 'validate.required', + ignore: true, + }, + { + key: 'validate.unique', + ignore: true, + }, + { + weight: 0, + type: 'checkbox', + label: 'Require Day', + tooltip: + 'A required field must be filled in before the form can be submitted.', + key: 'fields.day.required', + input: true, + }, + { + weight: 10, + type: 'checkbox', + label: 'Require Month', + tooltip: + 'A required field must be filled in before the form can be submitted.', + key: 'fields.month.required', + input: true, + }, + { + weight: 20, + type: 'checkbox', + label: 'Require Year', + tooltip: + 'A required field must be filled in before the form can be submitted.', + key: 'fields.year.required', + input: true, + }, + { + weight: 40, + type: 'textfield', + label: 'Minimum Day', + placeholder: 'yyyy-MM-dd', + tooltip: + "A minimum date that can be set. You can also use Moment.js functions. For example: \n \n moment().subtract(10, 'days')", + key: 'minDate', + input: true, + }, + { + weight: 30, + type: 'textfield', + label: 'Maximum Day', + placeholder: 'yyyy-MM-dd', + tooltip: + "A maximum day that can be set. You can also use Moment.js functions. For example: \n \n moment().add(10, 'days')", + key: 'maxDate', + input: true, + }, ]; diff --git a/src/components/day/editForm/Day.edit.year.js b/src/components/day/editForm/Day.edit.year.js index e3ca7ff5ec..8648050cd6 100644 --- a/src/components/day/editForm/Day.edit.year.js +++ b/src/components/day/editForm/Day.edit.year.js @@ -1,56 +1,57 @@ export default [ - { - wieght: 200, - type: 'select', - datasrc: 'values', - key: 'fields.year.type', - label: 'Type of input', - data: { - values: [ - { - label: 'Number', - value: 'number' + { + wieght: 200, + type: 'select', + datasrc: 'values', + key: 'fields.year.type', + label: 'Type of input', + data: { + values: [ + { + label: 'Number', + value: 'number', + }, + { + label: 'Select', + value: 'select', + }, + ], }, - { - label: 'Select', - value: 'select' - }, - ] - } - }, - { - weight: 203, - type: 'number', - input: true, - key: 'fields.year.minYear', - label: 'Minimum Year', - placeholder: '1900', - tooltip: 'The minimum year that can be entered.' - }, - { - weight: 204, - type: 'number', - input: true, - key: 'fields.year.maxYear', - label: 'Maximum Year', - placeholder: '2030', - tooltip: 'The maximum year that can be entered.' - }, - { - weight: 210, - type: 'textfield', - input: true, - key: 'fields.year.placeholder', - label: 'Placeholder', - placeholder: 'Year Placeholder', - tooltip: 'The placeholder text that will appear when Year field is empty.' - }, - { - weight: 215, - type: 'checkbox', - label: 'Hidden', - tooltip: 'Hide the Year part of the component.', - key: 'fields.year.hide', - input: true - }, + }, + { + weight: 203, + type: 'number', + input: true, + key: 'fields.year.minYear', + label: 'Minimum Year', + placeholder: '1900', + tooltip: 'The minimum year that can be entered.', + }, + { + weight: 204, + type: 'number', + input: true, + key: 'fields.year.maxYear', + label: 'Maximum Year', + placeholder: '2030', + tooltip: 'The maximum year that can be entered.', + }, + { + weight: 210, + type: 'textfield', + input: true, + key: 'fields.year.placeholder', + label: 'Placeholder', + placeholder: 'Year Placeholder', + tooltip: + 'The placeholder text that will appear when Year field is empty.', + }, + { + weight: 215, + type: 'checkbox', + label: 'Hidden', + tooltip: 'Hide the Year part of the component.', + key: 'fields.year.hide', + input: true, + }, ]; diff --git a/src/components/day/fixtures/comp1.js b/src/components/day/fixtures/comp1.js index aae7504006..f2d0be042e 100644 --- a/src/components/day/fixtures/comp1.js +++ b/src/components/day/fixtures/comp1.js @@ -1,39 +1,37 @@ export default { - 'conditional': { - 'eq': '', - 'when': null, - 'show': '' - }, - 'tags': [ - - ], - 'type': 'day', - 'validate': { - 'custom': '' - }, - 'clearOnHide': true, - 'persistent': true, - 'protected': false, - 'dayFirst': false, - 'fields': { - 'year': { - 'required': false, - 'placeholder': '', - 'type': 'number' + conditional: { + eq: '', + when: null, + show: '', }, - 'month': { - 'required': false, - 'placeholder': '', - 'type': 'select' + tags: [], + type: 'day', + validate: { + custom: '', }, - 'day': { - 'required': false, - 'placeholder': '', - 'type': 'number' - } - }, - 'key': 'date', - 'label': 'Date', - 'tableView': true, - 'input': true + clearOnHide: true, + persistent: true, + protected: false, + dayFirst: false, + fields: { + year: { + required: false, + placeholder: '', + type: 'number', + }, + month: { + required: false, + placeholder: '', + type: 'select', + }, + day: { + required: false, + placeholder: '', + type: 'number', + }, + }, + key: 'date', + label: 'Date', + tableView: true, + input: true, }; diff --git a/src/components/day/fixtures/comp2.js b/src/components/day/fixtures/comp2.js index c373bf846c..9c34dffc94 100644 --- a/src/components/day/fixtures/comp2.js +++ b/src/components/day/fixtures/comp2.js @@ -1,39 +1,37 @@ export default { - 'conditional': { - 'eq': '', - 'when': null, - 'show': '' - }, - 'tags': [ - - ], - 'type': 'day', - 'validate': { - 'custom': '' - }, - 'clearOnHide': true, - 'persistent': true, - 'protected': false, - 'dayFirst': false, - 'fields': { - 'year': { - 'required': true, - 'placeholder': '', - 'type': 'number' + conditional: { + eq: '', + when: null, + show: '', }, - 'month': { - 'required': true, - 'placeholder': '', - 'type': 'select' + tags: [], + type: 'day', + validate: { + custom: '', }, - 'day': { - 'required': true, - 'placeholder': '', - 'type': 'number' - } - }, - 'key': 'date', - 'label': 'Date', - 'tableView': true, - 'input': true + clearOnHide: true, + persistent: true, + protected: false, + dayFirst: false, + fields: { + year: { + required: true, + placeholder: '', + type: 'number', + }, + month: { + required: true, + placeholder: '', + type: 'select', + }, + day: { + required: true, + placeholder: '', + type: 'number', + }, + }, + key: 'date', + label: 'Date', + tableView: true, + input: true, }; diff --git a/src/components/day/fixtures/comp3.js b/src/components/day/fixtures/comp3.js index cf33b96ec8..a5ce794913 100644 --- a/src/components/day/fixtures/comp3.js +++ b/src/components/day/fixtures/comp3.js @@ -1,41 +1,39 @@ export default { - 'conditional': { - 'eq': '', - 'when': null, - 'show': '' - }, - 'tags': [ - - ], - 'type': 'day', - 'validate': { - 'custom': '' - }, - 'minDate': '2020-03-01', - 'maxDate': '2021-01-03', - 'clearOnHide': true, - 'persistent': true, - 'protected': false, - 'dayFirst': true, - 'fields': { - 'year': { - 'required': false, - 'placeholder': '', - 'type': 'number' + conditional: { + eq: '', + when: null, + show: '', }, - 'month': { - 'required': false, - 'placeholder': '', - 'type': 'select' + tags: [], + type: 'day', + validate: { + custom: '', }, - 'day': { - 'required': false, - 'placeholder': '', - 'type': 'number' - } - }, - 'key': 'date', - 'label': 'Date', - 'tableView': true, - 'input': true + minDate: '2020-03-01', + maxDate: '2021-01-03', + clearOnHide: true, + persistent: true, + protected: false, + dayFirst: true, + fields: { + year: { + required: false, + placeholder: '', + type: 'number', + }, + month: { + required: false, + placeholder: '', + type: 'select', + }, + day: { + required: false, + placeholder: '', + type: 'number', + }, + }, + key: 'date', + label: 'Date', + tableView: true, + input: true, }; diff --git a/src/components/day/fixtures/comp4.js b/src/components/day/fixtures/comp4.js index 4f26ac4489..070bc03bd7 100644 --- a/src/components/day/fixtures/comp4.js +++ b/src/components/day/fixtures/comp4.js @@ -1,58 +1,54 @@ export default { - 'conditional': { - 'eq': '', - 'when': null, - 'show': '' - }, - 'tags': [ - - ], - 'type': 'panel', - 'components': [ - { - 'conditional': { - 'eq': '', - 'when': null, - 'show': '' - }, - 'tags': [ - - ], - 'type': 'day', - 'validate': { - 'custom': '' - }, - 'clearOnHide': true, - 'persistent': true, - 'protected': false, - 'dayFirst': false, - 'fields': { - 'year': { - 'required': false, - 'placeholder': '', - 'type': 'number' + conditional: { + eq: '', + when: null, + show: '', + }, + tags: [], + type: 'panel', + components: [ + { + conditional: { + eq: '', + when: null, + show: '', + }, + tags: [], + type: 'day', + validate: { + custom: '', + }, + clearOnHide: true, + persistent: true, + protected: false, + dayFirst: false, + fields: { + year: { + required: false, + placeholder: '', + type: 'number', + }, + month: { + required: false, + placeholder: '', + type: 'select', + }, + day: { + required: false, + placeholder: '', + type: 'number', + }, + }, + key: 'date', + label: 'Date', + tableView: true, + input: true, }, - 'month': { - 'required': false, - 'placeholder': '', - 'type': 'select' - }, - 'day': { - 'required': false, - 'placeholder': '', - 'type': 'number' - } - }, - 'key': 'date', - 'label': 'Date', - 'tableView': true, - 'input': true - } - ], - 'nextPage': null, - 'theme': 'default', - 'title': 'User Information', - 'input': false, - 'disabled': true, - 'key': 'panel1' + ], + nextPage: null, + theme: 'default', + title: 'User Information', + input: false, + disabled: true, + key: 'panel1', }; diff --git a/src/components/day/fixtures/comp5.js b/src/components/day/fixtures/comp5.js index 882efc18f7..6fabeed257 100644 --- a/src/components/day/fixtures/comp5.js +++ b/src/components/day/fixtures/comp5.js @@ -1,30 +1,30 @@ export default { - type: 'form', - display: 'form', - components: [ - { - label: 'Day', - hideInputLabels: false, - inputsLabelPosition: 'top', - useLocaleSettings: false, - tableView: false, - fields: { - day: { - hide: false, - required: true + type: 'form', + display: 'form', + components: [ + { + label: 'Day', + hideInputLabels: false, + inputsLabelPosition: 'top', + useLocaleSettings: false, + tableView: false, + fields: { + day: { + hide: false, + required: true, + }, + month: { + hide: false, + }, + year: { + hide: false, + }, + }, + validateOn: 'blur', + key: 'day', + type: 'day', + input: true, + defaultValue: '00/00/0000', }, - month: { - hide: false - }, - year: { - hide: false - } - }, - validateOn: 'blur', - key: 'day', - type: 'day', - input: true, - defaultValue: '00/00/0000' - }, - ] + ], }; diff --git a/src/components/day/fixtures/comp6.js b/src/components/day/fixtures/comp6.js index 95bc3dd950..e8ac38f112 100644 --- a/src/components/day/fixtures/comp6.js +++ b/src/components/day/fixtures/comp6.js @@ -1,74 +1,74 @@ export default { - type: 'form', - display: 'form', - components: [ - { - label: 'Text Field', - applyMaskOn: 'change', - tableView: true, - key: 'textField', - type: 'textfield', - input: true, - }, - { - label: 'Day', - hideInputLabels: false, - inputsLabelPosition: 'top', - useLocaleSettings: false, - tableView: false, - fields: { - day: { - hide: true, - }, - month: { - hide: false, - }, - year: { - hide: false, + type: 'form', + display: 'form', + components: [ + { + label: 'Text Field', + applyMaskOn: 'change', + tableView: true, + key: 'textField', + type: 'textfield', + input: true, }, - }, - defaultValue: '00/00/0000', - key: 'day', - logic: [ { - name: 'Disable when Test is empty', - trigger: { - type: 'simple', - simple: { - show: true, - conjunction: 'all', - conditions: [ - { - component: 'textField', - operator: 'isEmpty', + label: 'Day', + hideInputLabels: false, + inputsLabelPosition: 'top', + useLocaleSettings: false, + tableView: false, + fields: { + day: { + hide: true, + }, + month: { + hide: false, + }, + year: { + hide: false, }, - ], - }, - }, - actions: [ - { - name: 'Disable', - type: 'property', - property: { - label: 'Disabled', - value: 'disabled', - type: 'boolean', - }, - state: true, }, - ], + defaultValue: '00/00/0000', + key: 'day', + logic: [ + { + name: 'Disable when Test is empty', + trigger: { + type: 'simple', + simple: { + show: true, + conjunction: 'all', + conditions: [ + { + component: 'textField', + operator: 'isEmpty', + }, + ], + }, + }, + actions: [ + { + name: 'Disable', + type: 'property', + property: { + label: 'Disabled', + value: 'disabled', + type: 'boolean', + }, + state: true, + }, + ], + }, + ], + type: 'day', + input: true, + }, + { + type: 'button', + label: 'Submit', + key: 'submit', + disableOnInvalid: true, + input: true, + tableView: false, }, - ], - type: 'day', - input: true, - }, - { - type: 'button', - label: 'Submit', - key: 'submit', - disableOnInvalid: true, - input: true, - tableView: false, - }, - ], + ], }; diff --git a/src/components/day/fixtures/values.js b/src/components/day/fixtures/values.js index 1abf3fd8b6..cbac71d3ce 100644 --- a/src/components/day/fixtures/values.js +++ b/src/components/day/fixtures/values.js @@ -1,4 +1 @@ -export default [ - '01/01/2001', - '12/31/1999', -]; +export default ['01/01/2001', '12/31/1999']; diff --git a/src/components/editgrid/EditGrid.form.js b/src/components/editgrid/EditGrid.form.js index caa0e655ba..63df9f0358 100644 --- a/src/components/editgrid/EditGrid.form.js +++ b/src/components/editgrid/EditGrid.form.js @@ -4,25 +4,28 @@ import EditGridEditDisplay from './editForm/EditGrid.edit.display'; import EditGridEditTemplates from './editForm/EditGrid.edit.templates'; import EditGridEditValidation from './editForm/EditGrid.edit.validation'; -export default function(...extend) { - return Components.baseEditForm([ - { - label: 'Templates', - key: 'templates', - weight: 5, - components: EditGridEditTemplates - }, - { - key: 'display', - components: EditGridEditDisplay, - }, - { - key: 'data', - components: EditGridEditData, - }, - { - key: 'validation', - components: EditGridEditValidation - }, - ], ...extend); +export default function (...extend) { + return Components.baseEditForm( + [ + { + label: 'Templates', + key: 'templates', + weight: 5, + components: EditGridEditTemplates, + }, + { + key: 'display', + components: EditGridEditDisplay, + }, + { + key: 'data', + components: EditGridEditData, + }, + { + key: 'validation', + components: EditGridEditValidation, + }, + ], + ...extend, + ); } diff --git a/src/components/editgrid/EditGrid.js b/src/components/editgrid/EditGrid.js index f8532a9060..d00e7e0802 100644 --- a/src/components/editgrid/EditGrid.js +++ b/src/components/editgrid/EditGrid.js @@ -2,67 +2,75 @@ import _ from 'lodash'; import NestedArrayComponent from '../_classes/nestedarray/NestedArrayComponent'; import Component from '../_classes/component/Component'; import Alert from '../alert/Alert'; -import { fastCloneDeep, Evaluator, getArrayFromComponentPath, eachComponent } from '../../utils/utils'; +import { + fastCloneDeep, + Evaluator, + getArrayFromComponentPath, + eachComponent, +} from '../../utils/utils'; import { editgrid as templates } from '@formio/bootstrap/components'; const EditRowState = { - New: 'new', - Editing: 'editing', - Saved: 'saved', - Viewing: 'viewing', - Removed: 'removed', - Draft: 'draft', + New: 'new', + Editing: 'editing', + Saved: 'saved', + Viewing: 'viewing', + Removed: 'removed', + Draft: 'draft', }; export default class EditGridComponent extends NestedArrayComponent { - static schema(...extend) { - return NestedArrayComponent.schema({ - type: 'editgrid', - label: 'Edit Grid', - key: 'editGrid', - clearOnHide: true, - input: true, - tree: true, - removeRow: 'Cancel', - defaultOpen: false, - openWhenEmpty: false, - modal: false, - components: [], - inlineEdit: false, - templates: { - header: EditGridComponent.defaultHeaderTemplate, - row: EditGridComponent.defaultRowTemplate, - tableHeader: EditGridComponent.defaultTableHeaderTemplate, - tableRow: EditGridComponent.defaultTableRowTemplate, - footer: '', - }, - }, ...extend); - } - - static get builderInfo() { - return { - title: 'Edit Grid', - icon: 'tasks', - group: 'data', - documentation: '/userguide/form-building/data-components#edit-grid', - showPreview: false, - weight: 30, - schema: EditGridComponent.schema(), - }; - } - - static get defaultHeaderTemplate() { - return `
    + static schema(...extend) { + return NestedArrayComponent.schema( + { + type: 'editgrid', + label: 'Edit Grid', + key: 'editGrid', + clearOnHide: true, + input: true, + tree: true, + removeRow: 'Cancel', + defaultOpen: false, + openWhenEmpty: false, + modal: false, + components: [], + inlineEdit: false, + templates: { + header: EditGridComponent.defaultHeaderTemplate, + row: EditGridComponent.defaultRowTemplate, + tableHeader: EditGridComponent.defaultTableHeaderTemplate, + tableRow: EditGridComponent.defaultTableRowTemplate, + footer: '', + }, + }, + ...extend, + ); + } + + static get builderInfo() { + return { + title: 'Edit Grid', + icon: 'tasks', + group: 'data', + documentation: '/userguide/form-building/data-components#edit-grid', + showPreview: false, + weight: 30, + schema: EditGridComponent.schema(), + }; + } + + static get defaultHeaderTemplate() { + return `
    {% util.eachComponent(components, function(component) { %} {% if (displayValue(component)) { %}
    {{ t(component.label) }}
    {% } %} {% }) %}
    `; - } + } - static get defaultTableHeaderTemplate() { - return ` + static get defaultTableHeaderTemplate() { + return ` {% util.eachComponent(components, function(component) { %} {% if (!component.hasOwnProperty('tableView') || component.tableView) { %} @@ -74,10 +82,10 @@ export default class EditGridComponent extends NestedArrayComponent { {% } %} `; - } + } - static get defaultRowTemplate() { - return `
    + static get defaultRowTemplate() { + return `
    {% util.eachComponent(components, function(component) { %} {% if (displayValue(component)) { %}
    @@ -96,10 +104,10 @@ export default class EditGridComponent extends NestedArrayComponent {
    {% } %}
    `; - } + } - static get defaultTableRowTemplate() { - return ` + static get defaultTableRowTemplate() { + return ` {% util.eachComponent(components, function(component) { %} {% if (!component.hasOwnProperty('tableView') || component.tableView) { %} @@ -118,1267 +126,1504 @@ export default class EditGridComponent extends NestedArrayComponent { {% } %} `; - } + } - get defaultDialogTemplate() { - return ` + get defaultDialogTemplate() { + return `

    ${this.t('Do you want to clear data?')}

    - - + +
    `; - } - - get defaultRowTemplate() { - return this.displayAsTable - ? EditGridComponent.defaultTableRowTemplate - : EditGridComponent.defaultRowTemplate; - } - - get defaultHeaderTemplate() { - return this.displayAsTable - ? EditGridComponent.defaultTableHeaderTemplate - : EditGridComponent.defaultHeaderTemplate; - } - - get rowTemplate() { - let rowTemplate; - if (Evaluator.noeval) { - rowTemplate = this.displayAsTable ? - templates.tableRow - : templates.row; - } - else { - rowTemplate = this.displayAsTable ? - _.get(this.component, 'templates.tableRow', this.defaultRowTemplate) - : _.get(this.component, 'templates.row', this.defaultRowTemplate); - } - - return rowTemplate; - } - - get headerTemplate() { - let headerTemplate; - if (Evaluator.noeval) { - headerTemplate = this.displayAsTable ? - templates.tableHeader - : templates.header; - } - else { - headerTemplate = this.displayAsTable ? - _.get(this.component, 'templates.tableHeader', this.defaultHeaderTemplate) - : _.get(this.component, 'templates.header', this.defaultHeaderTemplate); - } - - return headerTemplate; - } - - /** - * Returns true if the component has nested components which don't trigger changes on the root level - */// - get hasScopedChildren() { - return !this.inlineEditMode; - } - - get defaultSchema() { - return EditGridComponent.schema(); - } - - get emptyValue() { - return []; - } - - get editgridKey() { - return `editgrid-${this.key}`; - } - - get rowRef() { - return `${this.editgridKey}-row`; - } - - get rowElements() { - return this.refs[this.rowRef]; - } - - get rowRefs() { - return this.refs[`editgrid-${this.component.key}-row`]; - } - - get addRowRef() { - return `${this.editgridKey}-addRow`; - } - - get addRowElements() { - return this.refs[this.addRowRef]; - } - - get saveRowRef() { - return `${this.editgridKey}-saveRow`; - } - - get saveRowElements() { - return this.refs[this.saveRowRef]; - } - - get cancelRowRef() { - return `${this.editgridKey}-cancelRow`; - } - - get cancelRowElements() { - return this.refs[this.cancelRowRef]; - } - - get inlineEditMode() { - return this.component.inlineEdit; - } - - get saveEditMode() { - return !this.inlineEditMode; - } - - get minLength() { - return this.builderMode ? 0 : _.get(this.component, 'validate.minLength', 0); - } - - get data() { - return this._data; - } - - get dataValue() { - return super.dataValue || []; - } - - set dataValue(value) { - super.dataValue = value; - } - - get displayAsTable() { - return this.component.displayAsTable; - } - - set data(value) { - this._data = value; - - const data = this.dataValue; - - (this.editRows || []).forEach((row, index) => { - if (!data[index] && row.state !== EditRowState.New) { - data[index] = {}; - } - const rowData = data[index] || {}; - - row.data = rowData; - row.components.forEach((component) => { - component.data = rowData; - }); - }); - } - - get iteratableRows() { - return this.editRows; - } - - get defaultValue() { - const value = super.defaultValue; - const defaultValue = Array.isArray(value) ? value : []; - - _.times(this.minLength - defaultValue.length, () => defaultValue.push({})); - - return defaultValue; - } - - constructor(...args) { - super(...args); - this.type = 'editgrid'; - } - - hasRemoveButtons() { - return !this.component.disableAddingRemovingRows && - !this.options.readOnly && - !this.disabled && - this.fullMode && - (this.dataValue.length > _.get(this.component, 'validate.minLength', 0)); - } - - init() { - if (this.builderMode) { - this.editRows = []; - return super.init(); - } - this.components = this.components || []; - const dataValue = this.dataValue; - const openWhenEmpty = !dataValue.length && this.component.openWhenEmpty; - if (openWhenEmpty) { - const dataObj = {}; - this.editRows = []; - this.createRow(dataObj, 0); - } - else { - this.editRows = dataValue.map((row, rowIndex) => ({ - components: this.lazyLoad ? [] : this.createRowComponents(row, rowIndex), - data: row, - state: EditRowState.Saved, - backup: null, - error: null, - rowIndex, - })); - } - this.prevHasAddButton = this.hasAddButton(); - - this.checkData(); - - this.setVariableTypeComponents(); - - if (this.variableTypeComponentsIndexes.length) { - _.each(this.editRows || [], (editRow, rowIndex) => this.checkRowVariableTypeComponents(editRow, rowIndex)); - } - } - - checkRowVariableTypeComponents(editRow, rowIndex) { - const rowComponents = editRow.components; - - if (_.some(this.variableTypeComponentsIndexes, (compIndex) => { - const variableTypeComp = rowComponents[compIndex]; - return variableTypeComp.type !== variableTypeComp.component.type; - })) { - editRow.components = this.createRowComponents(editRow.data, rowIndex, true); - } - } - - setVariableTypeComponents() { - //set components which type is changing within a row (e.g.,by mergeComponentSchema action) - this.variableTypeComponentsIndexes = []; - - _.each(this.component.components, (comp, index) => { - if (comp.typeChangeEnabled) { - this.variableTypeComponentsIndexes.push(index); - } - }); - } - - isOpen(editRow) { - return [EditRowState.New, EditRowState.Editing, EditRowState.Viewing].includes(editRow.state); - } - - isComponentVisibleInSomeRow(component) { - const rows = this.editRows; - const savedStates = [EditRowState.Saved, EditRowState.Editing, EditRowState.Draft]; - const savedRows = rows.filter(row => _.includes(savedStates, row.state)); - - this.visibleInHeader = this.visibleInHeader || []; - - const changeVisibleInHeader = (component, isVisible) => { - if (!isVisible) { - _.remove(this.visibleInHeader, (key) => key === component.key); - } - - if (isVisible && !_.includes(this.visibleInHeader, component.key)) { - this.visibleInHeader.push(component.key); - } - }; - - if (_.isEmpty(rows)) { - const rowComponents = this.createRowComponents({}, 0); - let checkComponent; - - eachComponent(rowComponents, (comp) => { - if (comp.component.key === component.key) { - checkComponent = comp; + } + + get defaultRowTemplate() { + return this.displayAsTable + ? EditGridComponent.defaultTableRowTemplate + : EditGridComponent.defaultRowTemplate; + } + + get defaultHeaderTemplate() { + return this.displayAsTable + ? EditGridComponent.defaultTableHeaderTemplate + : EditGridComponent.defaultHeaderTemplate; + } + + get rowTemplate() { + let rowTemplate; + if (Evaluator.noeval) { + rowTemplate = this.displayAsTable + ? templates.tableRow + : templates.row; + } else { + rowTemplate = this.displayAsTable + ? _.get( + this.component, + 'templates.tableRow', + this.defaultRowTemplate, + ) + : _.get( + this.component, + 'templates.row', + this.defaultRowTemplate, + ); } - comp.checkConditions(); - }); - const isVisible = checkComponent ? checkComponent.visible : true; - [...this.components].forEach((comp) => this.removeComponent(comp, this.components)); + return rowTemplate; + } + + get headerTemplate() { + let headerTemplate; + if (Evaluator.noeval) { + headerTemplate = this.displayAsTable + ? templates.tableHeader + : templates.header; + } else { + headerTemplate = this.displayAsTable + ? _.get( + this.component, + 'templates.tableHeader', + this.defaultHeaderTemplate, + ) + : _.get( + this.component, + 'templates.header', + this.defaultHeaderTemplate, + ); + } - changeVisibleInHeader(component, isVisible); + return headerTemplate; + } - return isVisible; + /** + * Returns true if the component has nested components which don't trigger changes on the root level + */ // + get hasScopedChildren() { + return !this.inlineEditMode; } - const isOpenRowWhenEmpty = _.get(this.component, 'openWhenEmpty') && rows.length === 1 && rows[0].state === EditRowState.New; + get defaultSchema() { + return EditGridComponent.schema(); + } - if (!_.isEmpty(rows) && _.isEmpty(savedRows) && !isOpenRowWhenEmpty) { - return _.includes(this.visibleInHeader, component.key); + get emptyValue() { + return []; } - return _.some(isOpenRowWhenEmpty ? rows : savedRows, (row, index) => { - const editingRow = row.state === EditRowState.Editing; - let isVisible; + get editgridKey() { + return `editgrid-${this.key}`; + } - if (!editingRow) { - const flattenedComponents = this.flattenComponents(index); - const instance = flattenedComponents[component.key]; - isVisible = instance ? instance.visible : true; - - changeVisibleInHeader(component, isVisible); - } - else { - isVisible = _.includes(this.visibleInHeader, component.key); - } - - return isVisible; - }); - } - - render(children) { - if (this.builderMode) { - return super.render(); - } - - const dataValue = this.dataValue; - const headerTemplate = this.headerTemplate; - const t = this.t.bind(this); - const templateName = this.displayAsTable ? 'editgridTable' : 'editgrid'; - - return super.render(children || this.renderTemplate(templateName, { - ref: { - row: this.rowRef, - addRow: this.addRowRef, - saveRow: this.saveRowRef, - cancelRow: this.cancelRowRef, - }, - header: this.renderString(headerTemplate, { - displayValue: (component) => this.displayComponentValue(component, true), - components: this.component.components, - value: dataValue, - t - }), - footer: this.renderString(_.get(this.component, 'templates.footer'), { - components: this.component.components, - value: dataValue, - t - }), - rows: this.editRows.map(this.renderRow.bind(this)), - openRows: this.editRows.map((row) => this.isOpen(row)), - errors: this.editRows.map((row) => row.error), - hasAddButton: this.hasAddButton(), - hasRemoveButtons: this.hasRemoveButtons(), - })); - } - - renderComponents(components) { - components = components || this.getComponents(); - const children = components.map(component => component.render()); - const templateName = this.displayAsTable && this.prevHasAddButton ? 'tableComponents' : 'components'; - - return this.renderTemplate(templateName, { - children, - components, - }); - } - - attach(element) { - if (this.builderMode) { - return super.attach(element); - } - - this.loadRefs(element, { - [this.addRowRef]: 'multiple', - [this.saveRowRef]: 'multiple', - [this.cancelRowRef]: 'multiple', - [this.rowRef]: 'multiple', - }); - this.addRowElements.forEach((addButton) => { - this.addEventListener(addButton, 'click', () => this.addRow()); - }); - - let openRowCount = 0; - this.rowElements.forEach((row, rowIndex) => { - const editRow = this.editRows[rowIndex]; - if (editRow?.isRowSelected) { - row.classList.add('selected'); - } - if (this.isOpen(editRow)) { - this.attachComponents(row, editRow.components); - this.addEventListener(this.saveRowElements[openRowCount], 'click', () => this.saveRow(rowIndex, true)); - this.addEventListener(this.cancelRowElements[openRowCount], 'click', () => this.cancelRow(rowIndex)); - openRowCount++; - } - else { - // Attach edit and remove button events. - [ - { - className: 'removeRow', - event: 'click', - action: () => this.removeRow(rowIndex, true), - }, - { - className: 'editRow', - event: 'click', - action: () => { - this.editRow(rowIndex).then(() => { - if (this.component.rowDrafts) { - this.validateRow(editRow, false); - - const hasErrors = editRow.errors && !!editRow.errors.length; - const shouldShowRowErrorsAlert = this.component.modal && hasErrors && this.root?.submitted; - - if (shouldShowRowErrorsAlert) { - this.alert.showErrors(editRow.errors, false); - editRow.alerts = true; - } - } - }); - }, - }, - { - className: 'row', - event: 'click', - action: () => { - row.classList.toggle('selected'); - let eventName = 'editGridSelectRow'; - if (Array.from(row.classList).includes('selected')) { - editRow.isRowSelected = true; - } - else { - delete editRow.isRowSelected; - eventName = 'editGridUnSelectRow'; - } - - this.emit(eventName, { - component: this.component, - data: this.dataValue[rowIndex] - }); - }, - } - ].forEach(({ - className, - event, - action, - }) => { - const elements = row.getElementsByClassName(className); - Array.prototype.forEach.call(elements, (element) => { - if (this.options.pdf && _.intersection(element.classList, ['editRow', 'removeRow']).length) { - element.style.display = 'none'; - } - else { - this.addEventListener(element, event, action); + get rowRef() { + return `${this.editgridKey}-row`; + } + + get rowElements() { + return this.refs[this.rowRef]; + } + + get rowRefs() { + return this.refs[`editgrid-${this.component.key}-row`]; + } + + get addRowRef() { + return `${this.editgridKey}-addRow`; + } + + get addRowElements() { + return this.refs[this.addRowRef]; + } + + get saveRowRef() { + return `${this.editgridKey}-saveRow`; + } + + get saveRowElements() { + return this.refs[this.saveRowRef]; + } + + get cancelRowRef() { + return `${this.editgridKey}-cancelRow`; + } + + get cancelRowElements() { + return this.refs[this.cancelRowRef]; + } + + get inlineEditMode() { + return this.component.inlineEdit; + } + + get saveEditMode() { + return !this.inlineEditMode; + } + + get minLength() { + return this.builderMode + ? 0 + : _.get(this.component, 'validate.minLength', 0); + } + + get data() { + return this._data; + } + + get dataValue() { + return super.dataValue || []; + } + + set dataValue(value) { + super.dataValue = value; + } + + get displayAsTable() { + return this.component.displayAsTable; + } + + set data(value) { + this._data = value; + + const data = this.dataValue; + + (this.editRows || []).forEach((row, index) => { + if (!data[index] && row.state !== EditRowState.New) { + data[index] = {}; } - }); + const rowData = data[index] || {}; + + row.data = rowData; + row.components.forEach((component) => { + component.data = rowData; + }); }); - } - }); - - // Add open class to the element if any edit grid row is open - if (openRowCount) { - this.addClass(this.refs.component, `formio-component-${this.component.type}-row-open`); - } - else { - this.removeClass(this.refs.component, `formio-component-${this.component.type}-row-open`); - } - - const superAttach = super.attach(element); - this.loadRefs(element, { - messageContainer: 'single-scope', - }); - return superAttach; - } - - flattenRowDataValue(dataValue) { - const flattened = {}; - - Object.keys(dataValue).forEach((key) => { - if (_.isObject(dataValue[key]) && !_.isNil(dataValue[key])) { - Object.assign(flattened, this.flattenRowDataValue(dataValue[key])); - } - else { - flattened[key] = dataValue[key]; - } - }); - - return flattened; - } - - isComponentVisibleInRow(component, flattenedComponents) { - const instance = flattenedComponents[component.key]; - return instance ? instance.visible : true; - } - - displayComponentValue(component, header) { - return !!((!Object.prototype.hasOwnProperty.call(component, 'tableView') || component.tableView) - && header ? this.isComponentVisibleInSomeRow(component) : _.includes(this.visibleInHeader, component.key)); - } - - renderRow(row, rowIndex) { - const dataValue = this.dataValue; - if (this.isOpen(row)) { - return this.renderComponents(row.components); - } - else { - const flattenedComponents = this.flattenComponents(rowIndex); - const rowTemplate = this.rowTemplate; - - return this.renderString( - rowTemplate, - { - row: dataValue[rowIndex] || {}, - data: this.data, - rowIndex, - components: this.component.components, - flattenedComponents, - displayValue: (component) => this.displayComponentValue(component), - isVisibleInRow: (component) => this.isComponentVisibleInRow(component, flattenedComponents), - getView: (component, data) => { - const instance = flattenedComponents[component.key]; - const view = instance ? instance.getView(data || instance.dataValue) : ''; - - // If there is an html tag in view, don't allow it to be injected in template - const htmlTagRegExp = new RegExp('<(.*?)>'); - return typeof view === 'string' && view.length && !instance.component?.template && htmlTagRegExp.test(view) && instance.component?.inputFormat !== 'html' - ? `` - : view; - }, - state: this.editRows[rowIndex].state, - t: this.t.bind(this) - }, - ); - } - } - - eachComponent(fn, rowIndex) { - _.each(this.getComponents(rowIndex), (component, index) => { - if (fn(component, index) === false) { - return false; - } - }); - } - - restoreComponentsContext() { - this.getComponents().forEach((component) => { - const rowData = this.dataValue[component.rowIndex]; - const editRowData = this.editRows[component.rowIndex]?.data; - component.data = rowData || editRowData; - }); - } - - flattenComponents(rowIndex) { - const result = {}; - - this.everyComponent((component) => { - result[component.component.flattenAs || component.key] = component; - }, rowIndex); - - return result; - } - - getComponents(rowIndex) { - // Ensure editrows is set. - this.editRows = this.editRows || []; - return this.builderMode - ? super.getComponents() - : _.isNumber(rowIndex) - ? (this.editRows[rowIndex]?.components || []) - : this.editRows.reduce((result, row) => result.concat(row.components || []), []); - } - - destroy(all = false) { - this.calculatedValue = undefined; - super.destroy(all); - } - - destroyComponents(all = false, rowIndex = 0) { - if (this.builderMode) { - return super.destroyComponents(all); - } - - const components = this.getComponents(rowIndex).slice(); - components.forEach((comp) => this.removeComponent(comp, this.components, all)); - } - - createRow(dataObj, rowIndex) { - const editRow = { - components: this.createRowComponents(dataObj, rowIndex), - data: dataObj, - state: EditRowState.New, - backup: null, - error: null, - rowIndex, - }; - - this.editRows.push(editRow); - if (this.inlineEditMode) { - this.dataValue.push(dataObj); - } - - return editRow; - } - - addRow() { - if (this.options.readOnly) { - return; - } - - const dataObj = {}; - const rowIndex = this.editRows.length; - const editRow = this.createRow(dataObj, rowIndex); - - if (editRow.state === EditRowState.New) { - this.emptyRow = fastCloneDeep(editRow.data); - } - - if (this.inlineEditMode) { - this.triggerChange(); - } - this.emit('editGridAddRow', { - component: this.component, - row: editRow, - }); - this.checkRow('checkData', null, {}, editRow.data, editRow.components); - if (this.component.modal) { - this.addRowModal(rowIndex); - } - else { - this.redraw(); - } - return editRow; - } - - addRowModal(rowIndex) { - const modalContent = this.ce('div'); - const editRow = this.editRows[rowIndex]; - editRow.willBeSaved = false; - const { components } = editRow; - modalContent.innerHTML = this.renderComponents(components); - - const dialog = this.component.modal ? this.createModal(modalContent, {}, () => this.showDialog(rowIndex)) : undefined; - dialog.classList.add(`editgrid-row-modal-${this.id}`); + } - editRow.dialog = dialog; - - if (this.alert) { - this.alert.clear(); - this.alert = null; + get iteratableRows() { + return this.editRows; } - this.alert = new Alert(dialog.refs.dialogContents, this); - this.addEventListener(dialog, 'close', () => { - if (!editRow.willBeSaved) { - if (this.editRows[rowIndex] && this.editRows[rowIndex].state !== EditRowState.New) { - this.editRows[rowIndex].components.forEach((comp) => { - comp.setPristine(true); - }); + get defaultValue() { + const value = super.defaultValue; + const defaultValue = Array.isArray(value) ? value : []; + + _.times(this.minLength - defaultValue.length, () => + defaultValue.push({}), + ); + + return defaultValue; + } + + constructor(...args) { + super(...args); + this.type = 'editgrid'; + } + + hasRemoveButtons() { + return ( + !this.component.disableAddingRemovingRows && + !this.options.readOnly && + !this.disabled && + this.fullMode && + this.dataValue.length > + _.get(this.component, 'validate.minLength', 0) + ); + } + + init() { + if (this.builderMode) { + this.editRows = []; + return super.init(); } - this.cancelRow(rowIndex); - } - if (this.alert) { - this.alert.clear(); - this.alert = null; - } - - // Remove references to dialog elements to prevent possible in some cases memory leaks - delete editRow.confirmationDialog; - delete editRow.dialog; - }); - - dialog.refs.dialogContents.appendChild(this.ce('button', { - class: 'btn btn-primary', - onClick: () => { - // After an attempt to save, all the components inside the row should become not pristine - if (!this.component.rowDrafts) { - editRow.components.forEach((comp) => comp.setPristine(false)); + this.components = this.components || []; + const dataValue = this.dataValue; + const openWhenEmpty = !dataValue.length && this.component.openWhenEmpty; + if (openWhenEmpty) { + const dataObj = {}; + this.editRows = []; + this.createRow(dataObj, 0); + } else { + this.editRows = dataValue.map((row, rowIndex) => ({ + components: this.lazyLoad + ? [] + : this.createRowComponents(row, rowIndex), + data: row, + state: EditRowState.Saved, + backup: null, + error: null, + rowIndex, + })); } - if (this.validateRow(editRow, true) || this.component.rowDrafts) { - editRow.willBeSaved = true; - dialog.close(); - this.saveRow(rowIndex, true); + this.prevHasAddButton = this.hasAddButton(); + + this.checkData(); + + this.setVariableTypeComponents(); + + if (this.variableTypeComponentsIndexes.length) { + _.each(this.editRows || [], (editRow, rowIndex) => + this.checkRowVariableTypeComponents(editRow, rowIndex), + ); } - else { - this.alert.showErrors(editRow.errors, false); - editRow.alerts = true; + } + + checkRowVariableTypeComponents(editRow, rowIndex) { + const rowComponents = editRow.components; + + if ( + _.some(this.variableTypeComponentsIndexes, (compIndex) => { + const variableTypeComp = rowComponents[compIndex]; + return ( + variableTypeComp.type !== variableTypeComp.component.type + ); + }) + ) { + editRow.components = this.createRowComponents( + editRow.data, + rowIndex, + true, + ); } - }, - }, this.component.saveRow || 'Save')); + } - return this.attachComponents(modalContent, components); - } + setVariableTypeComponents() { + //set components which type is changing within a row (e.g.,by mergeComponentSchema action) + this.variableTypeComponentsIndexes = []; + + _.each(this.component.components, (comp, index) => { + if (comp.typeChangeEnabled) { + this.variableTypeComponentsIndexes.push(index); + } + }); + } - showDialog(rowIndex) { - const editRow = this.editRows[rowIndex]; - if (editRow.state === EditRowState.New ? _.isEqual(this.emptyRow, editRow.data) : _.isEqual(editRow.backup, editRow.data)) { - return Promise.resolve(); + isOpen(editRow) { + return [ + EditRowState.New, + EditRowState.Editing, + EditRowState.Viewing, + ].includes(editRow.state); } - const wrapper = this.ce('div', { ref: 'confirmationDialog' }); - const dialogContent = this.component.dialogTemplate || this.defaultDialogTemplate; + isComponentVisibleInSomeRow(component) { + const rows = this.editRows; + const savedStates = [ + EditRowState.Saved, + EditRowState.Editing, + EditRowState.Draft, + ]; + const savedRows = rows.filter((row) => + _.includes(savedStates, row.state), + ); + + this.visibleInHeader = this.visibleInHeader || []; + + const changeVisibleInHeader = (component, isVisible) => { + if (!isVisible) { + _.remove(this.visibleInHeader, (key) => key === component.key); + } + + if (isVisible && !_.includes(this.visibleInHeader, component.key)) { + this.visibleInHeader.push(component.key); + } + }; - wrapper.innerHTML = dialogContent; - wrapper.refs = {}; - this.loadRefs.call(wrapper, wrapper, { - dialogHeader: 'single', - dialogCancelButton: 'single', - dialogYesButton: 'single', - }); + if (_.isEmpty(rows)) { + const rowComponents = this.createRowComponents({}, 0); + let checkComponent; - const dialog = this.createModal(wrapper); - dialog.classList.add(`editgrid-row-modal-confirmation-${this.id}`); + eachComponent(rowComponents, (comp) => { + if (comp.component.key === component.key) { + checkComponent = comp; + } + comp.checkConditions(); + }); - const close = (event) => { - event.preventDefault(); - dialog.close(); - }; - let dialogResult; + const isVisible = checkComponent ? checkComponent.visible : true; + [...this.components].forEach((comp) => + this.removeComponent(comp, this.components), + ); - const promise = new Promise((resolve, reject) => { - dialogResult = { resolve, reject }; - }); + changeVisibleInHeader(component, isVisible); - this.addEventListener(wrapper.refs.dialogYesButton, 'click', (event) => { - close(event); - dialogResult.resolve(); - }); - this.addEventListener(wrapper.refs.dialogCancelButton, 'click', (event) => { - close(event); - dialogResult.reject(); - }); + return isVisible; + } + + const isOpenRowWhenEmpty = + _.get(this.component, 'openWhenEmpty') && + rows.length === 1 && + rows[0].state === EditRowState.New; + + if (!_.isEmpty(rows) && _.isEmpty(savedRows) && !isOpenRowWhenEmpty) { + return _.includes(this.visibleInHeader, component.key); + } + + return _.some(isOpenRowWhenEmpty ? rows : savedRows, (row, index) => { + const editingRow = row.state === EditRowState.Editing; + let isVisible; + + if (!editingRow) { + const flattenedComponents = this.flattenComponents(index); + const instance = flattenedComponents[component.key]; + isVisible = instance ? instance.visible : true; + + changeVisibleInHeader(component, isVisible); + } else { + isVisible = _.includes(this.visibleInHeader, component.key); + } + + return isVisible; + }); + } - editRow.confirmationDialog = dialog; - return promise; - } + render(children) { + if (this.builderMode) { + return super.render(); + } - editRow(rowIndex) { - const editRow = this.editRows[rowIndex]; - const isAlreadyEditing = editRow.state === EditRowState.Editing || editRow.state === EditRowState.New; - if (!editRow || isAlreadyEditing) { - return Promise.resolve(); + const dataValue = this.dataValue; + const headerTemplate = this.headerTemplate; + const t = this.t.bind(this); + const templateName = this.displayAsTable ? 'editgridTable' : 'editgrid'; + + return super.render( + children || + this.renderTemplate(templateName, { + ref: { + row: this.rowRef, + addRow: this.addRowRef, + saveRow: this.saveRowRef, + cancelRow: this.cancelRowRef, + }, + header: this.renderString(headerTemplate, { + displayValue: (component) => + this.displayComponentValue(component, true), + components: this.component.components, + value: dataValue, + t, + }), + footer: this.renderString( + _.get(this.component, 'templates.footer'), + { + components: this.component.components, + value: dataValue, + t, + }, + ), + rows: this.editRows.map(this.renderRow.bind(this)), + openRows: this.editRows.map((row) => this.isOpen(row)), + errors: this.editRows.map((row) => row.error), + hasAddButton: this.hasAddButton(), + hasRemoveButtons: this.hasRemoveButtons(), + }), + ); + } + + renderComponents(components) { + components = components || this.getComponents(); + const children = components.map((component) => component.render()); + const templateName = + this.displayAsTable && this.prevHasAddButton + ? 'tableComponents' + : 'components'; + + return this.renderTemplate(templateName, { + children, + components, + }); } - editRow.prevState = editRow.state; - editRow.state = this.options.readOnly ? EditRowState.Viewing : EditRowState.Editing; - if (this.lazyLoad && (editRow.components.length === 0)) { - editRow.components = this.createRowComponents(editRow.data, rowIndex); + attach(element) { + if (this.builderMode) { + return super.attach(element); + } + + this.loadRefs(element, { + [this.addRowRef]: 'multiple', + [this.saveRowRef]: 'multiple', + [this.cancelRowRef]: 'multiple', + [this.rowRef]: 'multiple', + }); + this.addRowElements.forEach((addButton) => { + this.addEventListener(addButton, 'click', () => this.addRow()); + }); + + let openRowCount = 0; + this.rowElements.forEach((row, rowIndex) => { + const editRow = this.editRows[rowIndex]; + if (editRow?.isRowSelected) { + row.classList.add('selected'); + } + if (this.isOpen(editRow)) { + this.attachComponents(row, editRow.components); + this.addEventListener( + this.saveRowElements[openRowCount], + 'click', + () => this.saveRow(rowIndex, true), + ); + this.addEventListener( + this.cancelRowElements[openRowCount], + 'click', + () => this.cancelRow(rowIndex), + ); + openRowCount++; + } else { + // Attach edit and remove button events. + [ + { + className: 'removeRow', + event: 'click', + action: () => this.removeRow(rowIndex, true), + }, + { + className: 'editRow', + event: 'click', + action: () => { + this.editRow(rowIndex).then(() => { + if (this.component.rowDrafts) { + this.validateRow(editRow, false); + + const hasErrors = + editRow.errors && + !!editRow.errors.length; + const shouldShowRowErrorsAlert = + this.component.modal && + hasErrors && + this.root?.submitted; + + if (shouldShowRowErrorsAlert) { + this.alert.showErrors( + editRow.errors, + false, + ); + editRow.alerts = true; + } + } + }); + }, + }, + { + className: 'row', + event: 'click', + action: () => { + row.classList.toggle('selected'); + let eventName = 'editGridSelectRow'; + if ( + Array.from(row.classList).includes('selected') + ) { + editRow.isRowSelected = true; + } else { + delete editRow.isRowSelected; + eventName = 'editGridUnSelectRow'; + } + + this.emit(eventName, { + component: this.component, + data: this.dataValue[rowIndex], + }); + }, + }, + ].forEach(({ className, event, action }) => { + const elements = row.getElementsByClassName(className); + Array.prototype.forEach.call(elements, (element) => { + if ( + this.options.pdf && + _.intersection(element.classList, [ + 'editRow', + 'removeRow', + ]).length + ) { + element.style.display = 'none'; + } else { + this.addEventListener(element, event, action); + } + }); + }); + } + }); + + // Add open class to the element if any edit grid row is open + if (openRowCount) { + this.addClass( + this.refs.component, + `formio-component-${this.component.type}-row-open`, + ); + } else { + this.removeClass( + this.refs.component, + `formio-component-${this.component.type}-row-open`, + ); + } + + const superAttach = super.attach(element); + this.loadRefs(element, { + messageContainer: 'single-scope', + }); + return superAttach; } - const dataSnapshot = fastCloneDeep(editRow.data); + flattenRowDataValue(dataValue) { + const flattened = {}; + + Object.keys(dataValue).forEach((key) => { + if (_.isObject(dataValue[key]) && !_.isNil(dataValue[key])) { + Object.assign( + flattened, + this.flattenRowDataValue(dataValue[key]), + ); + } else { + flattened[key] = dataValue[key]; + } + }); + + return flattened; + } - if (this.inlineEditMode) { - editRow.backup = dataSnapshot; + isComponentVisibleInRow(component, flattenedComponents) { + const instance = flattenedComponents[component.key]; + return instance ? instance.visible : true; + } + + displayComponentValue(component, header) { + return !!((!Object.prototype.hasOwnProperty.call( + component, + 'tableView', + ) || + component.tableView) && + header + ? this.isComponentVisibleInSomeRow(component) + : _.includes(this.visibleInHeader, component.key)); + } + + renderRow(row, rowIndex) { + const dataValue = this.dataValue; + if (this.isOpen(row)) { + return this.renderComponents(row.components); + } else { + const flattenedComponents = this.flattenComponents(rowIndex); + const rowTemplate = this.rowTemplate; + + return this.renderString(rowTemplate, { + row: dataValue[rowIndex] || {}, + data: this.data, + rowIndex, + components: this.component.components, + flattenedComponents, + displayValue: (component) => + this.displayComponentValue(component), + isVisibleInRow: (component) => + this.isComponentVisibleInRow( + component, + flattenedComponents, + ), + getView: (component, data) => { + const instance = flattenedComponents[component.key]; + const view = instance + ? instance.getView(data || instance.dataValue) + : ''; + + // If there is an html tag in view, don't allow it to be injected in template + const htmlTagRegExp = new RegExp('<(.*?)>'); + return typeof view === 'string' && + view.length && + !instance.component?.template && + htmlTagRegExp.test(view) && + instance.component?.inputFormat !== 'html' + ? `` + : view; + }, + state: this.editRows[rowIndex].state, + t: this.t.bind(this), + }); + } } - else { - editRow.backup = fastCloneDeep(editRow.data); - editRow.data = dataSnapshot; - this.restoreRowContext(editRow); + + eachComponent(fn, rowIndex) { + _.each(this.getComponents(rowIndex), (component, index) => { + if (fn(component, index) === false) { + return false; + } + }); } - if (this.component.modal) { - return this.addRowModal(rowIndex); + restoreComponentsContext() { + this.getComponents().forEach((component) => { + const rowData = this.dataValue[component.rowIndex]; + const editRowData = this.editRows[component.rowIndex]?.data; + component.data = rowData || editRowData; + }); } - return this.redraw(); - } + flattenComponents(rowIndex) { + const result = {}; + + this.everyComponent((component) => { + result[component.component.flattenAs || component.key] = component; + }, rowIndex); - clearErrors(rowIndex) { - const editRow = this.editRows[rowIndex]; - if (editRow && Array.isArray(editRow.components)) { - editRow.components.forEach((comp) => { - comp.setPristine(true); - comp.setCustomValidity(''); - }); + return result; } - } - cancelRow(rowIndex) { - if (this.options.readOnly) { - return; + getComponents(rowIndex) { + // Ensure editrows is set. + this.editRows = this.editRows || []; + return this.builderMode + ? super.getComponents() + : _.isNumber(rowIndex) + ? this.editRows[rowIndex]?.components || [] + : this.editRows.reduce( + (result, row) => result.concat(row.components || []), + [], + ); } - const editRow = this.editRows[rowIndex]; - switch (editRow.state) { - case EditRowState.New: { - editRow.state = EditRowState.Removed; + destroy(all = false) { + this.calculatedValue = undefined; + super.destroy(all); + } - this.clearErrors(rowIndex); - this.destroyComponents(false, rowIndex); - if (this.inlineEditMode) { - this.splice(rowIndex); + destroyComponents(all = false, rowIndex = 0) { + if (this.builderMode) { + return super.destroyComponents(all); } - this.editRows.splice(rowIndex, 1); - this.openWhenEmpty(); - break; - } - case EditRowState.Editing: { - editRow.state = editRow.prevState; + const components = this.getComponents(rowIndex).slice(); + components.forEach((comp) => + this.removeComponent(comp, this.components, all), + ); + } + + createRow(dataObj, rowIndex) { + const editRow = { + components: this.createRowComponents(dataObj, rowIndex), + data: dataObj, + state: EditRowState.New, + backup: null, + error: null, + rowIndex, + }; + + this.editRows.push(editRow); if (this.inlineEditMode) { - this.dataValue[rowIndex] = editRow.backup; + this.dataValue.push(dataObj); } - editRow.data = editRow.backup; - editRow.backup = null; - this.restoreRowContext(editRow); - this.clearErrors(rowIndex); - break; - } + + return editRow; } - this.emit('editGridCancelRow', { - instance: this, - component: this.component, - editRow, - }); + addRow() { + if (this.options.readOnly) { + return; + } + + const dataObj = {}; + const rowIndex = this.editRows.length; + const editRow = this.createRow(dataObj, rowIndex); - this.checkValidity(null, true); - this.redraw(); + if (editRow.state === EditRowState.New) { + this.emptyRow = fastCloneDeep(editRow.data); + } - if (this.component.rowDrafts) { - this.checkValidity(this.data, false); + if (this.inlineEditMode) { + this.triggerChange(); + } + this.emit('editGridAddRow', { + component: this.component, + row: editRow, + }); + this.checkRow('checkData', null, {}, editRow.data, editRow.components); + if (this.component.modal) { + this.addRowModal(rowIndex); + } else { + this.redraw(); + } + return editRow; } - } - saveRow(rowIndex, modified) { - const editRow = this.editRows[rowIndex]; + addRowModal(rowIndex) { + const modalContent = this.ce('div'); + const editRow = this.editRows[rowIndex]; + editRow.willBeSaved = false; + const { components } = editRow; + modalContent.innerHTML = this.renderComponents(components); + + const dialog = this.component.modal + ? this.createModal(modalContent, {}, () => + this.showDialog(rowIndex), + ) + : undefined; + dialog.classList.add(`editgrid-row-modal-${this.id}`); + + editRow.dialog = dialog; + + if (this.alert) { + this.alert.clear(); + this.alert = null; + } + this.alert = new Alert(dialog.refs.dialogContents, this); + + this.addEventListener(dialog, 'close', () => { + if (!editRow.willBeSaved) { + if ( + this.editRows[rowIndex] && + this.editRows[rowIndex].state !== EditRowState.New + ) { + this.editRows[rowIndex].components.forEach((comp) => { + comp.setPristine(true); + }); + } + this.cancelRow(rowIndex); + } + if (this.alert) { + this.alert.clear(); + this.alert = null; + } - if (this.options.readOnly) { - return; - } + // Remove references to dialog elements to prevent possible in some cases memory leaks + delete editRow.confirmationDialog; + delete editRow.dialog; + }); - // After an attempt to save, all the components inside the row should become not pristine - if (!this.component.rowDrafts) { - editRow.components.forEach((comp) => comp.setPristine(false)); - } + dialog.refs.dialogContents.appendChild( + this.ce( + 'button', + { + class: 'btn btn-primary', + onClick: () => { + // After an attempt to save, all the components inside the row should become not pristine + if (!this.component.rowDrafts) { + editRow.components.forEach((comp) => + comp.setPristine(false), + ); + } + if ( + this.validateRow(editRow, true) || + this.component.rowDrafts + ) { + editRow.willBeSaved = true; + dialog.close(); + this.saveRow(rowIndex, true); + } else { + this.alert.showErrors(editRow.errors, false); + editRow.alerts = true; + } + }, + }, + this.component.saveRow || 'Save', + ), + ); + + return this.attachComponents(modalContent, components); + } + + showDialog(rowIndex) { + const editRow = this.editRows[rowIndex]; + if ( + editRow.state === EditRowState.New + ? _.isEqual(this.emptyRow, editRow.data) + : _.isEqual(editRow.backup, editRow.data) + ) { + return Promise.resolve(); + } + + const wrapper = this.ce('div', { ref: 'confirmationDialog' }); + const dialogContent = + this.component.dialogTemplate || this.defaultDialogTemplate; + + wrapper.innerHTML = dialogContent; + wrapper.refs = {}; + this.loadRefs.call(wrapper, wrapper, { + dialogHeader: 'single', + dialogCancelButton: 'single', + dialogYesButton: 'single', + }); - const isRowValid = this.validateRow(editRow, true); + const dialog = this.createModal(wrapper); + dialog.classList.add(`editgrid-row-modal-confirmation-${this.id}`); - if (!this.component.rowDrafts) { - if (!isRowValid) { - return false; - } + const close = (event) => { + event.preventDefault(); + dialog.close(); + }; + let dialogResult; + + const promise = new Promise((resolve, reject) => { + dialogResult = { resolve, reject }; + }); + + this.addEventListener( + wrapper.refs.dialogYesButton, + 'click', + (event) => { + close(event); + dialogResult.resolve(); + }, + ); + this.addEventListener( + wrapper.refs.dialogCancelButton, + 'click', + (event) => { + close(event); + dialogResult.reject(); + }, + ); + + editRow.confirmationDialog = dialog; + return promise; } - if (this.saveEditMode) { - const dataValue = this.dataValue; - if (this.root?.focusedComponent?.component.typeChangeEnabled) { - this.root.focusedComponent = null; - } - switch (editRow.state) { - case EditRowState.New: { - const newIndex = dataValue.length; - dataValue.push(editRow.data); - editRow.components.forEach(component=>component.rowIndex = newIndex); - if (rowIndex !== newIndex) { - this.editRows.splice(rowIndex, 1); - this.editRows.splice(newIndex, 0, editRow); - } - break; + editRow(rowIndex) { + const editRow = this.editRows[rowIndex]; + const isAlreadyEditing = + editRow.state === EditRowState.Editing || + editRow.state === EditRowState.New; + if (!editRow || isAlreadyEditing) { + return Promise.resolve(); } - case EditRowState.Editing: { - dataValue[rowIndex] = editRow.data; - break; + editRow.prevState = editRow.state; + editRow.state = this.options.readOnly + ? EditRowState.Viewing + : EditRowState.Editing; + + if (this.lazyLoad && editRow.components.length === 0) { + editRow.components = this.createRowComponents( + editRow.data, + rowIndex, + ); } - } - } - - editRow.state = this.component.rowDrafts && !isRowValid ? EditRowState.Draft : EditRowState.Saved; - editRow.backup = null; - - this.updateValue(); - this.emit('editGridSaveRow', { - component: this.component, - row: editRow.data, - instance: this - }); - this.triggerChange({ modified, noPristineChangeOnModified: modified && this.component.rowDrafts, isolateRow: true }); - if (this.component.rowDrafts) { - editRow.components.forEach(comp => comp.setPristine(this.pristine)); - } - this.checkValidity(null, true); - this.redraw(); - - if (editRow.alerts) { - editRow.alerts = false; - } - - return true; - } - - beforeFocus(component) { - if ('beforeFocus' in this.parent) { - this.parent.beforeFocus(this); - } - const relativePath = this.getRelativePath(component.path); - const arrayPath = getArrayFromComponentPath(relativePath); - - const rowIndex = arrayPath[0]; - let rowToEditIndex = arrayPath[0]; - - this.editRows.forEach((row, indexInArray) => { - if (row.rowIndex === rowIndex) { - rowToEditIndex = indexInArray; - } - }); - - if (_.isNumber(rowToEditIndex)) { - this.editRow(rowToEditIndex); - } - } - - updateComponentsRowIndex(components, rowIndex) { - components.forEach((component, colIndex) => { - component.rowIndex = rowIndex; - component.row = `${rowIndex}-${colIndex}`; - }); - } - - updateRowsComponents(rowIndex) { - this.editRows.slice(rowIndex).forEach((row, index) => { - this.updateComponentsRowIndex(row.components, rowIndex + index); - }); - } - - baseRemoveRow(rowIndex) { - const editRow = this.editRows[rowIndex]; - - editRow.state = EditRowState.Removed; - this.destroyComponents(false, rowIndex); - - return editRow; - } - - removeRow(rowIndex, modified) { - if (this.options.readOnly) { - return; - } - - this.clearErrors(rowIndex); - this.baseRemoveRow(rowIndex); - this.splice(rowIndex); - this.emit('editGridDeleteRow', { - index: rowIndex - }); - this.editRows.splice(rowIndex, 1); - this.openWhenEmpty(); - this.updateRowsComponents(rowIndex); - this.updateValue(); - this.triggerChange({ modified, noPristineChangeOnModified: modified && this.component.rowDrafts, isolateRow: true }); - this.checkValidity(null, true); - this.checkData(); - this.redraw(); - } - - createRowComponents(row, rowIndex, recreatePartially) { - // Iterate through existing components and destroy the ones with the same rowIndex. - if (this.components) { - for (let i = 0; i < this.components.length; i++) { - if (this.components[i].rowIndex === rowIndex) { - this.components[i].destroy(); - this.components.splice(i, 1); + + const dataSnapshot = fastCloneDeep(editRow.data); + + if (this.inlineEditMode) { + editRow.backup = dataSnapshot; + } else { + editRow.backup = fastCloneDeep(editRow.data); + editRow.data = dataSnapshot; + this.restoreRowContext(editRow); } - } - } - const currentRowComponents = _.get(this.editRows, `[${rowIndex}].components`, null); - return this.component.components.map((col, colIndex) => { - if (recreatePartially && currentRowComponents && this.variableTypeComponentsIndexes.length) { - const currentComp = currentRowComponents[colIndex]; - const shouldRecreate = _.includes(this.variableTypeComponentsIndexes, colIndex) && currentComp?.type !== currentComp?.component?.type; - if (!shouldRecreate) { - return currentComp; + if (this.component.modal) { + return this.addRowModal(rowIndex); } - col = currentComp.component; - } + return this.redraw(); + } + + clearErrors(rowIndex) { + const editRow = this.editRows[rowIndex]; + if (editRow && Array.isArray(editRow.components)) { + editRow.components.forEach((comp) => { + comp.setPristine(true); + comp.setCustomValidity(''); + }); + } + } - const column = _.clone(col); - const options = _.clone(this.options); - options.name += `[${rowIndex}]`; - options.row = `${rowIndex}-${colIndex}`; - options.onChange = (flags = {}, changed, modified) => { - if (changed.instance.root?.id && (this.root?.id !== changed.instance.root.id)) { - changed.instance.root.triggerChange(flags, changed, modified); + cancelRow(rowIndex) { + if (this.options.readOnly) { + return; } - else if (!this.component.modal) { - this.triggerRootChange(flags, changed, modified); + + const editRow = this.editRows[rowIndex]; + switch (editRow.state) { + case EditRowState.New: { + editRow.state = EditRowState.Removed; + + this.clearErrors(rowIndex); + this.destroyComponents(false, rowIndex); + if (this.inlineEditMode) { + this.splice(rowIndex); + } + this.editRows.splice(rowIndex, 1); + this.openWhenEmpty(); + break; + } + case EditRowState.Editing: { + editRow.state = editRow.prevState; + + if (this.inlineEditMode) { + this.dataValue[rowIndex] = editRow.backup; + } + editRow.data = editRow.backup; + editRow.backup = null; + this.restoreRowContext(editRow); + this.clearErrors(rowIndex); + break; + } } - if (this.inlineEditMode) { - return; + this.emit('editGridCancelRow', { + instance: this, + component: this.component, + editRow, + }); + + this.checkValidity(null, true); + this.redraw(); + + if (this.component.rowDrafts) { + this.checkValidity(this.data, false); } + } + saveRow(rowIndex, modified) { const editRow = this.editRows[rowIndex]; - if (editRow?.alerts) { - this.checkData(null, { - ...flags, - changed, - rowIndex, - }, this.data); + if (this.options.readOnly) { + return; + } + + // After an attempt to save, all the components inside the row should become not pristine + if (!this.component.rowDrafts) { + editRow.components.forEach((comp) => comp.setPristine(false)); } - else if (editRow) { - // If drafts allowed, perform validation silently if there was no attempt to submit a form - const silentCheck = this.component.rowDrafts && !this.shouldValidateDraft(editRow); - - this.checkRow('checkData', null, { - ...flags, - changed, - silentCheck - }, editRow.data, editRow.components, silentCheck); + + const isRowValid = this.validateRow(editRow, true); + + if (!this.component.rowDrafts) { + if (!isRowValid) { + return false; + } } - if (this.variableTypeComponentsIndexes.length) { - this.checkRowVariableTypeComponents(editRow, rowIndex); - this.redraw(); + if (this.saveEditMode) { + const dataValue = this.dataValue; + if (this.root?.focusedComponent?.component.typeChangeEnabled) { + this.root.focusedComponent = null; + } + switch (editRow.state) { + case EditRowState.New: { + const newIndex = dataValue.length; + dataValue.push(editRow.data); + editRow.components.forEach( + (component) => (component.rowIndex = newIndex), + ); + if (rowIndex !== newIndex) { + this.editRows.splice(rowIndex, 1); + this.editRows.splice(newIndex, 0, editRow); + } + break; + } + case EditRowState.Editing: { + dataValue[rowIndex] = editRow.data; + break; + } + } } - }; - - const comp = this.createComponent( - _.assign({}, column, { row: options.row }), - options, - row, - null, - recreatePartially && currentRowComponents ? currentRowComponents[colIndex] : null - ); - comp.rowIndex = rowIndex; - comp.inEditGrid = true; - return comp; - }); - } - - hasOpenRows() { - return this.editRows.some(row => this.isOpen(row)); - } - - shouldValidateDraft(editRow) { - // Draft rows should be validated only when there was an attempt to submit a form - return (editRow.state === EditRowState.Draft && - !this.pristine && - !this.root?.pristine && - !this.hasOpenRows()) || - this.root?.submitted; - } - - shouldValidateRow(editRow, dirty) { - return this.shouldValidateDraft(editRow) || - editRow.state === EditRowState.Editing || - editRow.alerts || - dirty; - } - - validateRow(editRow, dirty, forceSilentCheck) { - let valid = true; - const errorsSnapshot = [...this.errors]; - - if (this.shouldValidateRow(editRow, dirty)) { - editRow.components.forEach(comp => { - const silentCheck = (this.component.rowDrafts && !this.shouldValidateDraft(editRow)) || forceSilentCheck; - - valid &= comp.checkValidity(null, dirty, null, silentCheck); - }); - } - - if (this.component.validate && this.component.validate.row) { - valid = this.evaluate(this.component.validate.row, { - valid, - row: editRow.data - }, 'valid', true); - if (valid.toString() !== 'true') { - editRow.error = valid; - valid = false; - } - else { - editRow.error = null; - } - if (valid === null) { - valid = `Invalid row validation for ${this.key}`; - } - } - - editRow.errors = !valid ? this.errors.filter((err) => !errorsSnapshot.includes(err)) : null; - - if (!this.component.rowDrafts || this.root?.submitted) { - this.showRowErrorAlerts(editRow, !!valid); - } - - return !!valid; - } - - showRowErrorAlerts(editRow, valid) { - if (editRow.alerts) { - if (this.alert) { - if (editRow.errors?.length && !valid) { - this.alert.showErrors(editRow.errors, false); - editRow.alerts = true; + + editRow.state = + this.component.rowDrafts && !isRowValid + ? EditRowState.Draft + : EditRowState.Saved; + editRow.backup = null; + + this.updateValue(); + this.emit('editGridSaveRow', { + component: this.component, + row: editRow.data, + instance: this, + }); + this.triggerChange({ + modified, + noPristineChangeOnModified: modified && this.component.rowDrafts, + isolateRow: true, + }); + if (this.component.rowDrafts) { + editRow.components.forEach((comp) => + comp.setPristine(this.pristine), + ); + } + this.checkValidity(null, true); + this.redraw(); + + if (editRow.alerts) { + editRow.alerts = false; + } + + return true; + } + + beforeFocus(component) { + if ('beforeFocus' in this.parent) { + this.parent.beforeFocus(this); } - else { - this.alert.clear(); + const relativePath = this.getRelativePath(component.path); + const arrayPath = getArrayFromComponentPath(relativePath); + + const rowIndex = arrayPath[0]; + let rowToEditIndex = arrayPath[0]; + + this.editRows.forEach((row, indexInArray) => { + if (row.rowIndex === rowIndex) { + rowToEditIndex = indexInArray; + } + }); + + if (_.isNumber(rowToEditIndex)) { + this.editRow(rowToEditIndex); } - } } - } - checkValidity(data, dirty, row, silentCheck) { - data = data || this.rootValue; - row = row || this.data; + updateComponentsRowIndex(components, rowIndex) { + components.forEach((component, colIndex) => { + component.rowIndex = rowIndex; + component.row = `${rowIndex}-${colIndex}`; + }); + } - if (!this.checkCondition(row, data)) { - this.setCustomValidity(''); - return true; + updateRowsComponents(rowIndex) { + this.editRows.slice(rowIndex).forEach((row, index) => { + this.updateComponentsRowIndex(row.components, rowIndex + index); + }); } - return this.checkComponentValidity(data, dirty, row, { silentCheck }); - } + baseRemoveRow(rowIndex) { + const editRow = this.editRows[rowIndex]; - checkComponentValidity(data, dirty, row, options = {}) { - const { silentCheck } = options; - const errorsLength = this.errors.length; - const superValid = super.checkComponentValidity(data, dirty, row, options); + editRow.state = EditRowState.Removed; + this.destroyComponents(false, rowIndex); - // If super tells us that component invalid and there is no need to update alerts, just return false - if (!superValid && (!this.alert && !this.hasOpenRows())) { - return false; + return editRow; } - if (this.shouldSkipValidation(data, dirty, row)) { - return true; + removeRow(rowIndex, modified) { + if (this.options.readOnly) { + return; + } + + this.clearErrors(rowIndex); + this.baseRemoveRow(rowIndex); + this.splice(rowIndex); + this.emit('editGridDeleteRow', { + index: rowIndex, + }); + this.editRows.splice(rowIndex, 1); + this.openWhenEmpty(); + this.updateRowsComponents(rowIndex); + this.updateValue(); + this.triggerChange({ + modified, + noPristineChangeOnModified: modified && this.component.rowDrafts, + isolateRow: true, + }); + this.checkValidity(null, true); + this.checkData(); + this.redraw(); + } + + createRowComponents(row, rowIndex, recreatePartially) { + // Iterate through existing components and destroy the ones with the same rowIndex. + if (this.components) { + for (let i = 0; i < this.components.length; i++) { + if (this.components[i].rowIndex === rowIndex) { + this.components[i].destroy(); + this.components.splice(i, 1); + } + } + } + const currentRowComponents = _.get( + this.editRows, + `[${rowIndex}].components`, + null, + ); + return this.component.components.map((col, colIndex) => { + if ( + recreatePartially && + currentRowComponents && + this.variableTypeComponentsIndexes.length + ) { + const currentComp = currentRowComponents[colIndex]; + const shouldRecreate = + _.includes(this.variableTypeComponentsIndexes, colIndex) && + currentComp?.type !== currentComp?.component?.type; + + if (!shouldRecreate) { + return currentComp; + } + + col = currentComp.component; + } + + const column = _.clone(col); + const options = _.clone(this.options); + options.name += `[${rowIndex}]`; + options.row = `${rowIndex}-${colIndex}`; + options.onChange = (flags = {}, changed, modified) => { + if ( + changed.instance.root?.id && + this.root?.id !== changed.instance.root.id + ) { + changed.instance.root.triggerChange( + flags, + changed, + modified, + ); + } else if (!this.component.modal) { + this.triggerRootChange(flags, changed, modified); + } + + if (this.inlineEditMode) { + return; + } + + const editRow = this.editRows[rowIndex]; + + if (editRow?.alerts) { + this.checkData( + null, + { + ...flags, + changed, + rowIndex, + }, + this.data, + ); + } else if (editRow) { + // If drafts allowed, perform validation silently if there was no attempt to submit a form + const silentCheck = + this.component.rowDrafts && + !this.shouldValidateDraft(editRow); + + this.checkRow( + 'checkData', + null, + { + ...flags, + changed, + silentCheck, + }, + editRow.data, + editRow.components, + silentCheck, + ); + } + + if (this.variableTypeComponentsIndexes.length) { + this.checkRowVariableTypeComponents(editRow, rowIndex); + this.redraw(); + } + }; + + const comp = this.createComponent( + _.assign({}, column, { row: options.row }), + options, + row, + null, + recreatePartially && currentRowComponents + ? currentRowComponents[colIndex] + : null, + ); + comp.rowIndex = rowIndex; + comp.inEditGrid = true; + return comp; + }); + } + + hasOpenRows() { + return this.editRows.some((row) => this.isOpen(row)); + } + + shouldValidateDraft(editRow) { + // Draft rows should be validated only when there was an attempt to submit a form + return ( + (editRow.state === EditRowState.Draft && + !this.pristine && + !this.root?.pristine && + !this.hasOpenRows()) || + this.root?.submitted + ); } - let rowsValid = true; - let rowsEditing = false; + shouldValidateRow(editRow, dirty) { + return ( + this.shouldValidateDraft(editRow) || + editRow.state === EditRowState.Editing || + editRow.alerts || + dirty + ); + } - this.editRows.forEach((editRow, index) => { - // Trigger all errors on the row. - const rowValid = this.validateRow(editRow, dirty, silentCheck); + validateRow(editRow, dirty, forceSilentCheck) { + let valid = true; + const errorsSnapshot = [...this.errors]; - rowsValid &= rowValid; + if (this.shouldValidateRow(editRow, dirty)) { + editRow.components.forEach((comp) => { + const silentCheck = + (this.component.rowDrafts && + !this.shouldValidateDraft(editRow)) || + forceSilentCheck; - if (this.rowRefs) { - const rowContainer = this.rowRefs[index]; + valid &= comp.checkValidity(null, dirty, null, silentCheck); + }); + } + + if (this.component.validate && this.component.validate.row) { + valid = this.evaluate( + this.component.validate.row, + { + valid, + row: editRow.data, + }, + 'valid', + true, + ); + if (valid.toString() !== 'true') { + editRow.error = valid; + valid = false; + } else { + editRow.error = null; + } + if (valid === null) { + valid = `Invalid row validation for ${this.key}`; + } + } - if (rowContainer) { - const errorContainer = rowContainer.querySelector('.editgrid-row-error'); + editRow.errors = !valid + ? this.errors.filter((err) => !errorsSnapshot.includes(err)) + : null; - if (!rowValid && errorContainer && (!this.component.rowDrafts || this.shouldValidateDraft(editRow))) { - this.addClass(errorContainer, 'help-block' ); - errorContainer.textContent = this.t(this.errorMessage('invalidRowError')); - } - else if (errorContainer) { - errorContainer.textContent = ''; - } + if (!this.component.rowDrafts || this.root?.submitted) { + this.showRowErrorAlerts(editRow, !!valid); } - } - // If this is a dirty check, and any rows are still editing, we need to throw validation error. - rowsEditing |= (dirty && this.isOpen(editRow)); - }); - - if (!rowsValid) { - if (!silentCheck && (!this.component.rowDrafts || this.root?.submitted)) { - this.setCustomValidity(this.t(this.errorMessage('invalidRowsError')), dirty); - // Delete this class, because otherwise all the components inside EditGrid will has red border even if they are valid - this.removeClass(this.element, 'has-error'); - } - return false; - } - else if (rowsEditing && this.saveEditMode && !this.component.openWhenEmpty) { - this.setCustomValidity(this.t(this.errorMessage('unsavedRowsError')), dirty); - return false; - } - - const message = this.invalid || this.invalidMessage(data, dirty); - if (this.errors?.length !== errorsLength && this.root?.submitted && !message) { - this.setCustomValidity(message, dirty); - this.root.showErrors(); - } - else { - this.setCustomValidity(message, dirty); - } - return superValid; - } - - changeState(changed, flags) { - if (this.visible && (changed || (flags.resetValue && this.component.modalEdit))) { - this.rebuild(); - } - else { - this.redraw(); - } - } - - setValue(value, flags = {}) { - if (!value) { - value = this.defaultValue; - } - - if (!Array.isArray(value)) { - if (typeof value === 'object') { - value = [value]; - } - else { - return false; - } - } - - const changed = this.hasChanged(value, this.dataValue); - flags.noValidate = !changed; - if (this.parent && !this.options.server) { - this.parent.checkComponentConditions(); - } - this.dataValue = value; - // Refresh editRow data when data changes. - this.dataValue.forEach((row, rowIndex) => { - const editRow = this.editRows[rowIndex]; - if (editRow) { - editRow.data = row; - this.restoreRowContext(editRow, flags); - editRow.state = EditRowState.Saved; - editRow.backup = null; - editRow.error = null; - } - else { - this.editRows[rowIndex] = { - components: this.lazyLoad ? [] : this.createRowComponents(row, rowIndex), - data: row, - state: EditRowState.Saved, - backup: null, - error: null, - }; - } - }); - let { length: dataLength } = this.dataValue; - // If the last row is a new row, then do not remove it. - if (this.editRows[dataLength] && (this.editRows[dataLength].state === EditRowState.New)) { - dataLength = (dataLength + 1); + return !!valid; } - this.editRows.slice(dataLength).forEach((editRow, index) => this.baseRemoveRow(dataLength + index)); - this.editRows = this.editRows.slice(0, dataLength); - this.openWhenEmpty(); - this.updateOnChange(flags, changed); - // do not call checkData with server option, it is called when change is triggered in updateOnChange - if (!this.options.server) { - this.checkData(); + showRowErrorAlerts(editRow, valid) { + if (editRow.alerts) { + if (this.alert) { + if (editRow.errors?.length && !valid) { + this.alert.showErrors(editRow.errors, false); + editRow.alerts = true; + } else { + this.alert.clear(); + } + } + } } - this.changeState(changed, flags); + checkValidity(data, dirty, row, silentCheck) { + data = data || this.rootValue; + row = row || this.data; - return changed; - } + if (!this.checkCondition(row, data)) { + this.setCustomValidity(''); + return true; + } - openWhenEmpty() { - const shouldBeOpened = !this.dataValue.length && this.component.openWhenEmpty; - const hasNoRows = !this.editRows.length; + return this.checkComponentValidity(data, dirty, row, { silentCheck }); + } + + checkComponentValidity(data, dirty, row, options = {}) { + const { silentCheck } = options; + const errorsLength = this.errors.length; + const superValid = super.checkComponentValidity( + data, + dirty, + row, + options, + ); + + // If super tells us that component invalid and there is no need to update alerts, just return false + if (!superValid && !this.alert && !this.hasOpenRows()) { + return false; + } - if (hasNoRows && shouldBeOpened && !this.builderMode) { - const dataObj = {}; - this.createRow(dataObj, 0); + if (this.shouldSkipValidation(data, dirty, row)) { + return true; + } + + let rowsValid = true; + let rowsEditing = false; + + this.editRows.forEach((editRow, index) => { + // Trigger all errors on the row. + const rowValid = this.validateRow(editRow, dirty, silentCheck); + + rowsValid &= rowValid; + + if (this.rowRefs) { + const rowContainer = this.rowRefs[index]; + + if (rowContainer) { + const errorContainer = rowContainer.querySelector( + '.editgrid-row-error', + ); + + if ( + !rowValid && + errorContainer && + (!this.component.rowDrafts || + this.shouldValidateDraft(editRow)) + ) { + this.addClass(errorContainer, 'help-block'); + errorContainer.textContent = this.t( + this.errorMessage('invalidRowError'), + ); + } else if (errorContainer) { + errorContainer.textContent = ''; + } + } + } + // If this is a dirty check, and any rows are still editing, we need to throw validation error. + rowsEditing |= dirty && this.isOpen(editRow); + }); + + if (!rowsValid) { + if ( + !silentCheck && + (!this.component.rowDrafts || this.root?.submitted) + ) { + this.setCustomValidity( + this.t(this.errorMessage('invalidRowsError')), + dirty, + ); + // Delete this class, because otherwise all the components inside EditGrid will has red border even if they are valid + this.removeClass(this.element, 'has-error'); + } + return false; + } else if ( + rowsEditing && + this.saveEditMode && + !this.component.openWhenEmpty + ) { + this.setCustomValidity( + this.t(this.errorMessage('unsavedRowsError')), + dirty, + ); + return false; + } + + const message = this.invalid || this.invalidMessage(data, dirty); + if ( + this.errors?.length !== errorsLength && + this.root?.submitted && + !message + ) { + this.setCustomValidity(message, dirty); + this.root.showErrors(); + } else { + this.setCustomValidity(message, dirty); + } + return superValid; } - } - restoreRowContext(editRow, flags = {}) { - editRow.components.forEach((component) => { - component.data = editRow.data; - this.setNestedValue(component, editRow.data, flags); - }); - } + changeState(changed, flags) { + if ( + this.visible && + (changed || (flags.resetValue && this.component.modalEdit)) + ) { + this.rebuild(); + } else { + this.redraw(); + } + } - emptyRows() { - this.editRows.forEach((editRow, index) => this.destroyComponents(false, index)); - this.editRows = []; - } + setValue(value, flags = {}) { + if (!value) { + value = this.defaultValue; + } - resetValue() { - super.resetValue(); - this.emptyRows(); - } + if (!Array.isArray(value)) { + if (typeof value === 'object') { + value = [value]; + } else { + return false; + } + } + + const changed = this.hasChanged(value, this.dataValue); + flags.noValidate = !changed; + if (this.parent && !this.options.server) { + this.parent.checkComponentConditions(); + } + this.dataValue = value; + // Refresh editRow data when data changes. + this.dataValue.forEach((row, rowIndex) => { + const editRow = this.editRows[rowIndex]; + if (editRow) { + editRow.data = row; + this.restoreRowContext(editRow, flags); + editRow.state = EditRowState.Saved; + editRow.backup = null; + editRow.error = null; + } else { + this.editRows[rowIndex] = { + components: this.lazyLoad + ? [] + : this.createRowComponents(row, rowIndex), + data: row, + state: EditRowState.Saved, + backup: null, + error: null, + }; + } + }); + let { length: dataLength } = this.dataValue; + + // If the last row is a new row, then do not remove it. + if ( + this.editRows[dataLength] && + this.editRows[dataLength].state === EditRowState.New + ) { + dataLength = dataLength + 1; + } + this.editRows + .slice(dataLength) + .forEach((editRow, index) => + this.baseRemoveRow(dataLength + index), + ); + this.editRows = this.editRows.slice(0, dataLength); + + this.openWhenEmpty(); + this.updateOnChange(flags, changed); + // do not call checkData with server option, it is called when change is triggered in updateOnChange + if (!this.options.server) { + this.checkData(); + } + + this.changeState(changed, flags); + + return changed; + } + + openWhenEmpty() { + const shouldBeOpened = + !this.dataValue.length && this.component.openWhenEmpty; + const hasNoRows = !this.editRows.length; + + if (hasNoRows && shouldBeOpened && !this.builderMode) { + const dataObj = {}; + this.createRow(dataObj, 0); + } + } + + restoreRowContext(editRow, flags = {}) { + editRow.components.forEach((component) => { + component.data = editRow.data; + this.setNestedValue(component, editRow.data, flags); + }); + } + + emptyRows() { + this.editRows.forEach((editRow, index) => + this.destroyComponents(false, index), + ); + this.editRows = []; + } + + resetValue() { + super.resetValue(); + this.emptyRows(); + } } EditGridComponent.prototype.hasChanged = Component.prototype.hasChanged; diff --git a/src/components/editgrid/EditGrid.unit.js b/src/components/editgrid/EditGrid.unit.js index 363dca2c6f..12076cd75a 100644 --- a/src/components/editgrid/EditGrid.unit.js +++ b/src/components/editgrid/EditGrid.unit.js @@ -3,23 +3,23 @@ import _ from 'lodash'; import Harness from '../../../test/harness'; import EditGridComponent from './EditGrid'; import { - comp1, - comp4, - comp3, - comp5, - comp6, - comp7, - comp8, - comp9, - comp10, - comp11, - comp12, - comp13, - comp14, - comp15, - withOpenWhenEmptyAndConditions, - compOpenWhenEmpty, - compWithCustomDefaultValue, + comp1, + comp4, + comp3, + comp5, + comp6, + comp7, + comp8, + comp9, + comp10, + comp11, + comp12, + comp13, + comp14, + comp15, + withOpenWhenEmptyAndConditions, + compOpenWhenEmpty, + compWithCustomDefaultValue, } from './fixtures'; import formsWithEditGridAndConditions from './fixtures/formsWithEditGridAndConditions'; @@ -29,1542 +29,2651 @@ import Webform from '../../Webform'; import { displayAsModalEditGrid } from '../../../test/formtest'; import { Formio } from '../../Formio'; -describe('EditGrid Component', function() { - it('Should set correct values in dataMap inside editGrid and allow aditing them', function(done) { - Harness.testCreate(EditGridComponent, comp4).then((component) => { - component.setValue([{ dataMap: { key111: '111' } }]); +describe('EditGrid Component', function () { + it('Should set correct values in dataMap inside editGrid and allow aditing them', function (done) { + Harness.testCreate(EditGridComponent, comp4).then((component) => { + component.setValue([{ dataMap: { key111: '111' } }]); - setTimeout(()=>{ - const clickEvent = new Event('click'); - const editBtn = component.element.querySelector('.editRow'); - - editBtn.dispatchEvent(clickEvent); + setTimeout(() => { + const clickEvent = new Event('click'); + const editBtn = component.element.querySelector('.editRow'); - setTimeout(()=>{ - const keyValue = component.element.querySelectorAll('[ref="input"]')[0].value; - const valueValue = component.element.querySelectorAll('[ref="input"]')[1].value; - const saveBtnsQty = component.element.querySelectorAll('[ref="editgrid-editGrid-saveRow"]').length; + editBtn.dispatchEvent(clickEvent); - assert.equal(saveBtnsQty, 1); - assert.equal(keyValue, 'key111'); - assert.equal(valueValue, '111'); - done(); - }, 500); - }, 200); + setTimeout(() => { + const keyValue = + component.element.querySelectorAll('[ref="input"]')[0] + .value; + const valueValue = + component.element.querySelectorAll('[ref="input"]')[1] + .value; + const saveBtnsQty = component.element.querySelectorAll( + '[ref="editgrid-editGrid-saveRow"]', + ).length; + + assert.equal(saveBtnsQty, 1); + assert.equal(keyValue, 'key111'); + assert.equal(valueValue, '111'); + done(); + }, 500); + }, 200); + }); }); - }); - - it('Should set correct values after reset', function(done) { - Harness.testCreate(EditGridComponent, comp5) - .then((component) => { - assert.equal(component.components.length, 0); - - component.setValue([ - { textField: 'textField1' }, - { textField: 'textField2' } - ], { resetValue: true }); - - setTimeout(() => { - assert.equal(component.components.length, 2); - done(); - }, 300); - }); - }); - - it('Should display saved values if there are more then 1 nested components', function(done) { - Harness.testCreate(EditGridComponent, comp3).then((component) => { - component.setValue([{ container: { number: 55555 } }, { container: { number: 666666 } }]); - - setTimeout(()=>{ - const firstValue = component.element.querySelectorAll('[ref="editgrid-editGrid-row"]')[0].querySelector('.col-sm-2').textContent.trim(); - const secondValue = component.element.querySelectorAll('[ref="editgrid-editGrid-row"]')[1].querySelector('.col-sm-2').textContent.trim(); - - assert.equal(firstValue, '55555'); - assert.equal(secondValue, '666666'); - done(); - }, 600); + + it('Should set correct values after reset', function (done) { + Harness.testCreate(EditGridComponent, comp5).then((component) => { + assert.equal(component.components.length, 0); + + component.setValue( + [{ textField: 'textField1' }, { textField: 'textField2' }], + { resetValue: true }, + ); + + setTimeout(() => { + assert.equal(component.components.length, 2); + done(); + }, 300); + }); }); - }); - - it('Should build an empty edit grid component', function() { - return Harness.testCreate(EditGridComponent, comp1).then((component) => { - Harness.testInnerHtml(component, 'li.list-group-header div.row div:nth-child(1)', 'Field 1'); - Harness.testInnerHtml(component, 'li.list-group-header div.row div:nth-child(2)', 'Field 2'); - Harness.testInnerHtml(component, 'li.list-group-header div.row div:nth-child(3)', '0'); - Harness.testElements(component, 'li.list-group-header', 1); - Harness.testElements(component, 'li.list-group-item', 1); - Harness.testElements(component, 'li.list-group-footer', 0); - Harness.testElements(component, 'div.editRow', 0); - Harness.testElements(component, 'div.removeRow', 0); - assert.equal(component.refs[`${component.editgridKey}-addRow`].length, 1); - assert(component.checkValidity(component.getValue()), 'Item should be valid'); + + it('Should display saved values if there are more then 1 nested components', function (done) { + Harness.testCreate(EditGridComponent, comp3).then((component) => { + component.setValue([ + { container: { number: 55555 } }, + { container: { number: 666666 } }, + ]); + + setTimeout(() => { + const firstValue = component.element + .querySelectorAll('[ref="editgrid-editGrid-row"]')[0] + .querySelector('.col-sm-2') + .textContent.trim(); + const secondValue = component.element + .querySelectorAll('[ref="editgrid-editGrid-row"]')[1] + .querySelector('.col-sm-2') + .textContent.trim(); + + assert.equal(firstValue, '55555'); + assert.equal(secondValue, '666666'); + done(); + }, 600); + }); }); - }); - - it('Should build an edit grid component', function() { - return Harness.testCreate(EditGridComponent, comp1).then((component) => { - Harness.testInnerHtml(component, 'li.list-group-header div.row div:nth-child(1)', 'Field 1'); - Harness.testInnerHtml(component, 'li.list-group-header div.row div:nth-child(2)', 'Field 2'); - Harness.testInnerHtml(component, 'li.list-group-header div.row div:nth-child(3)', '0'); - Harness.testSetGet(component, [ - { - field1: 'good', - field2: 'foo' - }, - { - field1: 'good', - field2: 'bar' - } - ]); - Harness.testElements(component, 'li.list-group-header', 1); - Harness.testElements(component, 'li.list-group-item', 3); - Harness.testElements(component, 'li.list-group-footer', 0); - Harness.testElements(component, 'div.editRow', 2); - Harness.testElements(component, 'div.removeRow', 2); - assert.equal(component.refs[`${component.editgridKey}-addRow`].length, 1); - Harness.testInnerHtml(component, 'li.list-group-header div.row div:nth-child(3)', '2'); - Harness.testInnerHtml(component, 'li.list-group-item:nth-child(2) div.row div:nth-child(1)', 'good'); - Harness.testInnerHtml(component, 'li.list-group-item:nth-child(2) div.row div:nth-child(2)', 'foo'); - Harness.testInnerHtml(component, 'li.list-group-item:nth-child(3) div.row div:nth-child(1)', 'good'); - Harness.testInnerHtml(component, 'li.list-group-item:nth-child(3) div.row div:nth-child(2)', 'bar'); - assert(component.checkValidity(component.getValue()), 'Item should be valid'); + + it('Should build an empty edit grid component', function () { + return Harness.testCreate(EditGridComponent, comp1).then( + (component) => { + Harness.testInnerHtml( + component, + 'li.list-group-header div.row div:nth-child(1)', + 'Field 1', + ); + Harness.testInnerHtml( + component, + 'li.list-group-header div.row div:nth-child(2)', + 'Field 2', + ); + Harness.testInnerHtml( + component, + 'li.list-group-header div.row div:nth-child(3)', + '0', + ); + Harness.testElements(component, 'li.list-group-header', 1); + Harness.testElements(component, 'li.list-group-item', 1); + Harness.testElements(component, 'li.list-group-footer', 0); + Harness.testElements(component, 'div.editRow', 0); + Harness.testElements(component, 'div.removeRow', 0); + assert.equal( + component.refs[`${component.editgridKey}-addRow`].length, + 1, + ); + assert( + component.checkValidity(component.getValue()), + 'Item should be valid', + ); + }, + ); }); - }); - - it('Should add a row when add another is clicked', function() { - return Harness.testCreate(EditGridComponent, comp1).then((component) => { - Harness.testElements(component, 'li.list-group-item', 1); - Harness.testInnerHtml(component, 'li.list-group-header div.row div:nth-child(3)', '0'); - Harness.clickElement(component, component.refs[`${component.editgridKey}-addRow`][0]); - Harness.testElements(component, 'li.list-group-item', 2); - Harness.testInnerHtml(component, 'li.list-group-header div.row div:nth-child(3)', '0'); - Harness.clickElement(component, component.refs[`${component.editgridKey}-addRow`][0]); - Harness.testElements(component, 'li.list-group-item', 3); - Harness.testInnerHtml(component, 'li.list-group-header div.row div:nth-child(3)', '0'); - assert(!component.checkValidity(component.getValue(), true), 'Item should not be valid'); + + it('Should build an edit grid component', function () { + return Harness.testCreate(EditGridComponent, comp1).then( + (component) => { + Harness.testInnerHtml( + component, + 'li.list-group-header div.row div:nth-child(1)', + 'Field 1', + ); + Harness.testInnerHtml( + component, + 'li.list-group-header div.row div:nth-child(2)', + 'Field 2', + ); + Harness.testInnerHtml( + component, + 'li.list-group-header div.row div:nth-child(3)', + '0', + ); + Harness.testSetGet(component, [ + { + field1: 'good', + field2: 'foo', + }, + { + field1: 'good', + field2: 'bar', + }, + ]); + Harness.testElements(component, 'li.list-group-header', 1); + Harness.testElements(component, 'li.list-group-item', 3); + Harness.testElements(component, 'li.list-group-footer', 0); + Harness.testElements(component, 'div.editRow', 2); + Harness.testElements(component, 'div.removeRow', 2); + assert.equal( + component.refs[`${component.editgridKey}-addRow`].length, + 1, + ); + Harness.testInnerHtml( + component, + 'li.list-group-header div.row div:nth-child(3)', + '2', + ); + Harness.testInnerHtml( + component, + 'li.list-group-item:nth-child(2) div.row div:nth-child(1)', + 'good', + ); + Harness.testInnerHtml( + component, + 'li.list-group-item:nth-child(2) div.row div:nth-child(2)', + 'foo', + ); + Harness.testInnerHtml( + component, + 'li.list-group-item:nth-child(3) div.row div:nth-child(1)', + 'good', + ); + Harness.testInnerHtml( + component, + 'li.list-group-item:nth-child(3) div.row div:nth-child(2)', + 'bar', + ); + assert( + component.checkValidity(component.getValue()), + 'Item should be valid', + ); + }, + ); }); - }); - - it('Should save a new row when save is clicked', function() { - return Harness.testCreate(EditGridComponent, comp1).then((component) => { - component.pristine = false; - Harness.testSetGet(component, [ - { - field1: 'good', - field2: 'foo' - }, - { - field1: 'good', - field2: 'bar' - } - ]); - Harness.testElements(component, 'li.list-group-item', 3); - Harness.testInnerHtml(component, 'li.list-group-header div.row div:nth-child(3)', '2'); - Harness.clickElement(component, component.refs[`${component.editgridKey}-addRow`][0]); - Harness.testElements(component, 'li.list-group-item', 4); - Harness.testInnerHtml(component, 'li.list-group-header div.row div:nth-child(3)', '2'); - Harness.setInputValue(component, 'data[editgrid][2][field1]', 'good'); - Harness.setInputValue(component, 'data[editgrid][2][field2]', 'baz'); - Harness.clickElement(component, 'div.editgrid-actions button.btn-primary'); - Harness.testElements(component, 'li.list-group-item', 4); - Harness.testInnerHtml(component, 'li.list-group-header div.row div:nth-child(3)', '3'); - Harness.testInnerHtml(component, 'li.list-group-item:nth-child(4) div.row div:nth-child(1)', 'good'); - Harness.testInnerHtml(component, 'li.list-group-item:nth-child(4) div.row div:nth-child(2)', 'baz'); - assert(component.checkValidity(component.getValue()), 'Item should be valid'); + + it('Should add a row when add another is clicked', function () { + return Harness.testCreate(EditGridComponent, comp1).then( + (component) => { + Harness.testElements(component, 'li.list-group-item', 1); + Harness.testInnerHtml( + component, + 'li.list-group-header div.row div:nth-child(3)', + '0', + ); + Harness.clickElement( + component, + component.refs[`${component.editgridKey}-addRow`][0], + ); + Harness.testElements(component, 'li.list-group-item', 2); + Harness.testInnerHtml( + component, + 'li.list-group-header div.row div:nth-child(3)', + '0', + ); + Harness.clickElement( + component, + component.refs[`${component.editgridKey}-addRow`][0], + ); + Harness.testElements(component, 'li.list-group-item', 3); + Harness.testInnerHtml( + component, + 'li.list-group-header div.row div:nth-child(3)', + '0', + ); + assert( + !component.checkValidity(component.getValue(), true), + 'Item should not be valid', + ); + }, + ); }); - }); - - it('Should cancel add a row when cancel is clicked', function() { - return Harness.testCreate(EditGridComponent, comp1).then((component) => { - Harness.testSetGet(component, [ - { - field1: 'good', - field2: 'foo' - }, - { - field1: 'good', - field2: 'bar' - } - ]); - Harness.testElements(component, 'li.list-group-item', 3); - Harness.testInnerHtml(component, 'li.list-group-header div.row div:nth-child(3)', '2'); - Harness.clickElement(component, component.refs[`${component.editgridKey}-addRow`][0]); - Harness.testElements(component, 'li.list-group-item', 4); - Harness.testInnerHtml(component, 'li.list-group-header div.row div:nth-child(3)', '2'); - Harness.setInputValue(component, 'data[editgrid][2][field1]', 'good'); - Harness.setInputValue(component, 'data[editgrid][2][field2]', 'baz'); - Harness.clickElement(component, 'div.editgrid-actions button.btn-danger'); - Harness.testElements(component, 'li.list-group-item', 3); - Harness.testInnerHtml(component, 'li.list-group-header div.row div:nth-child(3)', '2'); - assert.equal(component.editRows.length, 2); - assert(component.checkValidity(component.getValue(), true), 'Item should be valid'); + + it('Should save a new row when save is clicked', function () { + return Harness.testCreate(EditGridComponent, comp1).then( + (component) => { + component.pristine = false; + Harness.testSetGet(component, [ + { + field1: 'good', + field2: 'foo', + }, + { + field1: 'good', + field2: 'bar', + }, + ]); + Harness.testElements(component, 'li.list-group-item', 3); + Harness.testInnerHtml( + component, + 'li.list-group-header div.row div:nth-child(3)', + '2', + ); + Harness.clickElement( + component, + component.refs[`${component.editgridKey}-addRow`][0], + ); + Harness.testElements(component, 'li.list-group-item', 4); + Harness.testInnerHtml( + component, + 'li.list-group-header div.row div:nth-child(3)', + '2', + ); + Harness.setInputValue( + component, + 'data[editgrid][2][field1]', + 'good', + ); + Harness.setInputValue( + component, + 'data[editgrid][2][field2]', + 'baz', + ); + Harness.clickElement( + component, + 'div.editgrid-actions button.btn-primary', + ); + Harness.testElements(component, 'li.list-group-item', 4); + Harness.testInnerHtml( + component, + 'li.list-group-header div.row div:nth-child(3)', + '3', + ); + Harness.testInnerHtml( + component, + 'li.list-group-item:nth-child(4) div.row div:nth-child(1)', + 'good', + ); + Harness.testInnerHtml( + component, + 'li.list-group-item:nth-child(4) div.row div:nth-child(2)', + 'baz', + ); + assert( + component.checkValidity(component.getValue()), + 'Item should be valid', + ); + }, + ); }); - }); - - it('Should delete a row when delete is clicked', function() { - return Harness.testCreate(EditGridComponent, comp1).then((component) => { - Harness.testSetGet(component, [ - { - field1: 'good', - field2: 'foo' - }, - { - field1: 'good', - field2: 'bar' - }, - { - field1: 'good', - field2: 'baz' - } - ]); - Harness.testInnerHtml(component, 'li.list-group-header div.row div:nth-child(3)', '3'); - Harness.clickElement(component, 'li.list-group-item:nth-child(3) div.removeRow'); - Harness.testInnerHtml(component, 'li.list-group-header div.row div:nth-child(3)', '2'); - Harness.testInnerHtml(component, 'li.list-group-item:nth-child(2) div.row div:nth-child(1)', 'good'); - Harness.testInnerHtml(component, 'li.list-group-item:nth-child(2) div.row div:nth-child(2)', 'foo'); - Harness.testInnerHtml(component, 'li.list-group-item:nth-child(3) div.row div:nth-child(1)', 'good'); - Harness.testInnerHtml(component, 'li.list-group-item:nth-child(3) div.row div:nth-child(2)', 'baz'); - assert(component.checkValidity(component.getValue(), true), 'Item should be valid'); + + it('Should cancel add a row when cancel is clicked', function () { + return Harness.testCreate(EditGridComponent, comp1).then( + (component) => { + Harness.testSetGet(component, [ + { + field1: 'good', + field2: 'foo', + }, + { + field1: 'good', + field2: 'bar', + }, + ]); + Harness.testElements(component, 'li.list-group-item', 3); + Harness.testInnerHtml( + component, + 'li.list-group-header div.row div:nth-child(3)', + '2', + ); + Harness.clickElement( + component, + component.refs[`${component.editgridKey}-addRow`][0], + ); + Harness.testElements(component, 'li.list-group-item', 4); + Harness.testInnerHtml( + component, + 'li.list-group-header div.row div:nth-child(3)', + '2', + ); + Harness.setInputValue( + component, + 'data[editgrid][2][field1]', + 'good', + ); + Harness.setInputValue( + component, + 'data[editgrid][2][field2]', + 'baz', + ); + Harness.clickElement( + component, + 'div.editgrid-actions button.btn-danger', + ); + Harness.testElements(component, 'li.list-group-item', 3); + Harness.testInnerHtml( + component, + 'li.list-group-header div.row div:nth-child(3)', + '2', + ); + assert.equal(component.editRows.length, 2); + assert( + component.checkValidity(component.getValue(), true), + 'Item should be valid', + ); + }, + ); }); - }); - - it('Should edit a row when edit is clicked', function() { - return Harness.testCreate(EditGridComponent, comp1).then((component) => { - Harness.testSetGet(component, [ - { - field1: 'good', - field2: 'foo' - }, - { - field1: 'good', - field2: 'bar' - } - ]); - Harness.clickElement(component, 'li.list-group-item:nth-child(3) div.editRow'); - Harness.getInputValue(component, 'data[editgrid][1][field1]', 'good'); - Harness.getInputValue(component, 'data[editgrid][1][field2]', 'bar'); - Harness.testElements(component, 'div.editgrid-actions button.btn-primary', 1); - Harness.testElements(component, 'div.editgrid-actions button.btn-danger', 1); - assert(!component.checkValidity(component.getValue(), true), 'Item should not be valid'); + + it('Should delete a row when delete is clicked', function () { + return Harness.testCreate(EditGridComponent, comp1).then( + (component) => { + Harness.testSetGet(component, [ + { + field1: 'good', + field2: 'foo', + }, + { + field1: 'good', + field2: 'bar', + }, + { + field1: 'good', + field2: 'baz', + }, + ]); + Harness.testInnerHtml( + component, + 'li.list-group-header div.row div:nth-child(3)', + '3', + ); + Harness.clickElement( + component, + 'li.list-group-item:nth-child(3) div.removeRow', + ); + Harness.testInnerHtml( + component, + 'li.list-group-header div.row div:nth-child(3)', + '2', + ); + Harness.testInnerHtml( + component, + 'li.list-group-item:nth-child(2) div.row div:nth-child(1)', + 'good', + ); + Harness.testInnerHtml( + component, + 'li.list-group-item:nth-child(2) div.row div:nth-child(2)', + 'foo', + ); + Harness.testInnerHtml( + component, + 'li.list-group-item:nth-child(3) div.row div:nth-child(1)', + 'good', + ); + Harness.testInnerHtml( + component, + 'li.list-group-item:nth-child(3) div.row div:nth-child(2)', + 'baz', + ); + assert( + component.checkValidity(component.getValue(), true), + 'Item should be valid', + ); + }, + ); }); - }); - - it('Should save a row when save is clicked', function() { - return Harness.testCreate(EditGridComponent, comp1).then((component) => { - Harness.testSetGet(component, [ - { - field1: 'good', - field2: 'foo' - }, - { - field1: 'good', - field2: 'bar' - } - ]); - Harness.clickElement(component, 'li.list-group-item:nth-child(3) div.editRow'); - Harness.setInputValue(component, 'data[editgrid][1][field2]', 'baz'); - Harness.clickElement(component, 'div.editgrid-actions button.btn-primary'); - Harness.testElements(component, 'li.list-group-item', 3); - Harness.testInnerHtml(component, 'li.list-group-header div.row div:nth-child(3)', '2'); - Harness.testInnerHtml(component, 'li.list-group-item:nth-child(3) div.row div:nth-child(1)', 'good'); - Harness.testInnerHtml(component, 'li.list-group-item:nth-child(3) div.row div:nth-child(2)', 'baz'); - assert(component.checkValidity(component.getValue(), true), 'Item should be valid'); + + it('Should edit a row when edit is clicked', function () { + return Harness.testCreate(EditGridComponent, comp1).then( + (component) => { + Harness.testSetGet(component, [ + { + field1: 'good', + field2: 'foo', + }, + { + field1: 'good', + field2: 'bar', + }, + ]); + Harness.clickElement( + component, + 'li.list-group-item:nth-child(3) div.editRow', + ); + Harness.getInputValue( + component, + 'data[editgrid][1][field1]', + 'good', + ); + Harness.getInputValue( + component, + 'data[editgrid][1][field2]', + 'bar', + ); + Harness.testElements( + component, + 'div.editgrid-actions button.btn-primary', + 1, + ); + Harness.testElements( + component, + 'div.editgrid-actions button.btn-danger', + 1, + ); + assert( + !component.checkValidity(component.getValue(), true), + 'Item should not be valid', + ); + }, + ); }); - }); - - it('Should cancel edit row when cancel is clicked', function() { - return Harness.testCreate(EditGridComponent, comp1).then((component) => { - Harness.testSetGet(component, [ - { - field1: 'good', - field2: 'foo' - }, - { - field1: 'good', - field2: 'bar' - } - ]); - Harness.clickElement(component, 'li.list-group-item:nth-child(3) div.editRow'); - Harness.setInputValue(component, 'data[editgrid][1][field2]', 'baz'); - Harness.clickElement(component, 'div.editgrid-actions button.btn-danger'); - Harness.testElements(component, 'li.list-group-item', 3); - Harness.testInnerHtml(component, 'li.list-group-header div.row div:nth-child(3)', '2'); - Harness.testInnerHtml(component, 'li.list-group-item:nth-child(3) div.row div:nth-child(1)', 'good'); - Harness.testInnerHtml(component, 'li.list-group-item:nth-child(3) div.row div:nth-child(2)', 'bar'); - assert(component.checkValidity(component.getValue(), true), 'Item should be valid'); + + it('Should save a row when save is clicked', function () { + return Harness.testCreate(EditGridComponent, comp1).then( + (component) => { + Harness.testSetGet(component, [ + { + field1: 'good', + field2: 'foo', + }, + { + field1: 'good', + field2: 'bar', + }, + ]); + Harness.clickElement( + component, + 'li.list-group-item:nth-child(3) div.editRow', + ); + Harness.setInputValue( + component, + 'data[editgrid][1][field2]', + 'baz', + ); + Harness.clickElement( + component, + 'div.editgrid-actions button.btn-primary', + ); + Harness.testElements(component, 'li.list-group-item', 3); + Harness.testInnerHtml( + component, + 'li.list-group-header div.row div:nth-child(3)', + '2', + ); + Harness.testInnerHtml( + component, + 'li.list-group-item:nth-child(3) div.row div:nth-child(1)', + 'good', + ); + Harness.testInnerHtml( + component, + 'li.list-group-item:nth-child(3) div.row div:nth-child(2)', + 'baz', + ); + assert( + component.checkValidity(component.getValue(), true), + 'Item should be valid', + ); + }, + ); }); - }); - - it('Should show error messages for existing data in rows', function() { - return Harness.testCreate(EditGridComponent, comp1).then((component) => { - Harness.testSetGet(component, [ - { - field1: 'bad', - field2: 'foo' - }, - { - field1: 'good', - field2: 'bar' - }, - { - field1: 'also bad', - field2: 'baz' - } - ]); - Harness.testInnerHtml(component, 'li.list-group-item:nth-child(2) div.has-error div.editgrid-row-error', 'Must be good'); - Harness.testInnerHtml(component, 'li.list-group-item:nth-child(4) div.has-error div.editgrid-row-error', 'Must be good'); - assert(!component.checkValidity(component.getValue(), true), 'Item should not be valid'); + + it('Should cancel edit row when cancel is clicked', function () { + return Harness.testCreate(EditGridComponent, comp1).then( + (component) => { + Harness.testSetGet(component, [ + { + field1: 'good', + field2: 'foo', + }, + { + field1: 'good', + field2: 'bar', + }, + ]); + Harness.clickElement( + component, + 'li.list-group-item:nth-child(3) div.editRow', + ); + Harness.setInputValue( + component, + 'data[editgrid][1][field2]', + 'baz', + ); + Harness.clickElement( + component, + 'div.editgrid-actions button.btn-danger', + ); + Harness.testElements(component, 'li.list-group-item', 3); + Harness.testInnerHtml( + component, + 'li.list-group-header div.row div:nth-child(3)', + '2', + ); + Harness.testInnerHtml( + component, + 'li.list-group-item:nth-child(3) div.row div:nth-child(1)', + 'good', + ); + Harness.testInnerHtml( + component, + 'li.list-group-item:nth-child(3) div.row div:nth-child(2)', + 'bar', + ); + assert( + component.checkValidity(component.getValue(), true), + 'Item should be valid', + ); + }, + ); }); - }); - - it('Should not allow saving when errors exist', function() { - return Harness.testCreate(EditGridComponent, comp1).then((component) => { - Harness.clickElement(component, 'button.btn-primary'); - Harness.clickElement(component, 'div.editgrid-actions button.btn-primary'); - Harness.getInputValue(component, 'data[editgrid][0][field1]', ''); - Harness.getInputValue(component, 'data[editgrid][0][field2]', ''); - assert(!component.checkValidity(component.getValue(), true), 'Item should not be valid'); - Harness.setInputValue(component, 'data[editgrid][0][field2]', 'baz'); - Harness.clickElement(component, 'div.editgrid-actions button.btn-primary'); - Harness.getInputValue(component, 'data[editgrid][0][field1]', ''); - Harness.getInputValue(component, 'data[editgrid][0][field2]', 'baz'); - assert(!component.checkValidity(component.getValue(), true), 'Item should not be valid'); - Harness.setInputValue(component, 'data[editgrid][0][field1]', 'bad'); - Harness.clickElement(component, 'div.editgrid-actions button.btn-primary'); - Harness.getInputValue(component, 'data[editgrid][0][field1]', 'bad'); - Harness.getInputValue(component, 'data[editgrid][0][field2]', 'baz'); - assert(!component.checkValidity(component.getValue(), true), 'Item should not be valid'); - Harness.setInputValue(component, 'data[editgrid][0][field1]', 'good'); - Harness.clickElement(component, 'div.editgrid-actions button.btn-primary'); - assert(component.checkValidity(component.getValue(), true), 'Item should be valid'); - Harness.testInnerHtml(component, 'li.list-group-header div.row div:nth-child(3)', '1'); - Harness.testInnerHtml(component, 'li.list-group-item:nth-child(2) div.row div:nth-child(1)', 'good'); - Harness.testInnerHtml(component, 'li.list-group-item:nth-child(2) div.row div:nth-child(2)', 'baz'); + + it('Should show error messages for existing data in rows', function () { + return Harness.testCreate(EditGridComponent, comp1).then( + (component) => { + Harness.testSetGet(component, [ + { + field1: 'bad', + field2: 'foo', + }, + { + field1: 'good', + field2: 'bar', + }, + { + field1: 'also bad', + field2: 'baz', + }, + ]); + Harness.testInnerHtml( + component, + 'li.list-group-item:nth-child(2) div.has-error div.editgrid-row-error', + 'Must be good', + ); + Harness.testInnerHtml( + component, + 'li.list-group-item:nth-child(4) div.has-error div.editgrid-row-error', + 'Must be good', + ); + assert( + !component.checkValidity(component.getValue(), true), + 'Item should not be valid', + ); + }, + ); }); - }); - - it('Should not allow saving when rows are open', function() { - return Harness.testCreate(EditGridComponent, comp1).then((component) => { - Harness.testSetGet(component, [ - { - field1: 'good', - field2: 'foo' - }, - { - field1: 'good', - field2: 'bar' - } - ]); - Harness.clickElement(component, 'li.list-group-item:nth-child(3) div.editRow'); - assert(!component.checkValidity(component.getValue(), true), 'Item should not be valid'); - Harness.clickElement(component, 'div.editgrid-actions button.btn-primary'); - assert(component.checkValidity(component.getValue(), true), 'Item should be valid'); - Harness.clickElement(component, 'li.list-group-item:nth-child(3) div.editRow'); - assert(!component.checkValidity(component.getValue(), true), 'Item should not be valid'); - Harness.clickElement(component, 'div.editgrid-actions button.btn-danger'); - assert(component.checkValidity(component.getValue(), true), 'Item should be valid'); + + it('Should not allow saving when errors exist', function () { + return Harness.testCreate(EditGridComponent, comp1).then( + (component) => { + Harness.clickElement(component, 'button.btn-primary'); + Harness.clickElement( + component, + 'div.editgrid-actions button.btn-primary', + ); + Harness.getInputValue( + component, + 'data[editgrid][0][field1]', + '', + ); + Harness.getInputValue( + component, + 'data[editgrid][0][field2]', + '', + ); + assert( + !component.checkValidity(component.getValue(), true), + 'Item should not be valid', + ); + Harness.setInputValue( + component, + 'data[editgrid][0][field2]', + 'baz', + ); + Harness.clickElement( + component, + 'div.editgrid-actions button.btn-primary', + ); + Harness.getInputValue( + component, + 'data[editgrid][0][field1]', + '', + ); + Harness.getInputValue( + component, + 'data[editgrid][0][field2]', + 'baz', + ); + assert( + !component.checkValidity(component.getValue(), true), + 'Item should not be valid', + ); + Harness.setInputValue( + component, + 'data[editgrid][0][field1]', + 'bad', + ); + Harness.clickElement( + component, + 'div.editgrid-actions button.btn-primary', + ); + Harness.getInputValue( + component, + 'data[editgrid][0][field1]', + 'bad', + ); + Harness.getInputValue( + component, + 'data[editgrid][0][field2]', + 'baz', + ); + assert( + !component.checkValidity(component.getValue(), true), + 'Item should not be valid', + ); + Harness.setInputValue( + component, + 'data[editgrid][0][field1]', + 'good', + ); + Harness.clickElement( + component, + 'div.editgrid-actions button.btn-primary', + ); + assert( + component.checkValidity(component.getValue(), true), + 'Item should be valid', + ); + Harness.testInnerHtml( + component, + 'li.list-group-header div.row div:nth-child(3)', + '1', + ); + Harness.testInnerHtml( + component, + 'li.list-group-item:nth-child(2) div.row div:nth-child(1)', + 'good', + ); + Harness.testInnerHtml( + component, + 'li.list-group-item:nth-child(2) div.row div:nth-child(2)', + 'baz', + ); + }, + ); }); - }); - - it('Should disable components when in read only', function() { - return Harness.testCreate(EditGridComponent, comp1, { readOnly: true }).then((component) => { - Harness.testSetGet(component, [ - { - field1: 'good', - field2: 'foo' - }, - { - field1: 'good', - field2: 'bar' - } - ]); - Harness.clickElement(component, 'li.list-group-item:nth-child(3) div.removeRow'); - Harness.testInnerHtml(component, 'li.list-group-header div.row div:nth-child(3)', '2'); - Harness.clickElement(component, 'li.list-group-item:nth-child(3) div.editRow'); - Harness.testInnerHtml(component, 'li.list-group-header div.row div:nth-child(3)', '2'); + + it('Should not allow saving when rows are open', function () { + return Harness.testCreate(EditGridComponent, comp1).then( + (component) => { + Harness.testSetGet(component, [ + { + field1: 'good', + field2: 'foo', + }, + { + field1: 'good', + field2: 'bar', + }, + ]); + Harness.clickElement( + component, + 'li.list-group-item:nth-child(3) div.editRow', + ); + assert( + !component.checkValidity(component.getValue(), true), + 'Item should not be valid', + ); + Harness.clickElement( + component, + 'div.editgrid-actions button.btn-primary', + ); + assert( + component.checkValidity(component.getValue(), true), + 'Item should be valid', + ); + Harness.clickElement( + component, + 'li.list-group-item:nth-child(3) div.editRow', + ); + assert( + !component.checkValidity(component.getValue(), true), + 'Item should not be valid', + ); + Harness.clickElement( + component, + 'div.editgrid-actions button.btn-danger', + ); + assert( + component.checkValidity(component.getValue(), true), + 'Item should be valid', + ); + }, + ); }); - }); - - describe('Display As Modal', function() { - it('Should show add error classes to invalid components', function(done) { - const formElement = document.createElement('div'); - const form = new Webform(formElement); - form.setForm(displayAsModalEditGrid).then(() => { - const editGrid = form.components[0]; - const clickEvent = new Event('click'); - editGrid.addRow(); - setTimeout(() => { - const dialog = document.querySelector('[ref="dialogContents"]'); - const saveButton = dialog.querySelector('.btn.btn-primary'); - saveButton.dispatchEvent(clickEvent); - setTimeout(() => { - assert.equal(editGrid.errors.length, 6); - const components = Array.from(dialog.querySelectorAll('[ref="component"]')); - const areRequiredComponentsHaveErrorWrapper = components.every((comp) => { - const { className } = comp; - return (className.includes('required') && className.includes('formio-error-wrapper')) || true; - }); - assert.equal(areRequiredComponentsHaveErrorWrapper, true); - document.body.innerHTML = ''; - done(); - }, 100); - }, 100); - }).catch(done); + + it('Should disable components when in read only', function () { + return Harness.testCreate(EditGridComponent, comp1, { + readOnly: true, + }).then((component) => { + Harness.testSetGet(component, [ + { + field1: 'good', + field2: 'foo', + }, + { + field1: 'good', + field2: 'bar', + }, + ]); + Harness.clickElement( + component, + 'li.list-group-item:nth-child(3) div.removeRow', + ); + Harness.testInnerHtml( + component, + 'li.list-group-header div.row div:nth-child(3)', + '2', + ); + Harness.clickElement( + component, + 'li.list-group-item:nth-child(3) div.editRow', + ); + Harness.testInnerHtml( + component, + 'li.list-group-header div.row div:nth-child(3)', + '2', + ); + }); }); - it('Should set alert with validation errors on save and update them', function(done) { - const formElement = document.createElement('div'); - const form = new Webform(formElement); - form.setForm(ModalEditGrid).then(() => { - const editGrid = form.components[0]; + describe('Display As Modal', function () { + it('Should show add error classes to invalid components', function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement); + form.setForm(displayAsModalEditGrid) + .then(() => { + const editGrid = form.components[0]; + const clickEvent = new Event('click'); + editGrid.addRow(); + setTimeout(() => { + const dialog = document.querySelector( + '[ref="dialogContents"]', + ); + const saveButton = + dialog.querySelector('.btn.btn-primary'); + saveButton.dispatchEvent(clickEvent); + setTimeout(() => { + assert.equal(editGrid.errors.length, 6); + const components = Array.from( + dialog.querySelectorAll('[ref="component"]'), + ); + const areRequiredComponentsHaveErrorWrapper = + components.every((comp) => { + const { className } = comp; + return ( + (className.includes('required') && + className.includes( + 'formio-error-wrapper', + )) || + true + ); + }); + assert.equal( + areRequiredComponentsHaveErrorWrapper, + true, + ); + document.body.innerHTML = ''; + done(); + }, 100); + }, 100); + }) + .catch(done); + }); - form.checkValidity(form._data, true, form._data); - assert.equal(form.errors.length, 1); - editGrid.addRow(); + it('Should set alert with validation errors on save and update them', function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement); + form.setForm(ModalEditGrid) + .then(() => { + const editGrid = form.components[0]; - setTimeout(() => { - const editRow = editGrid.editRows[0]; - const dialog = editRow.dialog; - const saveButton = dialog.querySelector('.btn.btn-primary'); - const clickEvent = new Event('click'); - saveButton.dispatchEvent(clickEvent); + form.checkValidity(form._data, true, form._data); + assert.equal(form.errors.length, 1); + editGrid.addRow(); - setTimeout(() => { - const alert = dialog.querySelector('.alert.alert-danger'); - assert.equal(form.errors.length, 3); + setTimeout(() => { + const editRow = editGrid.editRows[0]; + const dialog = editRow.dialog; + const saveButton = + dialog.querySelector('.btn.btn-primary'); + const clickEvent = new Event('click'); + saveButton.dispatchEvent(clickEvent); - const errorsLinks = alert.querySelectorAll('li'); - assert.equal(errorsLinks.length, 2); - const textField = editRow.components[0].getComponent('textField'); - textField.setValue('new value'); + setTimeout(() => { + const alert = dialog.querySelector( + '.alert.alert-danger', + ); + assert.equal(form.errors.length, 3); + + const errorsLinks = alert.querySelectorAll('li'); + assert.equal(errorsLinks.length, 2); + const textField = + editRow.components[0].getComponent('textField'); + textField.setValue('new value'); + + setTimeout(() => { + const alertAfterFixingField = + dialog.querySelector('.alert.alert-danger'); + assert.equal(form.errors.length, 2); + + const errorsLinksAfterFixingField = + alertAfterFixingField.querySelectorAll( + 'li', + ); + assert.equal( + errorsLinksAfterFixingField.length, + 1, + ); - setTimeout(() => { - const alertAfterFixingField = dialog.querySelector('.alert.alert-danger'); - assert.equal(form.errors.length, 2); - - const errorsLinksAfterFixingField = alertAfterFixingField.querySelectorAll('li'); - assert.equal(errorsLinksAfterFixingField.length, 1); - - document.body.innerHTML = ''; - done(); - }, 450); - }, 100); - }, 100); - }).catch(done); - }); + document.body.innerHTML = ''; + done(); + }, 450); + }, 100); + }, 100); + }) + .catch(done); + }); - it('Confirmation dialog', function(done) { - const formElement = document.createElement('div'); - const form = new Webform(formElement); - form.setForm(comp6).then(() => { - const component = form.components[0]; - component.addRow(); - const dialog = document.querySelector('[ref="dialogContents"]'); - Harness.dispatchEvent('input', dialog, '[name="data[editGrid][0][textField]"]', (el) => el.value = '12'); - Harness.dispatchEvent('click', dialog, '[ref="dialogClose"]'); - const confirmationDialog = document.querySelector('[ref="confirmationDialog"]'); - assert(confirmationDialog, 'Should open a confirmation dialog when trying to close'); - Harness.dispatchEvent('click', confirmationDialog, '[ref="dialogCancelButton"]'); - setTimeout(() => { - assert.equal(component.editRows[0].data.textField, '12', 'Data should not be cleared'); - - Harness.dispatchEvent('click', dialog, '[ref="dialogClose"]'); - setTimeout(() => { - const confirmationDialog2 = document.querySelector('[ref="confirmationDialog"]'); - assert(confirmationDialog2, 'Should open again a conformation dialog'); - Harness.dispatchEvent('click', confirmationDialog2, '[ref="dialogYesButton"]'); - setTimeout(() => { - assert.equal(component.editRows.length, 0, 'Data should be cleared'); - done(); - }, 250); - }, 250); - }, 250); - }).catch(done); - }); + it('Confirmation dialog', function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement); + form.setForm(comp6) + .then(() => { + const component = form.components[0]; + component.addRow(); + const dialog = document.querySelector( + '[ref="dialogContents"]', + ); + Harness.dispatchEvent( + 'input', + dialog, + '[name="data[editGrid][0][textField]"]', + (el) => (el.value = '12'), + ); + Harness.dispatchEvent( + 'click', + dialog, + '[ref="dialogClose"]', + ); + const confirmationDialog = document.querySelector( + '[ref="confirmationDialog"]', + ); + assert( + confirmationDialog, + 'Should open a confirmation dialog when trying to close', + ); + Harness.dispatchEvent( + 'click', + confirmationDialog, + '[ref="dialogCancelButton"]', + ); + setTimeout(() => { + assert.equal( + component.editRows[0].data.textField, + '12', + 'Data should not be cleared', + ); - it('Confirmation dialog shouldn\'t occure if no values within the row are changed', function(done) { - const formElement = document.createElement('div'); - const form = new Webform(formElement); - form.setForm(comp6).then(() => { - const component = form.components[0]; - component.setValue([ - { textField: 'v1' } - ]); - setTimeout(() => { - component.editRow(0); - const dialog = document.querySelector('[ref="dialogContents"]'); - Harness.dispatchEvent('click', dialog, '[ref="dialogClose"]'); - const confirmationDialog = document.querySelector('[ref="confirmationDialog"]'); - assert(!confirmationDialog, 'Shouldn\'t open a confirmation dialog when no values were changed'); - assert.equal(component.editRows[0].data.textField, 'v1', 'Data shouldn\'t be changed'); - done(); - }, 150); - }).catch(done); - }); + Harness.dispatchEvent( + 'click', + dialog, + '[ref="dialogClose"]', + ); + setTimeout(() => { + const confirmationDialog2 = document.querySelector( + '[ref="confirmationDialog"]', + ); + assert( + confirmationDialog2, + 'Should open again a conformation dialog', + ); + Harness.dispatchEvent( + 'click', + confirmationDialog2, + '[ref="dialogYesButton"]', + ); + setTimeout(() => { + assert.equal( + component.editRows.length, + 0, + 'Data should be cleared', + ); + done(); + }, 250); + }, 250); + }, 250); + }) + .catch(done); + }); - it('Should not produce many components in Edit view when minLength validation set', function(done) { - const formElement = document.createElement('div'); - Formio.createForm(formElement, comp15, { attachMode:'builder' } ) - .then(form => { - const editGrid = form.components[0]; - const elements = editGrid.element.querySelectorAll('[ref="input"]'); - - setTimeout(() => { - assert.equal(elements.length, 2); - done(); - }, 200); - }) - .catch(done); - }); + it("Confirmation dialog shouldn't occure if no values within the row are changed", function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement); + form.setForm(comp6) + .then(() => { + const component = form.components[0]; + component.setValue([{ textField: 'v1' }]); + setTimeout(() => { + component.editRow(0); + const dialog = document.querySelector( + '[ref="dialogContents"]', + ); + Harness.dispatchEvent( + 'click', + dialog, + '[ref="dialogClose"]', + ); + const confirmationDialog = document.querySelector( + '[ref="confirmationDialog"]', + ); + assert( + !confirmationDialog, + "Shouldn't open a confirmation dialog when no values were changed", + ); + assert.equal( + component.editRows[0].data.textField, + 'v1', + "Data shouldn't be changed", + ); + done(); + }, 150); + }) + .catch(done); + }); - it('Should close row when Display as Modal checked', function(done) { - const formElement = document.createElement('div'); - const form = new Webform(formElement); - form.setForm(comp14).then(() => { - const editGrid = form.components[0]; - editGrid.addRow(); - setTimeout(() => { - const dialog = document.querySelector('[ref="dialogContents"]'); - Harness.dispatchEvent('input', dialog, '[name="data[editGrid][0][firstName]"]', (el) => el.value = 'Michael'); - Harness.dispatchEvent('click', dialog, '[ref="dialogClose"]'); - const confirmationDialog = document.querySelector('[ref="confirmationDialog"]'); - Harness.dispatchEvent('click', confirmationDialog, '[ref="dialogYesButton"]'); - setTimeout(() => { - assert.equal(!!document.querySelector('[ref="dialogContents"]'), false); - done(); - }, 100); - }, 100); - }).catch(done); - }); - }); - - describe('Draft Rows', function() { - it('Check saving rows as draft', function(done) { - Harness.testCreate(EditGridComponent, comp5).then((component) => { - component.addRow(); - Harness.clickElement(component, '[ref="editgrid-editGrid1-saveRow"]'); - assert.deepEqual(component.dataValue, [{ textField: '' }]); - const isInvalid = !component.checkValidity(component.dataValue, true); - assert(isInvalid, 'Item should not be valid'); - assert(component.editRows[0].state === 'draft', 'Row should be saved as draft if it has errors'); - done(); - }).catch(done); + it('Should not produce many components in Edit view when minLength validation set', function (done) { + const formElement = document.createElement('div'); + Formio.createForm(formElement, comp15, { attachMode: 'builder' }) + .then((form) => { + const editGrid = form.components[0]; + const elements = + editGrid.element.querySelectorAll('[ref="input"]'); + + setTimeout(() => { + assert.equal(elements.length, 2); + done(); + }, 200); + }) + .catch(done); + }); + + it('Should close row when Display as Modal checked', function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement); + form.setForm(comp14) + .then(() => { + const editGrid = form.components[0]; + editGrid.addRow(); + setTimeout(() => { + const dialog = document.querySelector( + '[ref="dialogContents"]', + ); + Harness.dispatchEvent( + 'input', + dialog, + '[name="data[editGrid][0][firstName]"]', + (el) => (el.value = 'Michael'), + ); + Harness.dispatchEvent( + 'click', + dialog, + '[ref="dialogClose"]', + ); + const confirmationDialog = document.querySelector( + '[ref="confirmationDialog"]', + ); + Harness.dispatchEvent( + 'click', + confirmationDialog, + '[ref="dialogYesButton"]', + ); + setTimeout(() => { + assert.equal( + !!document.querySelector( + '[ref="dialogContents"]', + ), + false, + ); + done(); + }, 100); + }, 100); + }) + .catch(done); + }); }); - it('Should not show row errors alerts if drafts enabled in modal-edit EditGrid', function(done) { - const formElement = document.createElement('div'); - const form = new Webform(formElement); - ModalEditGrid.components[0].rowDrafts = true; + describe('Draft Rows', function () { + it('Check saving rows as draft', function (done) { + Harness.testCreate(EditGridComponent, comp5) + .then((component) => { + component.addRow(); + Harness.clickElement( + component, + '[ref="editgrid-editGrid1-saveRow"]', + ); + assert.deepEqual(component.dataValue, [{ textField: '' }]); + const isInvalid = !component.checkValidity( + component.dataValue, + true, + ); + assert(isInvalid, 'Item should not be valid'); + assert( + component.editRows[0].state === 'draft', + 'Row should be saved as draft if it has errors', + ); + done(); + }) + .catch(done); + }); + + it('Should not show row errors alerts if drafts enabled in modal-edit EditGrid', function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement); + ModalEditGrid.components[0].rowDrafts = true; - form.setForm(ModalEditGrid).then(() => { - const editGrid = form.components[0]; - editGrid.addRow(); + form.setForm(ModalEditGrid) + .then(() => { + const editGrid = form.components[0]; + editGrid.addRow(); - setTimeout(() => { - editGrid.saveRow(0); + setTimeout(() => { + editGrid.saveRow(0); - setTimeout(() => { - editGrid.editRow(0).then(() => { - const textField = form.getComponent(['editGrid', 0, 'form', 'textField']); + setTimeout(() => { + editGrid.editRow(0).then(() => { + const textField = form.getComponent([ + 'editGrid', + 0, + 'form', + 'textField', + ]); + + textField.setValue('someValue'); + + setTimeout(() => { + Harness.dispatchEvent( + 'click', + editGrid.editRows[0].dialog, + `.editgrid-row-modal-${editGrid.id} [ref="dialogClose"]`, + ); + setTimeout(() => { + const dialog = + editGrid.editRows[0] + .confirmationDialog; + + Harness.dispatchEvent( + 'click', + dialog, + '[ref="dialogYesButton"]', + ); + + setTimeout(() => { + editGrid.editRow(0).then(() => { + textField.setValue('someValue'); + + setTimeout(() => { + const errorAlert = + editGrid.editRows[0].dialog.querySelector( + `#error-list-${editGrid.id}`, + ); + const hasError = + textField.className.includes( + 'has-error', + ); + assert( + !hasError, + 'Should stay valid until form is submitted', + ); + assert.equal( + errorAlert, + null, + 'Should be valid', + ); + + done(); + }, 100); + }); + }, 100); + }, 100); + }, 100); + }); + }, 100); + }, 100); + }) + .catch(done) + .finally(() => { + ModalEditGrid.components[0].rowDrafts = false; + }); + }); - textField.setValue('someValue'); + it('Should keep fields valid inside NestedForms if drafts are enabled', function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement); + ModalEditGrid.components[0].rowDrafts = true; - setTimeout(() => { - Harness.dispatchEvent('click', editGrid.editRows[0].dialog, `.editgrid-row-modal-${editGrid.id} [ref="dialogClose"]`); - setTimeout(() => { - const dialog = editGrid.editRows[0].confirmationDialog; + form.setForm(ModalEditGrid) + .then(() => { + const editGrid = form.components[0]; + + form.checkValidity(form._data, true, form._data); + assert.equal( + form.errors.length, + 1, + 'Should have an error saying that EditGrid is required', + ); + + // 1. Add a row + editGrid.addRow(); + + setTimeout(() => { + const editRow = editGrid.editRows[0]; + const dialog = editRow.dialog; + + // 2. Save the row + Harness.dispatchEvent( + 'click', + dialog, + '.btn.btn-primary', + ); + + setTimeout(() => { + const alert = dialog.querySelector( + '.alert.alert-danger', + ); + assert.equal( + form.errors.length, + 0, + 'Should not add new errors when drafts are enabled', + ); + assert( + !alert, + 'Should not show an error alert when drafts are enabled and form is not submitted', + ); + + const textField = + editRow.components[0].getComponent('textField'); + + // 3. Edit the row + editGrid.editRow(0); + + setTimeout(() => { + // 4. Change the value of the text field + textField.setValue('new value', { + modified: true, + }); + + setTimeout(() => { + assert.equal( + textField.dataValue, + 'new value', + ); + // 5. Clear the value of the text field + textField.setValue('', { modified: true }); + + setTimeout(() => { + assert.equal(textField.dataValue, ''); + assert.equal( + editGrid.editRows[0].errors.length, + 0, + 'Should not add error to components inside draft row', + ); + + const textFieldComponent = + textField.element; + assert( + textFieldComponent.className.includes( + 'has-error', + ), + 'Should add error class to component even when drafts enabled if the component is not pristine', + ); + + document.innerHTML = ''; + done(); + }, 300); + }, 300); + }, 150); + }, 100); + }, 100); + }) + .catch(done) + .finally(() => { + delete ModalEditGrid.components[0].rowDrafts; + }); + }); + + it('Should keep fields valid inside NestedForms if drafts are enabled part two', function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement); + ModalEditGrid.components[0].rowDrafts = true; + + form.setForm(ModalEditGrid) + .then(() => { + const editGrid = form.components[0]; + // 1. Add a row + editGrid.addRow(); - Harness.dispatchEvent('click', dialog, '[ref="dialogYesButton"]'); + setTimeout(() => { + const editRow = editGrid.editRows[0]; + const dialog = editRow.dialog; - setTimeout(() => { + // 2. Save the row + Harness.dispatchEvent( + 'click', + dialog, + '.btn.btn-primary', + ); + + setTimeout(() => { + // 3. Submit the form + Harness.dispatchEvent( + 'click', + form.element, + '[name="data[submit]"]', + ); + + setTimeout(() => { + assert.equal( + editGrid.errors.length, + 3, + 'Should be validated after an attempt to submit', + ); + assert.equal( + editGrid.editRows[0].errors.length, + 2, + 'Should dd errors to the row after an attempt to submit', + ); + const rows = editGrid.element.querySelectorAll( + '[ref="editgrid-editGrid-row"]', + ); + const firstRow = rows[0]; + Harness.dispatchEvent( + 'click', + firstRow, + '.editRow', + ); + + setTimeout(() => { + assert( + form.submitted, + 'Form should be submitted', + ); + const editRow = editGrid.editRows[0]; + assert( + editRow.alerts, + 'Should add an error alert to the modal', + ); + assert.equal( + editRow.errors.length, + 2, + 'Should add errors to components inside draft row aftre it was submitted', + ); + const textField = + editRow.components[0].getComponent( + 'textField', + ); + + const alert = editGrid.alert; + assert( + alert, + 'Should show an error alert when drafts are enabled and form is submitted', + ); + assert( + textField.element.className.includes( + 'has-error', + ), + 'Should add error class to component even when drafts enabled if the form was submitted', + ); + + // 4. Change the value of the text field + textField.setValue('new value', { + modified: true, + }); + + setTimeout(() => { + const textFieldEl = textField.element; + assert.equal( + textField.dataValue, + 'new value', + ); + assert( + !textFieldEl.className.includes( + 'has-error', + ), + 'Should remove an error class from component when it was fixed', + ); + const editRow = editGrid.editRows[0]; + const textField2 = + editRow.components[0].getComponent( + 'textField2', + ); + + textField2.setValue('test val', { + modified: true, + }); + + setTimeout(() => { + assert.equal( + textField2.dataValue, + 'test val', + ); + assert( + !textField2.element.className.includes( + 'has-error', + ), + 'Should remove an error class from component when it was fixed', + ); + + editGrid.saveRow(0); + + setTimeout(() => { + assert( + !form.alert, + 'Should remove an error alert after all the rows were fixed', + ); + + const rows = + editGrid.element.querySelectorAll( + '[ref="editgrid-editGrid-row"]', + ); + const firstRow = rows[0]; + Harness.dispatchEvent( + 'click', + firstRow, + '.editRow', + ); + setTimeout(() => { + const editRow = + editGrid.editRows[0]; + const textField2 = + editRow.components[0].getComponent( + 'textField2', + ); + + Harness.dispatchEvent( + 'input', + editRow.dialog, + '[name="data[textField2]"', + (i) => (i.value = ''), + ); + setTimeout(() => { + assert.equal( + textField2.dataValue, + '', + ); + Harness.dispatchEvent( + 'click', + editGrid.editRows[0] + .dialog, + `.editgrid-row-modal-${editGrid.id} [ref="dialogClose"]`, + ); + setTimeout(() => { + const dialog = + editGrid + .editRows[0] + .confirmationDialog; + + Harness.dispatchEvent( + 'click', + dialog, + '[ref="dialogYesButton"]', + ); + + setTimeout(() => { + assert( + !document.querySelector( + `#error-list-${form.id}`, + ), + 'Should not add an error alert when the changes that made the row invalid, were discarded by Cancel', + ); + document.innerHTML = + ''; + done(); + }, 100); + }, 100); + }, 200); + }, 300); + }, 300); + }, 300); + }, 300); + }, 450); + }, 250); + }, 100); + }, 100); + }) + .catch(done) + .finally(() => { + delete ModalEditGrid.components[0].rowDrafts; + }); + }); + + // it('', (done) => { + // const formElement = document.createElement('div'); + // const form = new Webform(formElement); + // form.setForm(ModalEditGrid).then(() => { + // + // }).catch(done); + // }); + }); + + it("Test simple conditions based on the EditGrid's child's value and default values when adding rows", function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement); + form.setForm({ display: 'form', components: [comp7], type: 'form' }) + .then(() => { + const component = form.getComponent(['editGrid']); + component.addRow(); + setTimeout(() => { + Harness.getInputValue( + component, + 'data[editGrid][0][checkbox]', + true, + 'checked', + ); + Harness.testComponentVisibility( + component, + '.formio-component-editGridChild', + true, + ); + Harness.testComponentVisibility( + component, + '.formio-component-panelChild', + true, + ); + done(); + }, 250); + }) + .catch(done); + }); + + it('Test clearOnHide inside EditGrid', function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement); + form.setForm({ display: 'form', components: [comp7], type: 'form' }) + .then(() => { + form.submission = { + data: { + editGrid: [ + { + checkbox: true, + editGridChild: 'Has Value', + panelChild: 'Has Value Too', + }, + ], + }, + }; + setTimeout(() => { + const editGrid = form.getComponent(['editGrid']); editGrid.editRow(0).then(() => { - textField.setValue('someValue'); + Harness.dispatchEvent( + 'click', + editGrid.element, + '[name="data[editGrid][0][checkbox]"]', + (el) => (el.checked = false), + ); + setTimeout(() => { + Harness.testComponentVisibility( + editGrid, + '.formio-component-editGridChild', + false, + ); + Harness.testComponentVisibility( + editGrid, + '.formio-component-panelChild', + false, + ); + editGrid.saveRow(0, true); + setTimeout(() => { + assert( + !form.data.editGrid[0].editGridChild, + 'Should be cleared', + ); + assert( + !form.data.editGrid[0].panelChild, + 'Should be cleared', + ); + done(); + }, 150); + }, 150); + }, 150); + }); + }) + .catch(done); + }); - setTimeout(() => { - const errorAlert = editGrid.editRows[0].dialog.querySelector(`#error-list-${editGrid.id}`); - const hasError = textField.className.includes('has-error'); - assert(!hasError, 'Should stay valid until form is submitted'); - assert.equal(errorAlert, null, 'Should be valid'); + it('Test refreshing inside EditGrid', function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement); + form.setForm({ display: 'form', components: [comp8], type: 'form' }) + .then(() => { + const editGrid = form.getComponent(['editGrid1']); + editGrid.addRow(); + const makeSelect = form.getComponent(['editGrid1', 0, 'make']); + const modelSelect = form.getComponent([ + 'editGrid1', + 0, + 'model', + ]); + makeSelect.setValue('ford'); + setTimeout(() => { + modelSelect.setValue('Focus'); + setTimeout(() => { + editGrid.saveRow(0, true); + setTimeout(() => { + assert.equal( + form.data.editGrid1[0].model, + 'Focus', + 'Should be saved properly', + ); + done(); + }, 150); + }, 100); + }, 150); + }) + .catch(done); + }); - done(); - }, 100); + it('Should display summary with values only for components that are visible at least in one row', function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement); + form.setForm(comp9) + .then(() => { + const editGrid = form.getComponent('editGrid'); + + const checkRows = (columnsNumber, rowsNumber) => { + const rowWithColumns = + editGrid.element.querySelector('.row'); + const rowsWithValues = editGrid.element.querySelectorAll( + '[ref="editgrid-editGrid-row"]', + ); + + assert.equal( + rowWithColumns.children.length, + columnsNumber, + 'Row should contain values only for visible components', + ); + assert.equal( + rowsWithValues.length, + rowsNumber, + 'Should have corrent number of rows', + ); + }; + + checkRows(2, 0); + form.setValue({ + data: { + editGrid: [ + { textField: 'test1', checkbox: false }, + { textField: 'test2', checkbox: false }, + ], + }, + }); + setTimeout(() => { + checkRows(2, 2); + form.setValue({ + data: { + editGrid: [ + { textField: 'test1', checkbox: false }, + { textField: 'test2', checkbox: true }, + ], + }, }); - }, 100); - }, 100); - }, 100); - }); - }, 100); - }, 100); - }).catch(done) - .finally(() => { - ModalEditGrid.components[0].rowDrafts = false; - }); + + setTimeout(() => { + checkRows(3, 2); + form.setValue({ + data: { + editGrid: [ + { textField: 'test1', checkbox: false }, + { + textField: 'test2', + checkbox: true, + textArea: 'test22', + }, + { + textField: 'show', + checkbox: true, + container: { number1: 1111 }, + textArea: 'test3', + }, + ], + }, + }); + + setTimeout(() => { + checkRows(4, 3); + form.setValue({ + data: { + editGrid: [ + { textField: 'test1', checkbox: false }, + { textField: 'test2', checkbox: false }, + { + textField: 'show', + checkbox: false, + container: { number1: 1111 }, + }, + ], + }, + }); + + setTimeout(() => { + checkRows(3, 3); + + done(); + }, 300); + }, 300); + }, 300); + }, 300); + }) + .catch(done); }); - it('Should keep fields valid inside NestedForms if drafts are enabled', function(done) { - const formElement = document.createElement('div'); - const form = new Webform(formElement); - ModalEditGrid.components[0].rowDrafts = true; + it('Should add component to the header only if it is visible in saved row', function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement); + form.setForm(comp9) + .then(() => { + const editGrid = form.getComponent('editGrid'); + + const checkHeader = (componentsNumber) => { + const header = editGrid.element + .querySelector('.list-group-header') + .querySelector('.row'); + + assert.equal( + editGrid.visibleInHeader.length, + componentsNumber, + ); + assert.equal(header.children.length, componentsNumber); + }; + + const clickElem = (elem) => { + const clickEvent = new Event('click'); + elem.dispatchEvent(clickEvent); + }; + const clickAddRow = () => { + const addAnotherBtn = + editGrid.refs['editgrid-editGrid-addRow'][0]; + clickElem(addAnotherBtn); + }; + + checkHeader(2); + clickAddRow(); - form.setForm(ModalEditGrid).then(() => { - const editGrid = form.components[0]; + setTimeout(() => { + assert.equal(editGrid.editRows.length, 1); + checkHeader(2); + const checkbox = editGrid.getComponent('checkbox')[0]; + checkbox.setValue(true); - form.checkValidity(form._data, true, form._data); - assert.equal(form.errors.length, 1, 'Should have an error saying that EditGrid is required'); + setTimeout(() => { + checkHeader(2); + assert.equal( + editGrid.getComponent('textArea')[0].visible, + true, + ); + clickAddRow(); - // 1. Add a row - editGrid.addRow(); + setTimeout(() => { + assert.equal(editGrid.editRows.length, 2); + checkHeader(2); + const saveFirstRowBtn = + editGrid.refs['editgrid-editGrid-saveRow'][0]; + clickElem(saveFirstRowBtn); + + setTimeout(() => { + assert.equal( + editGrid.editRows[0].state, + 'saved', + ); + checkHeader(3); - setTimeout(() => { - const editRow = editGrid.editRows[0]; - const dialog = editRow.dialog; + done(); + }, 300); + }, 300); + }, 300); + }, 300); + }) + .catch(done); + }).timeout(3000); + + it('Should add/save/cancel/delete/edit rows', function (done) { + const form = _.cloneDeep(comp10); + const element = document.createElement('div'); + + Formio.createForm(element, form) + .then((form) => { + const editGrid = form.getComponent('editGrid'); + + const click = (btn, index, selector) => { + let elem; + + if (selector) { + elem = editGrid.element.querySelectorAll(`.${btn}`)[ + index + ]; + } else { + elem = editGrid.refs[`editgrid-editGrid-${btn}`][index]; + } + + const clickEvent = new Event('click'); + elem.dispatchEvent(clickEvent); + }; + + assert.equal( + editGrid.refs['editgrid-editGrid-row'].length, + 0, + 'Should not have rows', + ); + assert.equal( + editGrid.editRows.length, + 0, + 'Should not have rows', + ); + + click('addRow', 0); - // 2. Save the row - Harness.dispatchEvent('click', dialog, '.btn.btn-primary'); + setTimeout(() => { + assert.equal( + editGrid.refs['editgrid-editGrid-row'].length, + 1, + 'Should have 1 row', + ); + assert.equal( + editGrid.editRows.length, + 1, + 'Should have 1 row', + ); + assert.equal( + editGrid.editRows[0].state, + 'new', + 'Should have state "new"', + ); + editGrid.editRows[0].components.forEach((comp) => { + comp.setValue(11111); + }); - setTimeout(() => { - const alert = dialog.querySelector('.alert.alert-danger'); - assert.equal(form.errors.length, 0, 'Should not add new errors when drafts are enabled'); - assert(!alert, 'Should not show an error alert when drafts are enabled and form is not submitted'); + setTimeout(() => { + assert.deepEqual( + editGrid.editRows[0].data, + { number: 11111, textField: '11111' }, + 'Should set row data', + ); + click('saveRow', 0); - const textField = editRow.components[0].getComponent('textField'); + setTimeout(() => { + assert.equal( + editGrid.refs['editgrid-editGrid-row'].length, + 1, + 'Should have 1 row', + ); + assert.equal( + editGrid.editRows.length, + 1, + 'Should have 1 row', + ); + assert.equal( + editGrid.editRows[0].state, + 'saved', + 'Should have state "saved"', + ); + assert.deepEqual( + editGrid.editRows[0].data, + { number: 11111, textField: '11111' }, + 'Should set row data', + ); + click('editRow', 0, true); + + setTimeout(() => { + assert.equal( + editGrid.refs['editgrid-editGrid-row'] + .length, + 1, + 'Should have 1 row', + ); + assert.equal( + editGrid.editRows.length, + 1, + 'Should have 1 row', + ); + assert.equal( + editGrid.editRows[0].state, + 'editing', + 'Should have state "editing"', + ); + editGrid.editRows[0].components.forEach( + (comp) => { + comp.setValue(22222); + }, + ); - // 3. Edit the row - editGrid.editRow(0); + setTimeout(() => { + assert.deepEqual( + editGrid.editRows[0].data, + { number: 22222, textField: '22222' }, + 'Should set row data', + ); + click('cancelRow', 0); + + setTimeout(() => { + assert.equal( + editGrid.refs[ + 'editgrid-editGrid-row' + ].length, + 1, + 'Should have 1 row', + ); + assert.equal( + editGrid.editRows.length, + 1, + 'Should have 1 row', + ); + assert.equal( + editGrid.editRows[0].state, + 'saved', + 'Should have state "saved"', + ); + assert.deepEqual( + editGrid.editRows[0].data, + { + number: 11111, + textField: '11111', + }, + 'Should cancel changed data', + ); + click('removeRow', 0, true); + + setTimeout(() => { + assert.equal( + editGrid.refs[ + 'editgrid-editGrid-row' + ].length, + 0, + 'Should not have rows', + ); + assert.equal( + editGrid.editRows.length, + 0, + 'Should have 0 rows', + ); + + document.innerHTML = ''; + done(); + }, 200); + }, 200); + }, 200); + }, 200); + }, 200); + }, 200); + }, 200); + }) + .catch(done); + }).timeout(3000); + + it('Should open first row when empty and allow saving openned row', function (done) { + const form = _.cloneDeep(comp10); + const element = document.createElement('div'); + form.components[0].openWhenEmpty = true; + + Formio.createForm(element, form) + .then((form) => { + const editGrid = form.getComponent('editGrid'); + + const click = (btn, index, selector) => { + let elem; + + if (selector) { + elem = editGrid.element.querySelectorAll(`.${btn}`)[ + index + ]; + } else { + elem = editGrid.refs[`editgrid-editGrid-${btn}`][index]; + } + + const clickEvent = new Event('click'); + elem.dispatchEvent(clickEvent); + }; + + assert.equal( + editGrid.refs['editgrid-editGrid-row'].length, + 1, + 'Should have 1 row', + ); + assert.equal(editGrid.editRows.length, 1, 'Should have 1 row'); + assert.equal( + editGrid.editRows[0].state, + 'new', + 'Should have state "new"', + ); + click('saveRow', 0); - setTimeout(() => { - // 4. Change the value of the text field - textField.setValue('new value', { modified: true }); + setTimeout(() => { + assert.equal( + editGrid.refs['editgrid-editGrid-row'].length, + 1, + 'Should have 1 row', + ); + assert.equal( + editGrid.editRows.length, + 1, + 'Should have 1 row', + ); + assert.equal( + editGrid.editRows[0].state, + 'saved', + 'Should have state "saved"', + ); - setTimeout(() => { - assert.equal(textField.dataValue, 'new value'); - // 5. Clear the value of the text field - textField.setValue('', { modified: true }); + document.innerHTML = ''; + done(); + }, 200); + }) + .catch(done); + }).timeout(3000); + + it('Should disable adding/removing rows', function (done) { + const form = _.cloneDeep(comp10); + const element = document.createElement('div'); + form.components[0].disableAddingRemovingRows = true; + const value = [ + { number: 1, textField: 'test' }, + { number: 2, textField: 'test2' }, + ]; + + Formio.createForm(element, form) + .then((form) => { + const editGrid = form.getComponent('editGrid'); + editGrid.setValue(value); setTimeout(() => { - assert.equal(textField.dataValue, ''); - assert.equal(editGrid.editRows[0].errors.length, 0, 'Should not add error to components inside draft row'); + assert.equal( + editGrid.refs['editgrid-editGrid-row'].length, + 2, + 'Should have 2 rows', + ); + assert.equal( + editGrid.editRows.length, + 2, + 'Should have 2 rows', + ); + assert.equal( + editGrid.refs['editgrid-editGrid-addRow'].length, + 0, + 'Should not have add row btn', + ); + assert.equal( + editGrid.element.querySelectorAll('.removeRow').length, + 0, + 'Should not have remove row btn', + ); + + document.innerHTML = ''; + done(); + }, 200); + }) + .catch(done); + }); - const textFieldComponent = textField.element; - assert(textFieldComponent.className.includes('has-error'), 'Should add error class to component even when drafts enabled if the component is not pristine'); + it('Should show conditional eddRow btn if condition is met', function (done) { + const form = _.cloneDeep(comp10); + const element = document.createElement('div'); + form.components[0].conditionalAddButton = 'show = data.number11 === 5'; + form.components.unshift({ + label: 'Number', + mask: false, + spellcheck: true, + tableView: false, + delimiter: false, + requireDecimal: false, + inputFormat: 'plain', + key: 'number11', + type: 'number', + input: true, + }); + Formio.createForm(element, form) + .then((form) => { + const editGrid = form.getComponent('editGrid'); + assert.equal( + editGrid.refs['editgrid-editGrid-addRow'].length, + 0, + 'Should not have add row btn', + ); + const numberComp = form.getComponent('number11'); + const inputEvent = new Event('input'); + const numberInput = numberComp.refs.input[0]; + numberInput.value = 5; + numberInput.dispatchEvent(inputEvent); - document.innerHTML = ''; - done(); - }, 300); - }, 300); - }, 150); - }, 100); - }, 100); - }).catch(done) - .finally(() => { - delete ModalEditGrid.components[0].rowDrafts; - }); + setTimeout(() => { + assert.equal( + editGrid.refs['editgrid-editGrid-addRow'].length, + 1, + 'Should have add row btn', + ); + + document.innerHTML = ''; + done(); + }, 400); + }) + .catch(done); }); - it('Should keep fields valid inside NestedForms if drafts are enabled part two', function(done) { - const formElement = document.createElement('div'); - const form = new Webform(formElement); - ModalEditGrid.components[0].rowDrafts = true; + it('Should use custom text for save/cancel/add btns', function (done) { + const form = _.cloneDeep(comp10); + const element = document.createElement('div'); + form.components[0].addAnother = 'add custom'; + form.components[0].saveRow = 'save custom'; + form.components[0].removeRow = 'cancel custom'; + + Formio.createForm(element, form) + .then((form) => { + const editGrid = form.getComponent('editGrid'); + assert.equal( + editGrid.refs[ + 'editgrid-editGrid-addRow' + ][0].textContent.trim(), + 'add custom', + ); + const clickEvent = new Event('click'); + editGrid.refs['editgrid-editGrid-addRow'][0].dispatchEvent( + clickEvent, + ); - form.setForm(ModalEditGrid).then(() => { - const editGrid = form.components[0]; - // 1. Add a row - editGrid.addRow(); + setTimeout(() => { + assert.equal( + editGrid.refs[ + 'editgrid-editGrid-saveRow' + ][0].textContent.trim(), + 'save custom', + ); + assert.equal( + editGrid.refs[ + 'editgrid-editGrid-cancelRow' + ][0].textContent.trim(), + 'cancel custom', + ); - setTimeout(() => { - const editRow = editGrid.editRows[0]; - const dialog = editRow.dialog; + document.innerHTML = ''; + done(); + }, 400); + }) + .catch(done); + }); - // 2. Save the row - Harness.dispatchEvent('click', dialog, '.btn.btn-primary'); + it('Should render headers when openWhenEmpry is enabled', function (done) { + const form = _.cloneDeep(comp11); + const element = document.createElement('div'); + + Formio.createForm(element, form) + .then((form) => { + const editGrid = form.getComponent('editGrid'); + const rowComponents = editGrid.component.components; + const headerEls = + editGrid.element.querySelector('.list-group-header') + .firstElementChild.children; + assert.equal(headerEls.length, rowComponents.length); + for (let index = 0; index < headerEls.length; index++) { + const el = headerEls[index]; + assert.equal( + el.textContent.trim(), + rowComponents[index].label, + `Should render ${rowComponents[index].key} component label in header`, + ); + } + done(); + }) + .catch(done); + }); - setTimeout(() => { - // 3. Submit the form - Harness.dispatchEvent('click', form.element, '[name="data[submit]"]'); + it('Should show validation when saving a row with required conditional filed inside container', function (done) { + const form = _.cloneDeep(comp12); + const element = document.createElement('div'); - setTimeout(() => { - assert.equal(editGrid.errors.length, 3, 'Should be validated after an attempt to submit'); - assert.equal(editGrid.editRows[0].errors.length, 2, 'Should dd errors to the row after an attempt to submit'); - const rows = editGrid.element.querySelectorAll('[ref="editgrid-editGrid-row"]'); - const firstRow = rows[0]; - Harness.dispatchEvent('click', firstRow, '.editRow'); - - setTimeout(() => { - assert(form.submitted, 'Form should be submitted'); - const editRow = editGrid.editRows[0]; - assert(editRow.alerts, 'Should add an error alert to the modal'); - assert.equal(editRow.errors.length, 2, 'Should add errors to components inside draft row aftre it was submitted'); - const textField = editRow.components[0].getComponent('textField'); - - const alert = editGrid.alert; - assert(alert, 'Should show an error alert when drafts are enabled and form is submitted'); - assert(textField.element.className.includes('has-error'), 'Should add error class to component even when drafts enabled if the form was submitted'); - - // 4. Change the value of the text field - textField.setValue('new value', { modified: true }); + Formio.createForm(element, form) + .then((form) => { + const editGrid = form.getComponent('editGrid'); + const clickEvent = new Event('click'); + editGrid.refs['editgrid-editGrid-addRow'][0].dispatchEvent( + clickEvent, + ); setTimeout(() => { - const textFieldEl = textField.element; - assert.equal(textField.dataValue, 'new value'); - assert(!textFieldEl.className.includes('has-error'), 'Should remove an error class from component when it was fixed'); - const editRow = editGrid.editRows[0]; - const textField2 = editRow.components[0].getComponent('textField2'); + const firstRowContainer = editGrid.components[0]; + const firstRowNumber = firstRowContainer.components[0]; + const firstRowTextField = firstRowContainer.components[1]; - textField2.setValue('test val', { modified: true }); + assert.equal(firstRowTextField.visible, false); - setTimeout(() => { - assert.equal(textField2.dataValue, 'test val'); - assert(!textField2.element.className.includes('has-error'), 'Should remove an error class from component when it was fixed'); + const inputEvent = new Event('input'); + const numberInput = firstRowNumber.refs.input[0]; - editGrid.saveRow(0); + numberInput.value = 5; + numberInput.dispatchEvent(inputEvent); setTimeout(() => { - assert(!form.alert, 'Should remove an error alert after all the rows were fixed'); - - const rows = editGrid.element.querySelectorAll('[ref="editgrid-editGrid-row"]'); - const firstRow = rows[0]; - Harness.dispatchEvent('click', firstRow, '.editRow'); - setTimeout(() => { - const editRow = editGrid.editRows[0]; - const textField2 = editRow.components[0].getComponent('textField2'); + assert.equal(firstRowTextField.visible, true); + editGrid.refs[ + 'editgrid-editGrid-saveRow' + ][0].dispatchEvent(clickEvent); - Harness.dispatchEvent( - 'input', - editRow.dialog, - '[name="data[textField2]"', - (i) => i.value = '' - ); setTimeout(() => { - assert.equal(textField2.dataValue, ''); - Harness.dispatchEvent( - 'click', - editGrid.editRows[0].dialog, - `.editgrid-row-modal-${editGrid.id} [ref="dialogClose"]` - ); - setTimeout(() => { - const dialog = editGrid.editRows[0].confirmationDialog; + assert.equal(!!firstRowTextField.error, true); + assert.equal(editGrid.editRows[0].errors.length, 1); + assert.equal(editGrid.editRows[0].state, 'new'); - Harness.dispatchEvent('click', dialog, '[ref="dialogYesButton"]'); - - setTimeout(() => { - assert( - !document.querySelector(`#error-list-${form.id}`), - 'Should not add an error alert when the changes that made the row invalid, were discarded by Cancel' - ); - document.innerHTML = ''; - done(); - }, 100); - }, 100); + document.innerHTML = ''; + done(); }, 200); - }, 300); - }, 300); - }, 300); + }, 250); }, 300); - }, 450); - }, 250); - }, 100); - }, 100); - }).catch(done) - .finally(() => { - delete ModalEditGrid.components[0].rowDrafts; - }); + }) + .catch(done); }); - // it('', (done) => { - // const formElement = document.createElement('div'); - // const form = new Webform(formElement); - // form.setForm(ModalEditGrid).then(() => { - // - // }).catch(done); - // }); - }); - - it('Test simple conditions based on the EditGrid\'s child\'s value and default values when adding rows', function(done) { - const formElement = document.createElement('div'); - const form = new Webform(formElement); - form.setForm({ display: 'form', components: [comp7], type: 'form' }).then(() => { - const component = form.getComponent(['editGrid']); - component.addRow(); - setTimeout(() => { - Harness.getInputValue(component, 'data[editGrid][0][checkbox]', true, 'checked'); - Harness.testComponentVisibility(component, '.formio-component-editGridChild', true); - Harness.testComponentVisibility(component, '.formio-component-panelChild', true); - done(); - }, 250); - }).catch(done); - }); - - it('Test clearOnHide inside EditGrid', function(done) { - const formElement = document.createElement('div'); - const form = new Webform(formElement); - form.setForm({ display: 'form', components: [comp7], type: 'form' }).then(() => { - form.submission = { - data: { - editGrid: [ - { - checkbox: true, - editGridChild: 'Has Value', - panelChild: 'Has Value Too', - } - ] - } - }; - setTimeout(() => { - const editGrid = form.getComponent(['editGrid']); - editGrid.editRow(0).then(() => { - Harness.dispatchEvent('click', editGrid.element, '[name="data[editGrid][0][checkbox]"]', el => el.checked = false); - setTimeout(() => { - Harness.testComponentVisibility(editGrid, '.formio-component-editGridChild', false); - Harness.testComponentVisibility(editGrid, '.formio-component-panelChild', false); - editGrid.saveRow(0, true); - setTimeout(() => { - assert(!form.data.editGrid[0].editGridChild, 'Should be cleared'); - assert(!form.data.editGrid[0].panelChild, 'Should be cleared'); - done(); - }, 150); - }, 150); - }, 150); - }); - }).catch(done); - }); - - it('Test refreshing inside EditGrid', function(done) { - const formElement = document.createElement('div'); - const form = new Webform(formElement); - form.setForm({ display: 'form', components: [comp8], type: 'form' }).then(() => { - const editGrid = form.getComponent(['editGrid1']); - editGrid.addRow(); - const makeSelect = form.getComponent(['editGrid1', 0, 'make']); - const modelSelect = form.getComponent(['editGrid1', 0, 'model']); - makeSelect.setValue('ford'); - setTimeout(() => { - modelSelect.setValue('Focus'); - setTimeout(() => { - editGrid.saveRow(0, true); - setTimeout(() => { - assert.equal(form.data.editGrid1[0].model, 'Focus', 'Should be saved properly'); - done(); - }, 150); - }, 100); - }, 150); - }).catch(done); - }); - - it('Should display summary with values only for components that are visible at least in one row', function(done) { - const formElement = document.createElement('div'); - const form = new Webform(formElement); - form.setForm(comp9).then(() => { - const editGrid = form.getComponent('editGrid'); - - const checkRows = (columnsNumber, rowsNumber) => { - const rowWithColumns = editGrid.element.querySelector('.row'); - const rowsWithValues = editGrid.element.querySelectorAll('[ref="editgrid-editGrid-row"]'); - - assert.equal(rowWithColumns.children.length, columnsNumber, 'Row should contain values only for visible components'); - assert.equal(rowsWithValues.length, rowsNumber, 'Should have corrent number of rows'); - }; - - checkRows(2, 0); - form.setValue({ - data: { - editGrid: [ - { textField: 'test1', checkbox: false }, - { textField: 'test2', checkbox: false }, - ], - } - }); - setTimeout(() => { - checkRows(2, 2); - form.setValue({ - data: { - editGrid: [ - { textField: 'test1', checkbox: false }, - { textField: 'test2', checkbox: true }, - ], - } - }); + it('Should render form with a submission in a draft-state without validation errors', function (done) { + const form = _.cloneDeep(comp13); + const element = document.createElement('div'); + + Formio.createForm(element, form) + .then((form) => { + form.submission = { + data: { + container: { + textField: '', + }, + editGrid: [], + }, + }; - setTimeout(() => { - checkRows(3, 2); - form.setValue({ - data: { - editGrid: [ - { textField: 'test1', checkbox: false }, - { textField: 'test2', checkbox: true, textArea: 'test22' }, - { textField: 'show', checkbox: true, container: { number1: 1111 }, textArea: 'test3' } - ], - } - }); - - setTimeout(() => { - checkRows(4, 3); - form.setValue({ - data: { - editGrid: [ - { textField: 'test1', checkbox: false }, - { textField: 'test2', checkbox: false }, - { textField: 'show', checkbox: false, container: { number1: 1111 } } - ], - } - }); + setTimeout(() => { + const editGrid = form.getComponent(['editGrid']); + assert.equal(editGrid.errors.length, 0); + done(); + }, 100); + }) + .catch(done); + }); - setTimeout(() => { - checkRows(3, 3); + it('Should keep value for conditional editGrid on setValue when server option is provided', function (done) { + const element = document.createElement('div'); - done(); - }, 300); - }, 300); - }, 300); - }, 300); - }).catch(done); - }); - - it('Should add component to the header only if it is visible in saved row', function(done) { - const formElement = document.createElement('div'); - const form = new Webform(formElement); - form.setForm(comp9).then(() => { - const editGrid = form.getComponent('editGrid'); - - const checkHeader = (componentsNumber) => { - const header = editGrid.element.querySelector('.list-group-header').querySelector('.row'); - - assert.equal(editGrid.visibleInHeader.length, componentsNumber); - assert.equal(header.children.length, componentsNumber); - }; - - const clickElem = (elem) => { - const clickEvent = new Event('click'); - elem.dispatchEvent(clickEvent); - }; - const clickAddRow = () => { - const addAnotherBtn = editGrid.refs['editgrid-editGrid-addRow'][0]; - clickElem(addAnotherBtn); - }; - - checkHeader(2); - clickAddRow(); - - setTimeout(() => { - assert.equal(editGrid.editRows.length, 1); - checkHeader(2); - const checkbox = editGrid.getComponent('checkbox')[0]; - checkbox.setValue(true); - - setTimeout(() => { - checkHeader(2); - assert.equal(editGrid.getComponent('textArea')[0].visible, true); - clickAddRow(); - - setTimeout(() => { - assert.equal(editGrid.editRows.length, 2); - checkHeader(2); - const saveFirstRowBtn = editGrid.refs['editgrid-editGrid-saveRow'][0]; - clickElem(saveFirstRowBtn); + Formio.createForm(element, formsWithEditGridAndConditions.form1, { + server: true, + }) + .then((form) => { + const formData = { + checkbox: true, + radio: 'yes', + editGrid: [ + { textField: 'test', number: 4 }, + { textField: 'test1', number: 5 }, + ], + }; + + form.setValue({ data: _.cloneDeep(formData) }); - setTimeout(() => { - assert.equal(editGrid.editRows[0].state, 'saved'); - checkHeader(3); + setTimeout(() => { + const editGrid = form.getComponent('editGrid'); + assert.deepEqual(editGrid.dataValue, formData.editGrid); - done(); - }, 300); - }, 300); - }, 300); - }, 300); - }).catch(done); - }).timeout(3000); - - it('Should add/save/cancel/delete/edit rows', function(done) { - const form = _.cloneDeep(comp10); - const element = document.createElement('div'); - - Formio.createForm(element, form).then(form => { - const editGrid = form.getComponent('editGrid'); - - const click = (btn, index, selector) => { - let elem; - - if (selector) { - elem = editGrid.element.querySelectorAll(`.${btn}`)[index]; - } - else { - elem = editGrid.refs[`editgrid-editGrid-${btn}`][index]; - } - - const clickEvent = new Event('click'); - elem.dispatchEvent(clickEvent); - }; - - assert.equal(editGrid.refs['editgrid-editGrid-row'].length, 0, 'Should not have rows'); - assert.equal(editGrid.editRows.length, 0, 'Should not have rows'); - - click('addRow', 0); - - setTimeout(() => { - assert.equal(editGrid.refs['editgrid-editGrid-row'].length, 1, 'Should have 1 row'); - assert.equal(editGrid.editRows.length, 1, 'Should have 1 row'); - assert.equal(editGrid.editRows[0].state, 'new', 'Should have state "new"'); - editGrid.editRows[0].components.forEach((comp) => { - comp.setValue(11111); - }); + done(); + }, 500); + }) + .catch(done); + }); - setTimeout(() => { - assert.deepEqual(editGrid.editRows[0].data, { number: 11111, textField: '11111' }, 'Should set row data'); - click('saveRow', 0); + it('Should set value for conditional editGrid inside editGrid on event when form is not pristine ', function (done) { + const element = document.createElement('div'); - setTimeout(() => { - assert.equal(editGrid.refs['editgrid-editGrid-row'].length, 1, 'Should have 1 row'); - assert.equal(editGrid.editRows.length, 1, 'Should have 1 row'); - assert.equal(editGrid.editRows[0].state, 'saved', 'Should have state "saved"'); - assert.deepEqual(editGrid.editRows[0].data, { number: 11111, textField: '11111' }, 'Should set row data'); - click('editRow', 0, true); + Formio.createForm(element, formsWithEditGridAndConditions.form2) + .then((form) => { + form.setPristine(false); + const editGrid1 = form.getComponent('editGrid1'); + editGrid1.addRow(); - setTimeout(() => { - assert.equal(editGrid.refs['editgrid-editGrid-row'].length, 1, 'Should have 1 row'); - assert.equal(editGrid.editRows.length, 1, 'Should have 1 row'); - assert.equal(editGrid.editRows[0].state, 'editing', 'Should have state "editing"'); - editGrid.editRows[0].components.forEach((comp) => { - comp.setValue(22222); - }); + setTimeout(() => { + const btn = editGrid1.getComponent('setPanelValue')[0]; + const clickEvent = new Event('click'); + btn.refs.button.dispatchEvent(clickEvent); + setTimeout(() => { + const conditionalEditGrid = + editGrid1.getComponent('editGrid')[0]; + assert.deepEqual(conditionalEditGrid.dataValue, [ + { textField: 'testyyyy' }, + ]); + assert.equal(conditionalEditGrid.editRows.length, 1); + done(); + }, 500); + }, 300); + }) + .catch(done); + }); + + it('Should keep value for conditional editGrid in tabs on setValue when server option is provided', function (done) { + const element = document.createElement('div'); - setTimeout(() => { - assert.deepEqual(editGrid.editRows[0].data, { number: 22222, textField: '22222' }, 'Should set row data'); - click('cancelRow', 0); + Formio.createForm(element, formsWithEditGridAndConditions.form3, { + server: true, + }) + .then((form) => { + const formData = { + affectedRiskTypes: { + creditRisk: false, + marketRisk: true, + operationalRisk: false, + counterpartyCreditRisk: false, + creditValuationRiskAdjustment: false, + }, + rwaImpact: 'yes', + submit: true, + mr: { + quantitativeInformation: { + cva: 'yes', + sameRiskCategories: false, + impactsPerEntity: [{ number: 123 }], + sameImpactAcrossEntities: false, + }, + }, + euParentInstitution: 'EUParent', + }; + + form.setValue({ data: _.cloneDeep(formData) }); setTimeout(() => { - assert.equal(editGrid.refs['editgrid-editGrid-row'].length, 1, 'Should have 1 row'); - assert.equal(editGrid.editRows.length, 1, 'Should have 1 row'); - assert.equal(editGrid.editRows[0].state, 'saved', 'Should have state "saved"'); - assert.deepEqual(editGrid.editRows[0].data, { number: 11111, textField: '11111' }, 'Should cancel changed data'); - click('removeRow', 0, true); + const editGrid = form.getComponent('impactsPerEntity'); + assert.deepEqual( + editGrid.dataValue, + formData.mr.quantitativeInformation.impactsPerEntity, + ); + assert.deepEqual(editGrid.editRows.length, 1); + + done(); + }, 500); + }) + .catch(done); + }); - setTimeout(() => { - assert.equal(editGrid.refs['editgrid-editGrid-row'].length, 0, 'Should not have rows'); - assert.equal(editGrid.editRows.length, 0, 'Should have 0 rows'); + it('Should calculate editGrid value when calculateOnServer is enabled and server option is passed', function (done) { + const element = document.createElement('div'); + + Formio.createForm(element, formsWithEditGridAndConditions.form4, { + server: true, + }) + .then((form) => { + const editGrid = form.getComponent('editGrid'); + assert.deepEqual(editGrid.dataValue, [{ textArea: 'test' }]); + assert.deepEqual(editGrid.editRows.length, 1); + done(); + }) + .catch(done); + }); + + it('Should keep value for conditional editGrid deeply nested in panels and containers on setValue when server option is provided', function (done) { + const element = document.createElement('div'); + + Formio.createForm(element, formsWithEditGridAndConditions.form5, { + server: true, + }) + .then((form) => { + const formData = { + generalInformation: { + listSupervisedEntitiesCovered: [ + { + id: 6256, + longName: 'Bank_DE', + leiCode: 'LEI6256', + countryCode: 'DE', + }, + ], + deSpecific: { + criticalPartsToBeOutsourcedSuboutsourcer: 'yes', + suboutsourcers: [ + { nameSuboutsourcer: 'test' }, + { nameSuboutsourcer: 'test 1' }, + ], + }, + }, + }; + + form.setValue({ data: _.cloneDeep(formData) }); + + setTimeout(() => { + const editGrid = form.getComponent('suboutsourcers'); + assert.deepEqual( + editGrid.dataValue, + formData.generalInformation.deSpecific.suboutsourcers, + ); + assert.deepEqual(editGrid.editRows.length, 2); - document.innerHTML = ''; done(); - }, 200); - }, 200); - }, 200); - }, 200); - }, 200); - }, 200); - }, 200); - }).catch(done); - }).timeout(3000); - - it('Should open first row when empty and allow saving openned row', function(done) { - const form = _.cloneDeep(comp10); - const element = document.createElement('div'); - form.components[0].openWhenEmpty = true; - - Formio.createForm(element, form).then(form => { - const editGrid = form.getComponent('editGrid'); - - const click = (btn, index, selector) => { - let elem; - - if (selector) { - elem = editGrid.element.querySelectorAll(`.${btn}`)[index]; - } - else { - elem = editGrid.refs[`editgrid-editGrid-${btn}`][index]; - } - - const clickEvent = new Event('click'); - elem.dispatchEvent(clickEvent); - }; - - assert.equal(editGrid.refs['editgrid-editGrid-row'].length, 1, 'Should have 1 row'); - assert.equal(editGrid.editRows.length, 1, 'Should have 1 row'); - assert.equal(editGrid.editRows[0].state, 'new', 'Should have state "new"'); - click('saveRow', 0); - - setTimeout(() => { - assert.equal(editGrid.refs['editgrid-editGrid-row'].length, 1, 'Should have 1 row'); - assert.equal(editGrid.editRows.length, 1, 'Should have 1 row'); - assert.equal(editGrid.editRows[0].state, 'saved', 'Should have state "saved"'); - - document.innerHTML = ''; - done(); - }, 200); - }).catch(done); - }).timeout(3000); - - it('Should disable adding/removing rows', function(done) { - const form = _.cloneDeep(comp10); - const element = document.createElement('div'); - form.components[0].disableAddingRemovingRows = true; - const value = [{ number: 1, textField: 'test' }, { number: 2, textField: 'test2' }]; - - Formio.createForm(element, form).then(form => { - const editGrid = form.getComponent('editGrid'); - editGrid.setValue(value); - - setTimeout(() => { - assert.equal(editGrid.refs['editgrid-editGrid-row'].length, 2, 'Should have 2 rows'); - assert.equal(editGrid.editRows.length, 2, 'Should have 2 rows'); - assert.equal(editGrid.refs['editgrid-editGrid-addRow'].length, 0, 'Should not have add row btn'); - assert.equal(editGrid.element.querySelectorAll('.removeRow').length, 0, 'Should not have remove row btn'); - - document.innerHTML = ''; - done(); - }, 200); - }).catch(done); - }); - - it('Should show conditional eddRow btn if condition is met', function(done) { - const form = _.cloneDeep(comp10); - const element = document.createElement('div'); - form.components[0].conditionalAddButton = 'show = data.number11 === 5'; - form.components.unshift({ - label: 'Number', - mask: false, - spellcheck: true, - tableView: false, - delimiter: false, - requireDecimal: false, - inputFormat: 'plain', - key: 'number11', - type: 'number', - input: true + }, 500); + }) + .catch(done); }); - Formio.createForm(element, form).then(form => { - const editGrid = form.getComponent('editGrid'); - assert.equal(editGrid.refs['editgrid-editGrid-addRow'].length, 0, 'Should not have add row btn'); - const numberComp = form.getComponent('number11'); - const inputEvent = new Event('input'); - const numberInput = numberComp.refs.input[0]; - numberInput.value = 5; - numberInput.dispatchEvent(inputEvent); - - setTimeout(() => { - assert.equal(editGrid.refs['editgrid-editGrid-addRow'].length, 1, 'Should have add row btn'); - - document.innerHTML = ''; - done(); - }, 400); - }).catch(done); - }); - - it('Should use custom text for save/cancel/add btns', function(done) { - const form = _.cloneDeep(comp10); - const element = document.createElement('div'); - form.components[0].addAnother = 'add custom'; - form.components[0].saveRow = 'save custom'; - form.components[0].removeRow = 'cancel custom'; - - Formio.createForm(element, form).then(form => { - const editGrid = form.getComponent('editGrid'); - assert.equal(editGrid.refs['editgrid-editGrid-addRow'][0].textContent.trim(), 'add custom'); - const clickEvent = new Event('click'); - editGrid.refs['editgrid-editGrid-addRow'][0].dispatchEvent(clickEvent); - - setTimeout(() => { - assert.equal(editGrid.refs['editgrid-editGrid-saveRow'][0].textContent.trim(), 'save custom'); - assert.equal(editGrid.refs['editgrid-editGrid-cancelRow'][0].textContent.trim(), 'cancel custom'); - - document.innerHTML = ''; - done(); - }, 400); - }).catch(done); - }); - - it('Should render headers when openWhenEmpry is enabled', function(done) { - const form = _.cloneDeep(comp11); - const element = document.createElement('div'); - - Formio.createForm(element, form).then(form => { - const editGrid = form.getComponent('editGrid'); - const rowComponents = editGrid.component.components; - const headerEls = editGrid.element.querySelector('.list-group-header').firstElementChild.children; - assert.equal(headerEls.length, rowComponents.length); - for (let index = 0; index < headerEls.length; index++) { - const el = headerEls[index]; - assert.equal(el.textContent.trim(), rowComponents[index].label, `Should render ${rowComponents[index].key} component label in header`); - } - done(); - }).catch(done); - }); - - it('Should show validation when saving a row with required conditional filed inside container', function(done) { - const form = _.cloneDeep(comp12); - const element = document.createElement('div'); - - Formio.createForm(element, form).then(form => { - const editGrid = form.getComponent('editGrid'); - const clickEvent = new Event('click'); - editGrid.refs['editgrid-editGrid-addRow'][0].dispatchEvent(clickEvent); - - setTimeout(() => { - const firstRowContainer = editGrid.components[0]; - const firstRowNumber = firstRowContainer.components[0]; - const firstRowTextField = firstRowContainer.components[1]; - - assert.equal(firstRowTextField.visible, false); - - const inputEvent = new Event('input'); - const numberInput = firstRowNumber.refs.input[0]; - - numberInput.value = 5; - numberInput.dispatchEvent(inputEvent); - - setTimeout(() => { - assert.equal(firstRowTextField.visible, true); - editGrid.refs['editgrid-editGrid-saveRow'][0].dispatchEvent(clickEvent); - - setTimeout(() => { - assert.equal(!!firstRowTextField.error, true); - assert.equal(editGrid.editRows[0].errors.length, 1); - assert.equal(editGrid.editRows[0].state, 'new'); - - document.innerHTML = ''; - done(); - }, 200); - }, 250); - }, 300); - }).catch(done); - }); - - it('Should render form with a submission in a draft-state without validation errors', function(done) { - const form = _.cloneDeep(comp13); - const element = document.createElement('div'); - - Formio.createForm(element, form).then(form => { - form.submission = { - data: { - 'container': { - 'textField': '', - }, - 'editGrid': [] - } - }; - - setTimeout(() => { - const editGrid = form.getComponent(['editGrid']); - assert.equal(editGrid.errors.length, 0); - done(); - }, 100); - }).catch(done); - }); - - it('Should keep value for conditional editGrid on setValue when server option is provided', function(done) { - const element = document.createElement('div'); - - Formio.createForm(element, formsWithEditGridAndConditions.form1, { server: true }).then(form => { - const formData = { - checkbox: true, - radio: 'yes', - editGrid: [ - { textField: 'test', number: 4 }, - { textField: 'test1', number: 5 }, - ], - }; - - form.setValue({ data: _.cloneDeep(formData) }); - - setTimeout(() => { - const editGrid = form.getComponent('editGrid'); - assert.deepEqual(editGrid.dataValue, formData.editGrid); - - done(); - }, 500); - }).catch(done); - }); - - it('Should set value for conditional editGrid inside editGrid on event when form is not pristine ', function(done) { - const element = document.createElement('div'); - - Formio.createForm(element, formsWithEditGridAndConditions.form2).then(form => { - form.setPristine(false); - const editGrid1 = form.getComponent('editGrid1'); - editGrid1.addRow(); - - setTimeout(() => { - const btn = editGrid1.getComponent('setPanelValue')[0]; - const clickEvent = new Event('click'); - btn.refs.button.dispatchEvent(clickEvent); - setTimeout(() => { - const conditionalEditGrid = editGrid1.getComponent('editGrid')[0]; - assert.deepEqual(conditionalEditGrid.dataValue, [{ textField:'testyyyy' }]); - assert.equal(conditionalEditGrid.editRows.length, 1); - done(); - }, 500); - }, 300); - }).catch(done); - }); - - it('Should keep value for conditional editGrid in tabs on setValue when server option is provided', function(done) { - const element = document.createElement('div'); - - Formio.createForm(element, formsWithEditGridAndConditions.form3, { server: true }).then(form => { - const formData = { - affectedRiskTypes: { - creditRisk: false, - marketRisk: true, - operationalRisk: false, - counterpartyCreditRisk: false, - creditValuationRiskAdjustment: false, - }, - rwaImpact: 'yes', - submit: true, - mr: { - quantitativeInformation: { - cva: 'yes', - sameRiskCategories: false, - impactsPerEntity: [{ number: 123 }], - sameImpactAcrossEntities: false, - }, - }, - euParentInstitution: 'EUParent', - }; - - form.setValue({ data: _.cloneDeep(formData) }); - - setTimeout(() => { - const editGrid = form.getComponent('impactsPerEntity'); - assert.deepEqual(editGrid.dataValue, formData.mr.quantitativeInformation.impactsPerEntity); - assert.deepEqual(editGrid.editRows.length, 1); - - done(); - }, 500); - }).catch(done); - }); - - it('Should calculate editGrid value when calculateOnServer is enabled and server option is passed', function(done) { - const element = document.createElement('div'); - - Formio.createForm(element, formsWithEditGridAndConditions.form4, { server: true }).then(form => { - const editGrid = form.getComponent('editGrid'); - assert.deepEqual(editGrid.dataValue, [{ textArea: 'test' }]); - assert.deepEqual(editGrid.editRows.length, 1); - done(); - }).catch(done); - }); - - it('Should keep value for conditional editGrid deeply nested in panels and containers on setValue when server option is provided', function(done) { - const element = document.createElement('div'); - - Formio.createForm(element, formsWithEditGridAndConditions.form5, { server: true }).then(form => { - const formData = { - generalInformation: { - listSupervisedEntitiesCovered: [ - { id: 6256, longName: 'Bank_DE', leiCode: 'LEI6256', countryCode: 'DE' }, - ], - deSpecific: { - criticalPartsToBeOutsourcedSuboutsourcer: 'yes', - suboutsourcers: [ - { nameSuboutsourcer: 'test' }, - { nameSuboutsourcer: 'test 1' }, - ], - }, - }, - }; - - form.setValue({ data: _.cloneDeep(formData) }); - - setTimeout(() => { - const editGrid = form.getComponent('suboutsourcers'); - assert.deepEqual(editGrid.dataValue, formData.generalInformation.deSpecific.suboutsourcers); - assert.deepEqual(editGrid.editRows.length, 2); - - done(); - }, 500); - }).catch(done); - }); - - it('Should calculate editGrid value when condition is met in advanced logic', function(done) { - const element = document.createElement('div'); - - Formio.createForm(element, formsWithEditGridAndConditions.form6).then(form => { - form.getComponent('textField').setValue('show'); - - setTimeout(() => { - const editGrid = form.getComponent('editGrid'); - assert.deepEqual(editGrid.dataValue, [{ number:1, textArea: 'test' }, { number:2, textArea: 'test2' }]); - assert.deepEqual(editGrid.editRows.length, 2); - - done(); - }, 300); - }).catch(done); - }); -}); -describe('EditGrid Open when Empty', function() { - it('Should be opened when shown conditionally', function(done) { - const formElement = document.createElement('div'); - Formio.createForm(formElement, withOpenWhenEmptyAndConditions) - .then((form) => { - const radio = form.getComponent(['radio']); - radio.setValue('show'); - - setTimeout(() => { - const editGrid = form.getComponent(['editGrid']); - assert.equal(editGrid.visible, true, 'Should be visible'); - assert.equal(editGrid.editRows.length, 1, 'Should have 1 row'); - const textField = editGrid.editRows[0].components[0]; - Harness.dispatchEvent( - 'input', - textField.element, - '[name="data[editGrid][0][textField]"]', - (input) => input.value = 'Value' - ); - - setTimeout(() => { - const row = editGrid.editRows[0]; - assert.equal(row.data.textField, 'Value', 'Value should be set properly'); - editGrid.saveRow(0); - setTimeout(() => { - assert.deepEqual(form.data.editGrid, [{ textField: 'Value', select1: '' }], 'Value should be saved correctly'); - radio.setValue('hide'); + it('Should calculate editGrid value when condition is met in advanced logic', function (done) { + const element = document.createElement('div'); + + Formio.createForm(element, formsWithEditGridAndConditions.form6) + .then((form) => { + form.getComponent('textField').setValue('show'); - setTimeout(() => { - assert.equal(editGrid.visible, false, 'Should be hidden'); + setTimeout(() => { + const editGrid = form.getComponent('editGrid'); + assert.deepEqual(editGrid.dataValue, [ + { number: 1, textArea: 'test' }, + { number: 2, textArea: 'test2' }, + ]); + assert.deepEqual(editGrid.editRows.length, 2); + + done(); + }, 300); + }) + .catch(done); + }); +}); + +describe('EditGrid Open when Empty', function () { + it('Should be opened when shown conditionally', function (done) { + const formElement = document.createElement('div'); + Formio.createForm(formElement, withOpenWhenEmptyAndConditions) + .then((form) => { + const radio = form.getComponent(['radio']); radio.setValue('show'); setTimeout(() => { - assert.equal(editGrid.visible, true, 'Should be visible'); - assert.equal(editGrid.editRows.length, 1, 'Should have 1 row'); - assert.equal(editGrid.editRows[0].state, 'new', 'Row should be a new one'); - done(); + const editGrid = form.getComponent(['editGrid']); + assert.equal(editGrid.visible, true, 'Should be visible'); + assert.equal( + editGrid.editRows.length, + 1, + 'Should have 1 row', + ); + const textField = editGrid.editRows[0].components[0]; + Harness.dispatchEvent( + 'input', + textField.element, + '[name="data[editGrid][0][textField]"]', + (input) => (input.value = 'Value'), + ); + + setTimeout(() => { + const row = editGrid.editRows[0]; + assert.equal( + row.data.textField, + 'Value', + 'Value should be set properly', + ); + editGrid.saveRow(0); + setTimeout(() => { + assert.deepEqual( + form.data.editGrid, + [{ textField: 'Value', select1: '' }], + 'Value should be saved correctly', + ); + radio.setValue('hide'); + + setTimeout(() => { + assert.equal( + editGrid.visible, + false, + 'Should be hidden', + ); + radio.setValue('show'); + + setTimeout(() => { + assert.equal( + editGrid.visible, + true, + 'Should be visible', + ); + assert.equal( + editGrid.editRows.length, + 1, + 'Should have 1 row', + ); + assert.equal( + editGrid.editRows[0].state, + 'new', + 'Row should be a new one', + ); + done(); + }, 300); + }, 300); + }, 250); + }, 350); }, 300); - }, 300); - }, 250); - }, 350); - }, 300); - }) - .catch(done); - }); - - it('Should create new row with empty data and no defaults', function(done) { - const formElement = document.createElement('div'); - Formio.createForm(formElement, compOpenWhenEmpty, { noDefaults: true }) - .then((form) => { - form.data = {}; - setTimeout(() => { - const editGrid = form.getComponent(['editGrid']); - assert.equal(editGrid.editRows.length, 1); - assert.equal(editGrid.editRows[0].state, 'new'); - done(); - }, 300); - }) - .catch(done); - }); - - it('Should always add a first row', function(done) { - const formElement = document.createElement('div'); - Formio.createForm(formElement, compOpenWhenEmpty) - .then((form) => { - const editGrid = form.getComponent(['editGrid']); - assert.equal(editGrid.editRows.length, 1, 'Should have 1 row on create'); - const textField = editGrid.editRows[0].components[0]; - Harness.dispatchEvent( - 'input', - textField.element, - '[name="data[editGrid][0][textField]"]', - (input) => input.value = 'Value' - ); + }) + .catch(done); + }); - setTimeout(() => { - const row = editGrid.editRows[0]; - assert.equal(row.data.textField, 'Value', 'Value should be set properly'); + it('Should create new row with empty data and no defaults', function (done) { + const formElement = document.createElement('div'); + Formio.createForm(formElement, compOpenWhenEmpty, { noDefaults: true }) + .then((form) => { + form.data = {}; + setTimeout(() => { + const editGrid = form.getComponent(['editGrid']); + assert.equal(editGrid.editRows.length, 1); + assert.equal(editGrid.editRows[0].state, 'new'); + done(); + }, 300); + }) + .catch(done); + }); - setTimeout(() => { - editGrid.cancelRow(0); + it('Should always add a first row', function (done) { + const formElement = document.createElement('div'); + Formio.createForm(formElement, compOpenWhenEmpty) + .then((form) => { + const editGrid = form.getComponent(['editGrid']); + assert.equal( + editGrid.editRows.length, + 1, + 'Should have 1 row on create', + ); + const textField = editGrid.editRows[0].components[0]; + Harness.dispatchEvent( + 'input', + textField.element, + '[name="data[editGrid][0][textField]"]', + (input) => (input.value = 'Value'), + ); - setTimeout(() => { - assert.equal(editGrid.editRows.length, 1, 'Should still have 1 row'); - const textField = editGrid.editRows[0].components[0]; - assert.equal(textField.dataValue, '', 'Value should be cleared after cancelling the row'); + setTimeout(() => { + const row = editGrid.editRows[0]; + assert.equal( + row.data.textField, + 'Value', + 'Value should be set properly', + ); - editGrid.saveRow(0); + setTimeout(() => { + editGrid.cancelRow(0); - setTimeout(() => { - assert.equal(editGrid.editRows.length, 1, 'Should have 1 row'); - assert.equal(editGrid.editRows[0].state === 'saved', 1, 'Row should be saved'); + setTimeout(() => { + assert.equal( + editGrid.editRows.length, + 1, + 'Should still have 1 row', + ); + const textField = + editGrid.editRows[0].components[0]; + assert.equal( + textField.dataValue, + '', + 'Value should be cleared after cancelling the row', + ); + + editGrid.saveRow(0); + + setTimeout(() => { + assert.equal( + editGrid.editRows.length, + 1, + 'Should have 1 row', + ); + assert.equal( + editGrid.editRows[0].state === 'saved', + 1, + 'Row should be saved', + ); - editGrid.removeRow(0); + editGrid.removeRow(0); + + setTimeout(() => { + assert.equal( + editGrid.editRows.length, + 1, + 'Should add the first row when delete the last one', + ); + assert.equal( + editGrid.editRows[0].state === 'new', + 1, + 'Should add the new row when the last one was deleted', + ); + + done(); + }, 250); + }, 250); + }, 250); + }, 250); + }, 250); + }) + .catch(done); + }); + it('Should restore focus on the proper component after change event', function (done) { + const formElement = document.createElement('div'); + Formio.createForm(formElement, compWithCustomDefaultValue) + .then((form) => { + const editGrid = form.getComponent(['selectedFunds2']); + editGrid.removeRow(2, true); setTimeout(() => { - assert.equal(editGrid.editRows.length, 1, 'Should add the first row when delete the last one'); - assert.equal(editGrid.editRows[0].state === 'new', 1, 'Should add the new row when the last one was deleted'); + assert.equal( + editGrid.editRows.length, + 4, + 'Should remove a row', + ); + editGrid.editRow(2); - done(); - }, 250); - }, 250); - }, 250); - }, 250); - }, 250); - }) - .catch(done); - }); - - it('Should restore focus on the proper component after change event', function(done) { - const formElement = document.createElement('div'); - Formio.createForm(formElement, compWithCustomDefaultValue) - .then((form) => { - const editGrid = form.getComponent(['selectedFunds2']); - editGrid.removeRow(2, true); - setTimeout(() => { - assert.equal(editGrid.editRows.length, 4, 'Should remove a row'); - editGrid.editRow(2); - - setTimeout(() => { - const currency = form.getComponent(['selectedFunds2', 2, 'allocationAmount2']); - currency.focus(); - currency.setValue(250); - editGrid.redraw(); + setTimeout(() => { + const currency = form.getComponent([ + 'selectedFunds2', + 2, + 'allocationAmount2', + ]); + currency.focus(); + currency.setValue(250); + editGrid.redraw(); - setTimeout(() => { - assert.equal(editGrid.editRows[2].state, 'editing', 'Should keep the row in the editing state'); - assert.equal(editGrid.editRows[3].state, 'saved', 'Should keep the next row in the saved state'); - done(); - }, 200); - }, 200); - }, 200); - }) - .catch(done); - }); - - it('Should submit form with empty rows when submit button is pressed and no rows are saved', function(done) { - const formElement = document.createElement('div'); - const form = new Webform(formElement); - - form.setForm(compOpenWhenEmpty).then(() => { - const editGrid = form.components[0]; - - setTimeout(() => { - Harness.dispatchEvent('click', form.element, '[name="data[submit]"]'); - setTimeout(() => { - const editRow = editGrid.editRows[0]; - assert(!editGrid.error, 'Should be no errors on EditGrid'); - assert.equal(editRow.errors, null, 'Should not be any errors on open row'); - assert.equal(form.submission.state, 'submitted', 'Form should be submitted'); - done(); - }, 450); - }, 100); - }).catch(done); - }); - - it('Should not submit form if any row inputs are set as required', function(done) { - const formElement = document.createElement('div'); - const form = new Webform(formElement); - - form.setForm(EditGridOpenWhenEmpty).then(() => { - const editGrid = form.components[0]; - - setTimeout(() => { - Harness.dispatchEvent('click', form.element, '[name="data[submit]"]'); - setTimeout(() => { - assert(!form.submission.state, 'Form should not be submitted'); - const editRow = editGrid.editRows[0]; - assert(editGrid.error, 'Should show error on EditGrid'); - assert.equal(editRow.errors.length, 1, 'Should show error on row'); - const textField = editRow.components[0]; - assert(textField.element.className.includes('formio-error-wrapper'), 'Should add error class to component'); - done(); - }, 450); - }, 100); - }).catch(done); - }); + setTimeout(() => { + assert.equal( + editGrid.editRows[2].state, + 'editing', + 'Should keep the row in the editing state', + ); + assert.equal( + editGrid.editRows[3].state, + 'saved', + 'Should keep the next row in the saved state', + ); + done(); + }, 200); + }, 200); + }, 200); + }) + .catch(done); + }); + + it('Should submit form with empty rows when submit button is pressed and no rows are saved', function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement); + + form.setForm(compOpenWhenEmpty) + .then(() => { + const editGrid = form.components[0]; + + setTimeout(() => { + Harness.dispatchEvent( + 'click', + form.element, + '[name="data[submit]"]', + ); + setTimeout(() => { + const editRow = editGrid.editRows[0]; + assert( + !editGrid.error, + 'Should be no errors on EditGrid', + ); + assert.equal( + editRow.errors, + null, + 'Should not be any errors on open row', + ); + assert.equal( + form.submission.state, + 'submitted', + 'Form should be submitted', + ); + done(); + }, 450); + }, 100); + }) + .catch(done); + }); + + it('Should not submit form if any row inputs are set as required', function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement); + + form.setForm(EditGridOpenWhenEmpty) + .then(() => { + const editGrid = form.components[0]; + + setTimeout(() => { + Harness.dispatchEvent( + 'click', + form.element, + '[name="data[submit]"]', + ); + setTimeout(() => { + assert( + !form.submission.state, + 'Form should not be submitted', + ); + const editRow = editGrid.editRows[0]; + assert(editGrid.error, 'Should show error on EditGrid'); + assert.equal( + editRow.errors.length, + 1, + 'Should show error on row', + ); + const textField = editRow.components[0]; + assert( + textField.element.className.includes( + 'formio-error-wrapper', + ), + 'Should add error class to component', + ); + done(); + }, 450); + }, 100); + }) + .catch(done); + }); }); diff --git a/src/components/editgrid/editForm/EditGrid.edit.data.js b/src/components/editgrid/editForm/EditGrid.edit.data.js index 0d29afba72..94d8f08784 100644 --- a/src/components/editgrid/editForm/EditGrid.edit.data.js +++ b/src/components/editgrid/editForm/EditGrid.edit.data.js @@ -1,18 +1,19 @@ export default [ - { - type: 'checkbox', - input: true, - weight: 105, - key: 'inlineEdit', - label: 'Inline Editing', - tooltip: 'Check this if you would like your changes within \'edit\' mode to be committed directly to the submission object as that row is being changed', - }, - { - key: 'defaultValue', - ignore: true, - }, - { - key: 'multiple', - ignore: true - }, + { + type: 'checkbox', + input: true, + weight: 105, + key: 'inlineEdit', + label: 'Inline Editing', + tooltip: + "Check this if you would like your changes within 'edit' mode to be committed directly to the submission object as that row is being changed", + }, + { + key: 'defaultValue', + ignore: true, + }, + { + key: 'multiple', + ignore: true, + }, ]; diff --git a/src/components/editgrid/editForm/EditGrid.edit.display.js b/src/components/editgrid/editForm/EditGrid.edit.display.js index 89f4bd8fd6..2e95baf325 100644 --- a/src/components/editgrid/editForm/EditGrid.edit.display.js +++ b/src/components/editgrid/editForm/EditGrid.edit.display.js @@ -1,54 +1,56 @@ import Evaluator from '../../../utils/Evaluator'; export default [ - { - key: 'placeholder', - ignore: true, - }, - { - type: 'checkbox', - label: 'Open First Row when Empty', - key: 'openWhenEmpty', - tooltip: 'Check this if you would like to open up the first row when the EditGrid is empty', - weight: 1000, - input: true, - conditional: { - json: { '!==': [{ var: 'data.modal' }, true] }, - } - }, - { - type: 'checkbox', - label: 'Disable Adding / Removing Rows', - key: 'disableAddingRemovingRows', - tooltip: 'Check if you want to hide Add Another button and Remove Row button', - weight: 1001, - input: true, - clearOnHide: false, - calculateValue: 'value = data.disableAddingRemovingRows;', - }, - { - type: 'checkbox', - label: 'Display EditGrid as Table', - key: 'displayAsTable', - tooltip: 'use Table Template', - weight: 1002, - input: false, - customConditional() { - return !Evaluator.noeval; + { + key: 'placeholder', + ignore: true, }, - }, - { - weight: 1010, - type: 'textarea', - input: true, - key: 'conditionalAddButton', - label: 'Conditional Add Button', - placeholder: 'show = ...', - tooltip: 'Specify condition when Add Button should be displayed.', - editor: 'ace', - as: 'javascript', - wysiwyg: { - minLines: 3, + { + type: 'checkbox', + label: 'Open First Row when Empty', + key: 'openWhenEmpty', + tooltip: + 'Check this if you would like to open up the first row when the EditGrid is empty', + weight: 1000, + input: true, + conditional: { + json: { '!==': [{ var: 'data.modal' }, true] }, + }, + }, + { + type: 'checkbox', + label: 'Disable Adding / Removing Rows', + key: 'disableAddingRemovingRows', + tooltip: + 'Check if you want to hide Add Another button and Remove Row button', + weight: 1001, + input: true, + clearOnHide: false, + calculateValue: 'value = data.disableAddingRemovingRows;', + }, + { + type: 'checkbox', + label: 'Display EditGrid as Table', + key: 'displayAsTable', + tooltip: 'use Table Template', + weight: 1002, + input: false, + customConditional() { + return !Evaluator.noeval; + }, + }, + { + weight: 1010, + type: 'textarea', + input: true, + key: 'conditionalAddButton', + label: 'Conditional Add Button', + placeholder: 'show = ...', + tooltip: 'Specify condition when Add Button should be displayed.', + editor: 'ace', + as: 'javascript', + wysiwyg: { + minLines: 3, + }, }, - }, ]; diff --git a/src/components/editgrid/editForm/EditGrid.edit.templates.js b/src/components/editgrid/editForm/EditGrid.edit.templates.js index e2b1abfb99..5df87c5bbf 100644 --- a/src/components/editgrid/editForm/EditGrid.edit.templates.js +++ b/src/components/editgrid/editForm/EditGrid.edit.templates.js @@ -1,127 +1,149 @@ import Evaluator from '../../../utils/Evaluator'; export default [ - { - type: 'textarea', - label: 'Header Template', - key: 'templates.header', - rows: 5, - editor: 'ace', - as: 'handlebars', - clearOnHide: false, - input: true, - placeholder: '/*** Lodash Template Code ***/', - description: 'Two available variables. "value" is the array of row data and "components" is the array of components in the grid.', - tooltip: 'This is the Lodash Template used to render the header of the Edit grid.', - customConditional({ data }) { - return (!Evaluator.noeval || Evaluator.protectedEval) && !data.displayAsTable; - } - }, - { - type: 'textarea', - label: 'Table Header Template', - key: 'templates.tableHeader', - rows: 6, - editor: 'ace', - as: 'handlebars', - clearOnHide: false, - input: true, - placeholder: '/*** Lodash Template Code ***/', - description: 'Two available variables. "value" is the array of row data and "components" is the array of components in the grid.', - tooltip: 'This is the Lodash Template used to render the header of the Edit grid.', - customConditional({ data }) { - return (!Evaluator.noeval || Evaluator.protectedEval) && data.displayAsTable; - } - }, - { - type: 'textarea', - label: 'Row Template', - key: 'templates.row', - rows: 5, - editor: 'ace', - as: 'handlebars', - clearOnHide: false, - input: true, - placeholder: '/*** Lodash Template Code ***/', - description: 'Three available variables. "row" is an object of one row\'s data, "components"' + - ' is the array of components in the grid and "state" is current row\'s state (can be "draft" or "saved").' + - ' To add click events, add the classes "editRow" and "removeRow" to elements.', - tooltip: 'This is the Lodash Template used to render each row of the Edit grid.', - customConditional({ data }) { - return (!Evaluator.noeval || Evaluator.protectedEval) && !data.displayAsTable; - } - }, - { - type: 'textarea', - label: 'Table Row Template', - key: 'templates.tableRow', - rows: 5, - editor: 'ace', - as: 'handlebars', - clearOnHide: false, - input: true, - placeholder: '/*** Lodash Template Code ***/', - description: 'Three available variables. "row" is an object of one row\'s data, "components"' + - ' is the array of components in the grid and "state" is current row\'s state (can be "draft" or "saved").' + - ' To add click events, add the classes "editRow" and "removeRow" to elements.', - tooltip: 'This is the Lodash Template used to render each row of the Edit grid.', - customConditional({ data }) { - return (!Evaluator.noeval || Evaluator.protectedEval) && data.displayAsTable; - } - }, - { - type: 'textarea', - label: 'Footer Template', - key: 'templates.footer', - rows: 5, - editor: 'ace', - as: 'handlebars', - input: true, - placeholder: '/*** Lodash Template Code ***/', - description: 'Two available variables. "value" is the array of row data and "components" is the array of components in the grid.', - tooltip: 'This is the Lodash Template used to render the footer of the Edit grid.', - customConditional() { - return !Evaluator.noeval || Evaluator.protectedEval; - } - }, - { - type: 'textfield', - input: true, - key: 'rowClass', - label: 'Row CSS Class', - placeholder: 'Row CSS Class', - tooltip: 'CSS class to add to the edit row wrapper.' - }, - { - type: 'textfield', - input: true, - key: 'addAnother', - label: 'Add Another Text', - placeholder: 'Add Another', - tooltip: 'Set the text of the Add Another button.' - }, - { - weight: 70, - type: 'checkbox', - label: 'Display as Modal', - tooltip: 'Display a modal to add or edit entries in the table', - key: 'modal', - input: true - }, - { - type: 'textfield', - input: true, - key: 'saveRow', - label: 'Save Row Text', - placeholder: 'Save', - tooltip: 'Set the text of the Save Row button.' - }, - { - type: 'textfield', - input: true, - key: 'removeRow', - label: 'Remove Row Text', - placeholder: 'Remove', - tooltip: 'Set the text of the remove Row button.' - } + { + type: 'textarea', + label: 'Header Template', + key: 'templates.header', + rows: 5, + editor: 'ace', + as: 'handlebars', + clearOnHide: false, + input: true, + placeholder: '/*** Lodash Template Code ***/', + description: + 'Two available variables. "value" is the array of row data and "components" is the array of components in the grid.', + tooltip: + "This is the Lodash Template used to render the header of the Edit grid.", + customConditional({ data }) { + return ( + (!Evaluator.noeval || Evaluator.protectedEval) && + !data.displayAsTable + ); + }, + }, + { + type: 'textarea', + label: 'Table Header Template', + key: 'templates.tableHeader', + rows: 6, + editor: 'ace', + as: 'handlebars', + clearOnHide: false, + input: true, + placeholder: '/*** Lodash Template Code ***/', + description: + 'Two available variables. "value" is the array of row data and "components" is the array of components in the grid.', + tooltip: + "This is the Lodash Template used to render the header of the Edit grid.", + customConditional({ data }) { + return ( + (!Evaluator.noeval || Evaluator.protectedEval) && + data.displayAsTable + ); + }, + }, + { + type: 'textarea', + label: 'Row Template', + key: 'templates.row', + rows: 5, + editor: 'ace', + as: 'handlebars', + clearOnHide: false, + input: true, + placeholder: '/*** Lodash Template Code ***/', + description: + 'Three available variables. "row" is an object of one row\'s data, "components"' + + ' is the array of components in the grid and "state" is current row\'s state (can be "draft" or "saved").' + + ' To add click events, add the classes "editRow" and "removeRow" to elements.', + tooltip: + "This is the Lodash Template used to render each row of the Edit grid.", + customConditional({ data }) { + return ( + (!Evaluator.noeval || Evaluator.protectedEval) && + !data.displayAsTable + ); + }, + }, + { + type: 'textarea', + label: 'Table Row Template', + key: 'templates.tableRow', + rows: 5, + editor: 'ace', + as: 'handlebars', + clearOnHide: false, + input: true, + placeholder: '/*** Lodash Template Code ***/', + description: + 'Three available variables. "row" is an object of one row\'s data, "components"' + + ' is the array of components in the grid and "state" is current row\'s state (can be "draft" or "saved").' + + ' To add click events, add the classes "editRow" and "removeRow" to elements.', + tooltip: + "This is the Lodash Template used to render each row of the Edit grid.", + customConditional({ data }) { + return ( + (!Evaluator.noeval || Evaluator.protectedEval) && + data.displayAsTable + ); + }, + }, + { + type: 'textarea', + label: 'Footer Template', + key: 'templates.footer', + rows: 5, + editor: 'ace', + as: 'handlebars', + input: true, + placeholder: '/*** Lodash Template Code ***/', + description: + 'Two available variables. "value" is the array of row data and "components" is the array of components in the grid.', + tooltip: + "This is the Lodash Template used to render the footer of the Edit grid.", + customConditional() { + return !Evaluator.noeval || Evaluator.protectedEval; + }, + }, + { + type: 'textfield', + input: true, + key: 'rowClass', + label: 'Row CSS Class', + placeholder: 'Row CSS Class', + tooltip: 'CSS class to add to the edit row wrapper.', + }, + { + type: 'textfield', + input: true, + key: 'addAnother', + label: 'Add Another Text', + placeholder: 'Add Another', + tooltip: 'Set the text of the Add Another button.', + }, + { + weight: 70, + type: 'checkbox', + label: 'Display as Modal', + tooltip: 'Display a modal to add or edit entries in the table', + key: 'modal', + input: true, + }, + { + type: 'textfield', + input: true, + key: 'saveRow', + label: 'Save Row Text', + placeholder: 'Save', + tooltip: 'Set the text of the Save Row button.', + }, + { + type: 'textfield', + input: true, + key: 'removeRow', + label: 'Remove Row Text', + placeholder: 'Remove', + tooltip: 'Set the text of the remove Row button.', + }, ]; diff --git a/src/components/editgrid/editForm/EditGrid.edit.validation.js b/src/components/editgrid/editForm/EditGrid.edit.validation.js index ec0c5e6f53..de34e416d6 100644 --- a/src/components/editgrid/editForm/EditGrid.edit.validation.js +++ b/src/components/editgrid/editForm/EditGrid.edit.validation.js @@ -1,32 +1,33 @@ export default [ - { - ignore: true, - key: 'unique', - }, - { - weight: 110, - key: 'validate.minLength', - label: 'Minimum Length', - placeholder: 'Minimum Length', - type: 'number', - tooltip: 'The minimum length requirement this field must meet.', - input: true - }, - { - weight: 120, - key: 'validate.maxLength', - label: 'Maximum Length', - placeholder: 'Maximum Length', - type: 'number', - tooltip: 'The maximum length requirement this field must meet.', - input: true - }, - { - type: 'checkbox', - input: true, - weight: 105, - key: 'rowDrafts', - label: 'Enable Row Drafts', - tooltip: 'Allow save rows even if their data is invalid. Errors will occur when try to submit with invalid rows.', - } + { + ignore: true, + key: 'unique', + }, + { + weight: 110, + key: 'validate.minLength', + label: 'Minimum Length', + placeholder: 'Minimum Length', + type: 'number', + tooltip: 'The minimum length requirement this field must meet.', + input: true, + }, + { + weight: 120, + key: 'validate.maxLength', + label: 'Maximum Length', + placeholder: 'Maximum Length', + type: 'number', + tooltip: 'The maximum length requirement this field must meet.', + input: true, + }, + { + type: 'checkbox', + input: true, + weight: 105, + key: 'rowDrafts', + label: 'Enable Row Drafts', + tooltip: + 'Allow save rows even if their data is invalid. Errors will occur when try to submit with invalid rows.', + }, ]; diff --git a/src/components/editgrid/fixtures/comp-openWhenEmpty.js b/src/components/editgrid/fixtures/comp-openWhenEmpty.js index 2341f43ab4..64155f6c0a 100644 --- a/src/components/editgrid/fixtures/comp-openWhenEmpty.js +++ b/src/components/editgrid/fixtures/comp-openWhenEmpty.js @@ -1,34 +1,34 @@ export default { - type: 'form', - components: [ - { - label: 'Edit Grid', - openWhenEmpty: true, - tableView: false, - rowDrafts: false, - key: 'editGrid', - type: 'editgrid', - input: true, - components: [ + type: 'form', + components: [ { - label: 'Text Field', - tableView: true, - key: 'textField', - type: 'textfield', - input: true - } - ] - }, - { - type: 'button', - label: 'Submit', - key: 'submit', - disableOnInvalid: true, - input: true, - tableView: false - } - ], - title: 'FIO-2819', - display: 'form', - name: 'fio2819', + label: 'Edit Grid', + openWhenEmpty: true, + tableView: false, + rowDrafts: false, + key: 'editGrid', + type: 'editgrid', + input: true, + components: [ + { + label: 'Text Field', + tableView: true, + key: 'textField', + type: 'textfield', + input: true, + }, + ], + }, + { + type: 'button', + label: 'Submit', + key: 'submit', + disableOnInvalid: true, + input: true, + tableView: false, + }, + ], + title: 'FIO-2819', + display: 'form', + name: 'fio2819', }; diff --git a/src/components/editgrid/fixtures/comp-with-basic-components.js b/src/components/editgrid/fixtures/comp-with-basic-components.js index 70188f47d8..0e5998cf11 100644 --- a/src/components/editgrid/fixtures/comp-with-basic-components.js +++ b/src/components/editgrid/fixtures/comp-with-basic-components.js @@ -1,222 +1,218 @@ export default { - type: 'form', - owner: '5e05a6b7549cdc2ece30c6b0', - components: [ - { - label: 'Edit Grid Basic Drafts Enabled Modal', - tableView: true, - templates: { - header: "
    \n {% util.eachComponent(components, function(component) { %}\n {% if (!component.hasOwnProperty('tableView') || component.tableView) { %}\n
    {{ component.label }}
    \n {% } %}\n {% }) %}\n
    ", - row: "
    \n {% util.eachComponent(components, function(component) { %}\n {% if (!component.hasOwnProperty('tableView') || component.tableView) { %}\n
    \n {{ getView(component, row[component.key]) }}\n
    \n {% } %}\n {% }) %}\n {% if (!instance.disabled) { %}\n
    \n
    \n \n {% if (!instance.hasRemoveButtons || instance.hasRemoveButtons()) { %}\n \n {% } %}\n
    \n
    \n {% } %}\n
    " - }, - modal: true, - key: 'editGridBasic2', - type: 'editgrid', - input: true, - components: [ + type: 'form', + owner: '5e05a6b7549cdc2ece30c6b0', + components: [ { - label: 'Text Field', - tableView: true, - validate: { - required: true - }, - key: 'textField', - type: 'textfield', - input: true - }, - { - label: 'Text Area', - autoExpand: false, - tableView: true, - validate: { - required: true - }, - key: 'textArea', - type: 'textarea', - input: true - }, - { - label: 'Number', - mask: false, - spellcheck: true, - tableView: true, - delimiter: false, - requireDecimal: false, - inputFormat: 'plain', - validate: { - required: true - }, - key: 'number', - type: 'number', - input: true - }, - { - label: 'Password', - tableView: true, - validate: { - required: true - }, - key: 'password', - type: 'password', - input: true, - protected: true - }, - { - label: 'Checkbox', - tableView: true, - defaultValue: false, - validate: { - required: true - }, - key: 'checkbox', - type: 'checkbox', - input: true - }, - { - label: 'Select Boxes', - optionsLabelPosition: 'right', - tableView: true, - defaultValue: { - '': false, - sa: false, - sb: false, - sc: false - }, - values: [ - { - label: 'sa', - value: 'sa', - shortcut: '' - }, - { - label: 'sb', - value: 'sb', - shortcut: '' + label: 'Edit Grid Basic Drafts Enabled Modal', + tableView: true, + templates: { + header: '
    \n {% util.eachComponent(components, function(component) { %}\n {% if (!component.hasOwnProperty(\'tableView\') || component.tableView) { %}\n
    {{ component.label }}
    \n {% } %}\n {% }) %}\n
    ', + row: '
    \n {% util.eachComponent(components, function(component) { %}\n {% if (!component.hasOwnProperty(\'tableView\') || component.tableView) { %}\n
    \n {{ getView(component, row[component.key]) }}\n
    \n {% } %}\n {% }) %}\n {% if (!instance.disabled) { %}\n
    \n
    \n \n {% if (!instance.hasRemoveButtons || instance.hasRemoveButtons()) { %}\n \n {% } %}\n
    \n
    \n {% } %}\n
    ', }, - { - label: 'sc', - value: 'sc', - shortcut: '' - } - ], - validate: { - required: true - }, - key: 'selectBoxes1', - type: 'selectboxes', - input: true, - inputType: 'checkbox' - }, - { - label: 'Select', - widget: 'choicesjs', - tableView: true, - data: { - values: [ - { - label: 'se1', - value: 'se1' - }, - { - label: 'se2', - value: 'se2' - }, - { - label: 'se3', - value: 'se3' - } - ] - }, - selectThreshold: 0.3, - validate: { - required: true - }, - key: 'select1', - type: 'select', - indexeddb: { - filter: { - - } - }, - input: true - }, - { - label: 'Select URL', - widget: 'choicesjs', - tableView: true, - dataSrc: 'url', - data: { - values: [ - { - label: '', - value: '' - } + modal: true, + key: 'editGridBasic2', + type: 'editgrid', + input: true, + components: [ + { + label: 'Text Field', + tableView: true, + validate: { + required: true, + }, + key: 'textField', + type: 'textfield', + input: true, + }, + { + label: 'Text Area', + autoExpand: false, + tableView: true, + validate: { + required: true, + }, + key: 'textArea', + type: 'textarea', + input: true, + }, + { + label: 'Number', + mask: false, + spellcheck: true, + tableView: true, + delimiter: false, + requireDecimal: false, + inputFormat: 'plain', + validate: { + required: true, + }, + key: 'number', + type: 'number', + input: true, + }, + { + label: 'Password', + tableView: true, + validate: { + required: true, + }, + key: 'password', + type: 'password', + input: true, + protected: true, + }, + { + label: 'Checkbox', + tableView: true, + defaultValue: false, + validate: { + required: true, + }, + key: 'checkbox', + type: 'checkbox', + input: true, + }, + { + label: 'Select Boxes', + optionsLabelPosition: 'right', + tableView: true, + defaultValue: { + '': false, + sa: false, + sb: false, + sc: false, + }, + values: [ + { + label: 'sa', + value: 'sa', + shortcut: '', + }, + { + label: 'sb', + value: 'sb', + shortcut: '', + }, + { + label: 'sc', + value: 'sc', + shortcut: '', + }, + ], + validate: { + required: true, + }, + key: 'selectBoxes1', + type: 'selectboxes', + input: true, + inputType: 'checkbox', + }, + { + label: 'Select', + widget: 'choicesjs', + tableView: true, + data: { + values: [ + { + label: 'se1', + value: 'se1', + }, + { + label: 'se2', + value: 'se2', + }, + { + label: 'se3', + value: 'se3', + }, + ], + }, + selectThreshold: 0.3, + validate: { + required: true, + }, + key: 'select1', + type: 'select', + indexeddb: { + filter: {}, + }, + input: true, + }, + { + label: 'Select URL', + widget: 'choicesjs', + tableView: true, + dataSrc: 'url', + data: { + values: [ + { + label: '', + value: '', + }, + ], + url: 'https://cdn.rawgit.com/mshafrir/2646763/raw/states_titlecase.json', + headers: [ + { + key: '', + value: '', + }, + ], + }, + template: '{{ item.name }}', + selectThreshold: 0.3, + validate: { + required: true, + }, + key: 'selectUrl', + type: 'select', + indexeddb: { + filter: {}, + }, + input: true, + disableLimit: false, + }, + { + label: 'Radio', + optionsLabelPosition: 'right', + inline: false, + tableView: true, + values: [ + { + label: 'ra1', + value: 'ra1', + shortcut: '', + }, + { + label: 'ra2', + value: 'ra2', + shortcut: '', + }, + { + label: 'ra3', + value: 'ra3', + shortcut: '', + }, + ], + validate: { + required: true, + onlyAvailableItems: false, + }, + key: 'radio1', + type: 'radio', + input: true, + }, ], - url: 'https://cdn.rawgit.com/mshafrir/2646763/raw/states_titlecase.json', - headers: [ - { - key: '', - value: '' - } - ] - }, - template: '{{ item.name }}', - selectThreshold: 0.3, - validate: { - required: true - }, - key: 'selectUrl', - type: 'select', - indexeddb: { - filter: { - - } - }, - input: true, - disableLimit: false }, { - label: 'Radio', - optionsLabelPosition: 'right', - inline: false, - tableView: true, - values: [ - { - label: 'ra1', - value: 'ra1', - shortcut: '' - }, - { - label: 'ra2', - value: 'ra2', - shortcut: '' - }, - { - label: 'ra3', - value: 'ra3', - shortcut: '' - } - ], - validate: { - required: true, - onlyAvailableItems: false - }, - key: 'radio1', - type: 'radio', - input: true - } - ] - }, - { - label: 'Submit', - showValidations: false, - tableView: false, - key: 'submit', - type: 'button', - input: true - } - ], - title: 'Test EG 2', - display: 'form', - name: 'testEg2', + label: 'Submit', + showValidations: false, + tableView: false, + key: 'submit', + type: 'button', + input: true, + }, + ], + title: 'Test EG 2', + display: 'form', + name: 'testEg2', }; diff --git a/src/components/editgrid/fixtures/comp-with-conditions-and-openWhenEmpty.js b/src/components/editgrid/fixtures/comp-with-conditions-and-openWhenEmpty.js index 05a123456f..090ed342ad 100644 --- a/src/components/editgrid/fixtures/comp-with-conditions-and-openWhenEmpty.js +++ b/src/components/editgrid/fixtures/comp-with-conditions-and-openWhenEmpty.js @@ -1,83 +1,82 @@ export default { - type: 'form', - components: [ - { - label: 'Radio', - optionsLabelPosition: 'right', - inline: false, - tableView: false, - values: [ + type: 'form', + components: [ { - label: 'show', - value: 'show', - shortcut: '' + label: 'Radio', + optionsLabelPosition: 'right', + inline: false, + tableView: false, + values: [ + { + label: 'show', + value: 'show', + shortcut: '', + }, + { + label: 'hide', + value: 'hide', + shortcut: '', + }, + ], + key: 'radio', + type: 'radio', + input: true, }, { - label: 'hide', - value: 'hide', - shortcut: '' - } - ], - key: 'radio', - type: 'radio', - input: true - }, - { - label: 'Edit Grid', - openWhenEmpty: true, - tableView: false, - rowDrafts: false, - key: 'editGrid', - customConditional: 'show = data.radio === \'show\';', - type: 'editgrid', - input: true, - components: [ - { - label: 'Text Field', - tableView: true, - key: 'textField', - type: 'textfield', - input: true + label: 'Edit Grid', + openWhenEmpty: true, + tableView: false, + rowDrafts: false, + key: 'editGrid', + customConditional: "show = data.radio === 'show';", + type: 'editgrid', + input: true, + components: [ + { + label: 'Text Field', + tableView: true, + key: 'textField', + type: 'textfield', + input: true, + }, + { + label: 'Select', + widget: 'choicesjs', + tableView: true, + data: { + values: [ + { + label: 'a', + value: 'a', + }, + { + label: 'b', + value: 'b', + }, + { + label: 'c', + value: 'c', + }, + ], + }, + selectThreshold: 0.3, + key: 'select1', + type: 'select', + indexeddb: { + filter: {}, + }, + input: true, + }, + ], }, { - label: 'Select', - widget: 'choicesjs', - tableView: true, - data: { - values: [ - { - label: 'a', - value: 'a' - }, - { - label: 'b', - value: 'b' - }, - { - label: 'c', - value: 'c' - } - ] - }, - selectThreshold: 0.3, - key: 'select1', - type: 'select', - indexeddb: { - filter: {} - }, - input: true - } - ] - }, - { - type: 'button', - label: 'Submit', - key: 'submit', - disableOnInvalid: true, - input: true, - tableView: false - } - ], - display: 'form', + type: 'button', + label: 'Submit', + key: 'submit', + disableOnInvalid: true, + input: true, + tableView: false, + }, + ], + display: 'form', }; - diff --git a/src/components/editgrid/fixtures/comp-with-custom-default-value.js b/src/components/editgrid/fixtures/comp-with-custom-default-value.js index 124250c82d..d588cb25a0 100644 --- a/src/components/editgrid/fixtures/comp-with-custom-default-value.js +++ b/src/components/editgrid/fixtures/comp-with-custom-default-value.js @@ -1,225 +1,228 @@ export default { - type: 'form', - components: [ - { - label: 'Selected Funds', - conditionalAddButton: 'show = !instance.hasOpenRows();', - tableView: false, - templates: { - header: "
    \r\n {% util.eachComponent(components, function(component) {\r\n if (component.key === 'loadTypeFundCode') { %}\r\n
    \r\n Fund Code\r\n
    \r\n
    \r\n Load Type\r\n
    \r\n {% } else if (component.key !== 'fundClass') { \r\n if (component.key === 'fundName2') { %}\r\n
    \r\n {% } else { %}\r\n
    \r\n {% } %}\r\n {{ component.label }}\r\n
    \r\n {% }\r\n }) %}\r\n
    ", - row: "
    \r\n {% console.dir(row);\r\n util.eachComponent(components, function(component) { \r\n if (component.key === 'loadTypeFundCode') { \r\n var fundCode = '';\r\n var loadType = '';\r\n var item = component.data.values.find(item=>item.value==row[component.key]);\r\n if (!!item) {\r\n\t var itemParts = item.label.split('/');\r\n loadType = itemParts [0];\r\n\t if (itemParts.length > 1) { \r\n\t fundCode = itemParts [1];\r\n }\r\n }\r\n %}\r\n
    {{ fundCode }}
    \r\n
    {{ loadType }}
    \r\n {% } else if (component.key !== 'fundClass') { \r\n if (component.key === 'fundName2') { %}\r\n
    \r\n {% } else { %}\r\n
    \r\n {% } \r\n if (!!component.data && !!Array.isArray(component.data.values)) { \r\n var item = component.data.values.find(item=>item.value==row[component.key]);\r\n if (!!item) { %}\r\n {{item.label}}\r\n {% } else { %}\r\n {{ row[component.key] }}\r\n {% } \r\n } else { %}\r\n {{ row[component.key] }}\r\n {% } %}\r\n
    \r\n {% }\r\n }) %}\r\n
    \r\n
    \r\n {% if (!instance.hasOpenRows()) { %}\r\n
    \r\n
    \r\n {% } %}\r\n
    \r\n
    \r\n
    " - }, - addAnother: 'Add Another Fund', - saveRow: 'Save Fund', - inlineEdit: true, - customDefaultValue: "value = [\n {\n fundClass: 'Class1',\n fundName2: 'Name1',\n loadTypeFundCode: 'dsc602',\n allocationAmount2: 100\n },\n {\n fundClass: 'Class2',\n fundName2: 'Name2',\n loadTypeFundCode: 'll1202',\n allocationAmount2: 200\n },\n {\n fundClass: 'Class3',\n fundName2: 'Name2',\n loadTypeFundCode: 'll1202',\n allocationAmount2: 100\n },\n {\n fundClass: 'Class3',\n fundName2: 'Name2',\n loadTypeFundCode: 'll1202',\n allocationAmount2: 100\n } ,\n {\n fundClass: 'Class3',\n fundName2: 'Name2',\n loadTypeFundCode: 'll1202',\n allocationAmount2: 100\n }\n];", - validate: { - minLength: 1 - }, - rowDrafts: false, - key: 'selectedFunds2', - type: 'editgrid', - input: true, - components: [ + type: 'form', + components: [ { - label: 'Columns', - columns: [ - { - components: [ - { - label: 'Fund Class', - widget: 'choicesjs', - tableView: true, - dataSrc: 'custom', - data: { - custom: "values = ['Class1', 'Class2', 'Class3'];" - }, - template: '{{ item }}', - validate: { - required: true - }, - key: 'fundClass', - type: 'select', - input: true, - hideOnChildrenHidden: false - } - ], - width: 6, - offset: 0, - push: 0, - pull: 0, - size: 'md', - currentWidth: 6 + label: 'Selected Funds', + conditionalAddButton: 'show = !instance.hasOpenRows();', + tableView: false, + templates: { + header: '
    \r\n {% util.eachComponent(components, function(component) {\r\n if (component.key === \'loadTypeFundCode\') { %}\r\n
    \r\n Fund Code\r\n
    \r\n
    \r\n Load Type\r\n
    \r\n {% } else if (component.key !== \'fundClass\') { \r\n if (component.key === \'fundName2\') { %}\r\n
    \r\n {% } else { %}\r\n
    \r\n {% } %}\r\n {{ component.label }}\r\n
    \r\n {% }\r\n }) %}\r\n
    ', + row: '
    \r\n {% console.dir(row);\r\n util.eachComponent(components, function(component) { \r\n if (component.key === \'loadTypeFundCode\') { \r\n var fundCode = \'\';\r\n var loadType = \'\';\r\n var item = component.data.values.find(item=>item.value==row[component.key]);\r\n if (!!item) {\r\n\t var itemParts = item.label.split(\'/\');\r\n loadType = itemParts [0];\r\n\t if (itemParts.length > 1) { \r\n\t fundCode = itemParts [1];\r\n }\r\n }\r\n %}\r\n
    {{ fundCode }}
    \r\n
    {{ loadType }}
    \r\n {% } else if (component.key !== \'fundClass\') { \r\n if (component.key === \'fundName2\') { %}\r\n
    \r\n {% } else { %}\r\n
    \r\n {% } \r\n if (!!component.data && !!Array.isArray(component.data.values)) { \r\n var item = component.data.values.find(item=>item.value==row[component.key]);\r\n if (!!item) { %}\r\n {{item.label}}\r\n {% } else { %}\r\n {{ row[component.key] }}\r\n {% } \r\n } else { %}\r\n {{ row[component.key] }}\r\n {% } %}\r\n
    \r\n {% }\r\n }) %}\r\n
    \r\n
    \r\n {% if (!instance.hasOpenRows()) { %}\r\n
    \r\n
    \r\n {% } %}\r\n
    \r\n
    \r\n
    ', + }, + addAnother: 'Add Another Fund', + saveRow: 'Save Fund', + inlineEdit: true, + customDefaultValue: + "value = [\n {\n fundClass: 'Class1',\n fundName2: 'Name1',\n loadTypeFundCode: 'dsc602',\n allocationAmount2: 100\n },\n {\n fundClass: 'Class2',\n fundName2: 'Name2',\n loadTypeFundCode: 'll1202',\n allocationAmount2: 200\n },\n {\n fundClass: 'Class3',\n fundName2: 'Name2',\n loadTypeFundCode: 'll1202',\n allocationAmount2: 100\n },\n {\n fundClass: 'Class3',\n fundName2: 'Name2',\n loadTypeFundCode: 'll1202',\n allocationAmount2: 100\n } ,\n {\n fundClass: 'Class3',\n fundName2: 'Name2',\n loadTypeFundCode: 'll1202',\n allocationAmount2: 100\n }\n];", + validate: { + minLength: 1, }, - { - components: [ + rowDrafts: false, + key: 'selectedFunds2', + type: 'editgrid', + input: true, + components: [ { - label: 'Fund Name', - widget: 'choicesjs', - tableView: true, - dataSrc: 'custom', - data: { - custom: "values = ['Name1', 'Name2'];" - }, - template: '{{ item }}', - validate: { - required: true - }, - key: 'fundName2', - logic: [ - { - name: 'Disable', - trigger: { - type: 'javascript', - javascript: "result = !row['fundClass'];" - }, - actions: [ + label: 'Columns', + columns: [ { - name: 'Disable', - type: 'property', - property: { - label: 'Disabled', - value: 'disabled', - type: 'boolean' - }, - state: true - } - ] - } - ], - type: 'select', - input: true, - hideOnChildrenHidden: false - } - ], - width: 6, - offset: 0, - push: 0, - pull: 0, - size: 'md', - currentWidth: 6 - } - ], - key: 'columns1', - type: 'columns', - tableView: false, - input: false, - hideOnChildrenHidden: false - }, - { - label: 'Columns', - columns: [ - { - components: [ - { - label: 'Load Type / Fund Code', - widget: 'choicesjs', - tableView: true, - data: { - values: [ - { - label: 'DSC/602', - value: 'dsc602' - }, - { - label: 'NL/702', - value: 'nl702' - }, - { - label: 'LL/1202', - value: 'll1202' - }, - { - label: 'NL-CB/3002', - value: 'nlCb3002' - }, - { - label: 'NL-CB5/5002', - value: 'nlCb55002' - } - ] - }, - refreshOn: 'editGrid1.fundName2', - validate: { - required: true - }, - key: 'loadTypeFundCode', - logic: [ - { - name: 'Disable', - trigger: { - type: 'javascript', - javascript: "result = !row['fundName2'];" - }, - actions: [ + components: [ + { + label: 'Fund Class', + widget: 'choicesjs', + tableView: true, + dataSrc: 'custom', + data: { + custom: "values = ['Class1', 'Class2', 'Class3'];", + }, + template: '{{ item }}', + validate: { + required: true, + }, + key: 'fundClass', + type: 'select', + input: true, + hideOnChildrenHidden: false, + }, + ], + width: 6, + offset: 0, + push: 0, + pull: 0, + size: 'md', + currentWidth: 6, + }, { - name: 'Disable', - type: 'property', - property: { - label: 'Disabled', - value: 'disabled', - type: 'boolean' - }, - state: true - } - ] - } - ], - type: 'select', - input: true, - hideOnChildrenHidden: false - } - ], - width: 6, - offset: 0, - push: 0, - pull: 0, - size: 'md', - currentWidth: 6 - }, - { - components: [ + components: [ + { + label: 'Fund Name', + widget: 'choicesjs', + tableView: true, + dataSrc: 'custom', + data: { + custom: "values = ['Name1', 'Name2'];", + }, + template: '{{ item }}', + validate: { + required: true, + }, + key: 'fundName2', + logic: [ + { + name: 'Disable', + trigger: { + type: 'javascript', + javascript: + "result = !row['fundClass'];", + }, + actions: [ + { + name: 'Disable', + type: 'property', + property: { + label: 'Disabled', + value: 'disabled', + type: 'boolean', + }, + state: true, + }, + ], + }, + ], + type: 'select', + input: true, + hideOnChildrenHidden: false, + }, + ], + width: 6, + offset: 0, + push: 0, + pull: 0, + size: 'md', + currentWidth: 6, + }, + ], + key: 'columns1', + type: 'columns', + tableView: false, + input: false, + hideOnChildrenHidden: false, + }, { - label: 'Amount', - mask: false, - spellcheck: true, - tableView: false, - currency: 'USD', - inputFormat: 'plain', - validate: { - required: true - }, - key: 'allocationAmount2', - type: 'currency', - input: true, - delimiter: true, - hideOnChildrenHidden: false - } - ], - width: 6, - offset: 0, - push: 0, - pull: 0, - size: 'md', - currentWidth: 6 - } - ], - key: 'columns', - type: 'columns', - input: false, - tableView: false, - hideOnChildrenHidden: false - } - ] - }, - { - type: 'button', - label: 'Submit', - key: 'submit', - disableOnInvalid: true, - input: true, - tableView: false - } - ], - display: 'form', + label: 'Columns', + columns: [ + { + components: [ + { + label: 'Load Type / Fund Code', + widget: 'choicesjs', + tableView: true, + data: { + values: [ + { + label: 'DSC/602', + value: 'dsc602', + }, + { + label: 'NL/702', + value: 'nl702', + }, + { + label: 'LL/1202', + value: 'll1202', + }, + { + label: 'NL-CB/3002', + value: 'nlCb3002', + }, + { + label: 'NL-CB5/5002', + value: 'nlCb55002', + }, + ], + }, + refreshOn: 'editGrid1.fundName2', + validate: { + required: true, + }, + key: 'loadTypeFundCode', + logic: [ + { + name: 'Disable', + trigger: { + type: 'javascript', + javascript: + "result = !row['fundName2'];", + }, + actions: [ + { + name: 'Disable', + type: 'property', + property: { + label: 'Disabled', + value: 'disabled', + type: 'boolean', + }, + state: true, + }, + ], + }, + ], + type: 'select', + input: true, + hideOnChildrenHidden: false, + }, + ], + width: 6, + offset: 0, + push: 0, + pull: 0, + size: 'md', + currentWidth: 6, + }, + { + components: [ + { + label: 'Amount', + mask: false, + spellcheck: true, + tableView: false, + currency: 'USD', + inputFormat: 'plain', + validate: { + required: true, + }, + key: 'allocationAmount2', + type: 'currency', + input: true, + delimiter: true, + hideOnChildrenHidden: false, + }, + ], + width: 6, + offset: 0, + push: 0, + pull: 0, + size: 'md', + currentWidth: 6, + }, + ], + key: 'columns', + type: 'columns', + input: false, + tableView: false, + hideOnChildrenHidden: false, + }, + ], + }, + { + type: 'button', + label: 'Submit', + key: 'submit', + disableOnInvalid: true, + input: true, + tableView: false, + }, + ], + display: 'form', }; diff --git a/src/components/editgrid/fixtures/comp1.js b/src/components/editgrid/fixtures/comp1.js index 8046b38017..f2aa262eb6 100644 --- a/src/components/editgrid/fixtures/comp1.js +++ b/src/components/editgrid/fixtures/comp1.js @@ -1,126 +1,126 @@ export default { - 'components': [ - { - 'components': [ + components: [ { - 'properties': { - '': '' - }, - 'tags': [], - 'type': 'textfield', - 'conditional': { - 'show': '', - 'when': null, - 'eq': '' - }, - 'validate': { - 'required': false, - 'minLength': '', - 'maxLength': '', - 'pattern': '', - 'custom': '', - 'customPrivate': false - }, - 'clearOnHide': true, - 'hidden': false, - 'persistent': true, - 'unique': false, - 'protected': false, - 'defaultValue': '', - 'multiple': false, - 'suffix': '', - 'prefix': '', - 'placeholder': '', - 'key': 'field1', - 'label': 'Field 1', - 'inputMask': '', - 'inputType': 'text', - 'tableView': true, - 'input': true + components: [ + { + properties: { + '': '', + }, + tags: [], + type: 'textfield', + conditional: { + show: '', + when: null, + eq: '', + }, + validate: { + required: false, + minLength: '', + maxLength: '', + pattern: '', + custom: '', + customPrivate: false, + }, + clearOnHide: true, + hidden: false, + persistent: true, + unique: false, + protected: false, + defaultValue: '', + multiple: false, + suffix: '', + prefix: '', + placeholder: '', + key: 'field1', + label: 'Field 1', + inputMask: '', + inputType: 'text', + tableView: true, + input: true, + }, + { + properties: { + '': '', + }, + tags: [], + type: 'textfield', + conditional: { + show: '', + when: null, + eq: '', + }, + validate: { + required: true, + minLength: '', + maxLength: '', + pattern: '', + custom: '', + customPrivate: false, + }, + clearOnHide: true, + hidden: false, + persistent: true, + unique: false, + protected: false, + defaultValue: '', + multiple: false, + suffix: '', + prefix: '', + placeholder: '', + key: 'field2', + label: 'Field 2', + inputMask: '', + inputType: 'text', + tableView: true, + input: true, + }, + ], + clearOnHide: false, + key: 'editgridPanel', + input: false, + title: '', + theme: 'default', + tableView: false, + type: 'panel', + breadcrumb: 'default', + tags: [], + conditional: { + eq: '', + when: null, + show: '', + }, + properties: { + '': '', + }, }, - { - 'properties': { - '': '' - }, - 'tags': [], - 'type': 'textfield', - 'conditional': { - 'show': '', - 'when': null, - 'eq': '' - }, - 'validate': { - 'required': true, - 'minLength': '', - 'maxLength': '', - 'pattern': '', - 'custom': '', - 'customPrivate': false - }, - 'clearOnHide': true, - 'hidden': false, - 'persistent': true, - 'unique': false, - 'protected': false, - 'defaultValue': '', - 'multiple': false, - 'suffix': '', - 'prefix': '', - 'placeholder': '', - 'key': 'field2', - 'label': 'Field 2', - 'inputMask': '', - 'inputType': 'text', - 'tableView': true, - 'input': true - } - ], - 'clearOnHide': false, - 'key': 'editgridPanel', - 'input': false, - 'title': '', - 'theme': 'default', - 'tableView': false, - 'type': 'panel', - 'breadcrumb': 'default', - 'tags': [], - 'conditional': { - 'eq': '', - 'when': null, - 'show': '' - }, - 'properties': { - '': '' - } - } - ], - 'validate': { - 'row': "valid = row.field1 === 'good' ? true : 'Must be good';" - }, - 'properties': { - '': '' - }, - 'conditional': { - 'show': '', - 'when': null, - 'eq': '' - }, - 'tags': [], - 'type': 'editgrid', - 'templates': { - 'header': '
    \n {%util.eachComponent(components, function(component) { %} \n
    \n {{ component.label }} \n
    \n {% }) %} \n
    {{value.length}}
    \n
    ', - 'row': '
    \n {%util.eachComponent(components, function(component) { %} \n
    \n {{ row[component.key] }} \n
    \n {% }) %} \n
    \n
    \n
    Edit
    \n
    Delete
    \n
    \n
    \n
    ', - 'footer': '' - }, - 'clearOnHide': true, - 'hidden': false, - 'persistent': true, - 'protected': false, - 'key': 'editgrid', - 'label': '', - 'tableView': true, - 'multiple': false, - 'tree': true, - 'input': true, - 'removeRow': 'Cancel' + ], + validate: { + row: "valid = row.field1 === 'good' ? true : 'Must be good';", + }, + properties: { + '': '', + }, + conditional: { + show: '', + when: null, + eq: '', + }, + tags: [], + type: 'editgrid', + templates: { + header: '
    \n {%util.eachComponent(components, function(component) { %} \n
    \n {{ component.label }} \n
    \n {% }) %} \n
    {{value.length}}
    \n
    ', + row: '
    \n {%util.eachComponent(components, function(component) { %} \n
    \n {{ row[component.key] }} \n
    \n {% }) %} \n
    \n
    \n
    Edit
    \n
    Delete
    \n
    \n
    \n
    ', + footer: '', + }, + clearOnHide: true, + hidden: false, + persistent: true, + protected: false, + key: 'editgrid', + label: '', + tableView: true, + multiple: false, + tree: true, + input: true, + removeRow: 'Cancel', }; diff --git a/src/components/editgrid/fixtures/comp10.js b/src/components/editgrid/fixtures/comp10.js index d873bd9c12..4aac08eb00 100644 --- a/src/components/editgrid/fixtures/comp10.js +++ b/src/components/editgrid/fixtures/comp10.js @@ -1,46 +1,46 @@ export default { - type: 'form', - components: [ - { - label: 'Edit Grid', - tableView: false, - rowDrafts: false, - key: 'editGrid', - type: 'editgrid', - input: true, - components: [ - { - label: 'Text Field', - tableView: true, - key: 'textField', - type: 'textfield', - input: true - }, - { - label: 'Number', - mask: false, - spellcheck: true, - tableView: false, - delimiter: false, - requireDecimal: false, - inputFormat: 'plain', - key: 'number', - type: 'number', - input: true - } - ] - }, - { - label: 'Submit', - showValidations: false, - tableView: false, - key: 'submit', - type: 'button', - input: true - } - ], - title: 'test11', - display: 'form', - name: 'test11', - path: 'test11', + type: 'form', + components: [ + { + label: 'Edit Grid', + tableView: false, + rowDrafts: false, + key: 'editGrid', + type: 'editgrid', + input: true, + components: [ + { + label: 'Text Field', + tableView: true, + key: 'textField', + type: 'textfield', + input: true, + }, + { + label: 'Number', + mask: false, + spellcheck: true, + tableView: false, + delimiter: false, + requireDecimal: false, + inputFormat: 'plain', + key: 'number', + type: 'number', + input: true, + }, + ], + }, + { + label: 'Submit', + showValidations: false, + tableView: false, + key: 'submit', + type: 'button', + input: true, + }, + ], + title: 'test11', + display: 'form', + name: 'test11', + path: 'test11', }; diff --git a/src/components/editgrid/fixtures/comp11.js b/src/components/editgrid/fixtures/comp11.js index 8d320e4d89..b63fb9ad21 100644 --- a/src/components/editgrid/fixtures/comp11.js +++ b/src/components/editgrid/fixtures/comp11.js @@ -1,48 +1,48 @@ export default { - type: 'form', - components: [ - { - label: 'Edit Grid', - openWhenEmpty: true, - tableView: false, - rowDrafts: false, - key: 'editGrid', - type: 'editgrid', - displayAsTable: false, - input: true, - components: [ + type: 'form', + components: [ { - label: 'Text Field', - tableView: true, - key: 'textField', - type: 'textfield', - input: true, + label: 'Edit Grid', + openWhenEmpty: true, + tableView: false, + rowDrafts: false, + key: 'editGrid', + type: 'editgrid', + displayAsTable: false, + input: true, + components: [ + { + label: 'Text Field', + tableView: true, + key: 'textField', + type: 'textfield', + input: true, + }, + { + label: 'Number', + mask: false, + tableView: true, + delimiter: false, + requireDecimal: false, + inputFormat: 'plain', + truncateMultipleSpaces: false, + key: 'number', + type: 'number', + input: true, + }, + ], }, { - label: 'Number', - mask: false, - tableView: true, - delimiter: false, - requireDecimal: false, - inputFormat: 'plain', - truncateMultipleSpaces: false, - key: 'number', - type: 'number', - input: true, + type: 'button', + label: 'Submit', + key: 'submit', + disableOnInvalid: true, + input: true, + tableView: false, }, - ], - }, - { - type: 'button', - label: 'Submit', - key: 'submit', - disableOnInvalid: true, - input: true, - tableView: false, - }, - ], - title: 'tetste', - display: 'form', - name: 'tetste', - path: 'tetste', + ], + title: 'tetste', + display: 'form', + name: 'tetste', + path: 'tetste', }; diff --git a/src/components/editgrid/fixtures/comp12.js b/src/components/editgrid/fixtures/comp12.js index d36042f48e..3af3419307 100644 --- a/src/components/editgrid/fixtures/comp12.js +++ b/src/components/editgrid/fixtures/comp12.js @@ -1,48 +1,56 @@ export default { - 'type': 'form', - 'components': [{ - 'label': 'Edit Grid', - 'tableView': false, - 'rowDrafts': false, - 'key': 'editGrid', - 'type': 'editgrid', - 'input': true, - 'components': [{ - 'label': 'Container', - 'tableView': false, - 'key': 'container', - 'type': 'container', - 'input': true, - 'components': [{ - 'label': 'Number', - 'mask': false, - 'tableView': false, - 'delimiter': false, - 'requireDecimal': false, - 'inputFormat': 'plain', - 'key': 'number', - 'type': 'number', - 'input': true - }, { - 'label': 'Text Field', - 'tableView': true, - 'validate': { - 'required': true + type: 'form', + components: [ + { + label: 'Edit Grid', + tableView: false, + rowDrafts: false, + key: 'editGrid', + type: 'editgrid', + input: true, + components: [ + { + label: 'Container', + tableView: false, + key: 'container', + type: 'container', + input: true, + components: [ + { + label: 'Number', + mask: false, + tableView: false, + delimiter: false, + requireDecimal: false, + inputFormat: 'plain', + key: 'number', + type: 'number', + input: true, + }, + { + label: 'Text Field', + tableView: true, + validate: { + required: true, + }, + key: 'textField', + customConditional: 'show = row.number === 5;', + type: 'textfield', + input: true, + }, + ], + }, + ], }, - 'key': 'textField', - 'customConditional': 'show = row.number === 5;', - 'type': 'textfield', - 'input': true - }] - }] - }, { - 'type': 'button', - 'label': 'Submit', - 'key': 'submit', - 'disableOnInvalid': true, - 'input': true, - 'tableView': false - }], - 'title': 'test form', - 'display': 'form', + { + type: 'button', + label: 'Submit', + key: 'submit', + disableOnInvalid: true, + input: true, + tableView: false, + }, + ], + title: 'test form', + display: 'form', }; diff --git a/src/components/editgrid/fixtures/comp13.js b/src/components/editgrid/fixtures/comp13.js index b8da8f0d7c..8a5668ec81 100644 --- a/src/components/editgrid/fixtures/comp13.js +++ b/src/components/editgrid/fixtures/comp13.js @@ -1,62 +1,62 @@ export default { - type: 'form', - tags: [], - components: [ - { - label: 'Container', - tableView: false, - key: 'container', - type: 'container', - input: true, - components: [ + type: 'form', + tags: [], + components: [ { - label: 'Text Field', - tableView: true, - validate: { - required: true, - minLength: 2, - }, - key: 'textField', - type: 'textfield', - input: true, + label: 'Container', + tableView: false, + key: 'container', + type: 'container', + input: true, + components: [ + { + label: 'Text Field', + tableView: true, + validate: { + required: true, + minLength: 2, + }, + key: 'textField', + type: 'textfield', + input: true, + }, + ], }, - ], - }, - { - label: 'Edit Grid', - tableView: false, - validate: { - required: true, - }, - rowDrafts: false, - key: 'editGrid', - type: 'editgrid', - input: true, - components: [ { - label: 'Text Field', - tableView: true, - validate: { - required: true, - }, - key: 'textField', - type: 'textfield', - input: true, + label: 'Edit Grid', + tableView: false, + validate: { + required: true, + }, + rowDrafts: false, + key: 'editGrid', + type: 'editgrid', + input: true, + components: [ + { + label: 'Text Field', + tableView: true, + validate: { + required: true, + }, + key: 'textField', + type: 'textfield', + input: true, + }, + ], }, - ], - }, - { - label: 'Submit', - showValidations: false, - tableView: false, - key: 'submit', - type: 'button', - input: true, - saveOnEnter: false, - }, - ], - title: 'FIO-3715', - display: 'form', - name: 'fio3715', - path: 'fio3715', + { + label: 'Submit', + showValidations: false, + tableView: false, + key: 'submit', + type: 'button', + input: true, + saveOnEnter: false, + }, + ], + title: 'FIO-3715', + display: 'form', + name: 'fio3715', + path: 'fio3715', }; diff --git a/src/components/editgrid/fixtures/comp14.js b/src/components/editgrid/fixtures/comp14.js index 5dd028b811..8182175ebe 100644 --- a/src/components/editgrid/fixtures/comp14.js +++ b/src/components/editgrid/fixtures/comp14.js @@ -1,83 +1,82 @@ export default { - type: 'form', - components: [ - { - label: 'editGrid', - hideLabel: true, - tableView: false, - modalEdit: true, - templates: { - header: - '
    \n
    Title
    \n
    First name
    \n
    Last name
    \n
    \n
    ', - row: '
    \n
    \n {{ _.get(row, \'title\', \'\') }}\n
    \n
    \n {{ _.get(row, \'firstName\', \'\') }}\n
    \n
    \n {{ _.get(row, \'familyName\', \'\') }}\n
    \n
    \n {% if (instance.options.readOnly) { %}\n
    \n
    \n \n
    \n
    \n {% } else { %}\n
    \n \n \n
    \n {% } %}\n
    \n
    ', - }, - addAnother: 'Add', - 'modal': true, - saveRow: 'Close', - redrawOn: 'data', - validate: { required: true, maxLength: 2 }, - rowDrafts: true, - key: 'editGrid', - type: 'editgrid', - displayAsTable: false, - alwaysEnabled: false, - input: true, - components: [ + type: 'form', + components: [ { - label: 'Title', - widget: 'choicesjs', - tableView: true, - data: { - values: [ - { value: 'mr', label: 'Mr' }, - { value: 'ms', label: 'Ms' }, + label: 'editGrid', + hideLabel: true, + tableView: false, + modalEdit: true, + templates: { + header: '
    \n
    Title
    \n
    First name
    \n
    Last name
    \n
    \n
    ', + row: '
    \n
    \n {{ _.get(row, \'title\', \'\') }}\n
    \n
    \n {{ _.get(row, \'firstName\', \'\') }}\n
    \n
    \n {{ _.get(row, \'familyName\', \'\') }}\n
    \n
    \n {% if (instance.options.readOnly) { %}\n
    \n
    \n \n
    \n
    \n {% } else { %}\n
    \n \n \n
    \n {% } %}\n
    \n
    ', + }, + addAnother: 'Add', + modal: true, + saveRow: 'Close', + redrawOn: 'data', + validate: { required: true, maxLength: 2 }, + rowDrafts: true, + key: 'editGrid', + type: 'editgrid', + displayAsTable: false, + alwaysEnabled: false, + input: true, + components: [ + { + label: 'Title', + widget: 'choicesjs', + tableView: true, + data: { + values: [ + { value: 'mr', label: 'Mr' }, + { value: 'ms', label: 'Ms' }, + ], + }, + validate: { required: true }, + key: 'title', + type: 'select', + labelWidth: 30, + labelMargin: 3, + alwaysEnabled: false, + input: true, + hideOnChildrenHidden: false, + }, + { + label: 'First name', + tableView: true, + validate: { required: true }, + key: 'firstName', + type: 'textfield', + labelWidth: 30, + labelMargin: 3, + alwaysEnabled: false, + input: true, + hideOnChildrenHidden: false, + }, + { + label: 'Family name', + tableView: true, + validate: { required: true }, + key: 'familyName', + type: 'textfield', + labelWidth: 30, + labelMargin: 3, + alwaysEnabled: false, + input: true, + hideOnChildrenHidden: false, + }, ], - }, - validate: { required: true }, - key: 'title', - type: 'select', - labelWidth: 30, - labelMargin: 3, - alwaysEnabled: false, - input: true, - hideOnChildrenHidden: false, + path: 'editGridPath', }, { - label: 'First name', - tableView: true, - validate: { required: true }, - key: 'firstName', - type: 'textfield', - labelWidth: 30, - labelMargin: 3, - alwaysEnabled: false, - input: true, - hideOnChildrenHidden: false, + type: 'button', + label: 'Submit', + key: 'submit', + disableOnInvalid: true, + input: true, + tableView: false, }, - { - label: 'Family name', - tableView: true, - validate: { required: true }, - key: 'familyName', - type: 'textfield', - labelWidth: 30, - labelMargin: 3, - alwaysEnabled: false, - input: true, - hideOnChildrenHidden: false, - }, - ], - path: 'editGridPath', - }, - { - type: 'button', - label: 'Submit', - key: 'submit', - disableOnInvalid: true, - input: true, - tableView: false, - }, - ], - title: 'edit grids', - display: 'form', + ], + title: 'edit grids', + display: 'form', }; diff --git a/src/components/editgrid/fixtures/comp15.js b/src/components/editgrid/fixtures/comp15.js index ccd8e69a08..3e69a657d5 100644 --- a/src/components/editgrid/fixtures/comp15.js +++ b/src/components/editgrid/fixtures/comp15.js @@ -1,49 +1,49 @@ export default { - type: 'form', - display: 'form', - components: [ - { - label: 'Edit Grid', - tableView: false, - validate: { - minLength: 6 - }, - rowDrafts: false, - key: 'editGrid', - type: 'editgrid', - displayAsTable: false, - input: true, - components: [ + type: 'form', + display: 'form', + components: [ { - label: 'Text', - applyMaskOn: 'change', - tableView: true, - key: 'text', - type: 'textfield', - input: true + label: 'Edit Grid', + tableView: false, + validate: { + minLength: 6, + }, + rowDrafts: false, + key: 'editGrid', + type: 'editgrid', + displayAsTable: false, + input: true, + components: [ + { + label: 'Text', + applyMaskOn: 'change', + tableView: true, + key: 'text', + type: 'textfield', + input: true, + }, + { + label: 'Number', + applyMaskOn: 'change', + mask: false, + tableView: false, + delimiter: false, + requireDecimal: false, + inputFormat: 'plain', + truncateMultipleSpaces: false, + key: 'number', + type: 'number', + input: true, + }, + ], }, { - label: 'Number', - applyMaskOn: 'change', - mask: false, - tableView: false, - delimiter: false, - requireDecimal: false, - inputFormat: 'plain', - truncateMultipleSpaces: false, - key: 'number', - type: 'number', - input: true - } - ] - }, - { - type: 'button', - label: 'Submit', - key: 'submit', - disableOnInvalid: true, - input: true, - tableView: false - } - ] + type: 'button', + label: 'Submit', + key: 'submit', + disableOnInvalid: true, + input: true, + tableView: false, + }, + ], }; diff --git a/src/components/editgrid/fixtures/comp2.js b/src/components/editgrid/fixtures/comp2.js index 8c2131b28c..ce8a26b410 100644 --- a/src/components/editgrid/fixtures/comp2.js +++ b/src/components/editgrid/fixtures/comp2.js @@ -1,112 +1,112 @@ export default { - 'components': [ - { - 'components': [ + components: [ { - 'type': 'textfield', - 'conditional': { - 'show': '', - 'when': null, - 'eq': '' - }, - 'validate': { - 'required': false, - 'minLength': '', - 'maxLength': '', - 'pattern': '', - 'custom': '', - 'customPrivate': false - }, - 'customDefaultValue': "value = 'bar';", - 'clearOnHide': true, - 'hidden': false, - 'persistent': true, - 'unique': false, - 'protected': false, - 'defaultValue': '', - 'multiple': false, - 'suffix': '', - 'prefix': '', - 'placeholder': '', - 'key': 'field1', - 'label': 'Field 1', - 'inputMask': '', - 'inputType': 'text', - 'tableView': true, - 'input': true + components: [ + { + type: 'textfield', + conditional: { + show: '', + when: null, + eq: '', + }, + validate: { + required: false, + minLength: '', + maxLength: '', + pattern: '', + custom: '', + customPrivate: false, + }, + customDefaultValue: "value = 'bar';", + clearOnHide: true, + hidden: false, + persistent: true, + unique: false, + protected: false, + defaultValue: '', + multiple: false, + suffix: '', + prefix: '', + placeholder: '', + key: 'field1', + label: 'Field 1', + inputMask: '', + inputType: 'text', + tableView: true, + input: true, + }, + { + properties: { + '': '', + }, + tags: [], + type: 'textfield', + conditional: { + show: 'true', + when: 'field1', + eq: 'foo', + }, + validate: { + required: true, + minLength: '', + maxLength: '', + pattern: '', + custom: '', + customPrivate: false, + }, + clearOnHide: true, + hidden: false, + persistent: true, + unique: false, + protected: false, + defaultValue: '', + multiple: false, + suffix: '', + prefix: '', + placeholder: '', + key: 'field2', + label: 'Field 2', + inputMask: '', + inputType: 'text', + tableView: true, + input: true, + }, + ], + clearOnHide: false, + key: 'editgridPanel', + input: false, + title: '', + theme: 'default', + tableView: false, + type: 'panel', + breadcrumb: 'default', + tags: [], + conditional: { + eq: '', + when: null, + show: '', + }, + properties: { + '': '', + }, }, - { - 'properties': { - '': '' - }, - 'tags': [], - 'type': 'textfield', - 'conditional': { - 'show': 'true', - 'when': 'field1', - 'eq': 'foo' - }, - 'validate': { - 'required': true, - 'minLength': '', - 'maxLength': '', - 'pattern': '', - 'custom': '', - 'customPrivate': false - }, - 'clearOnHide': true, - 'hidden': false, - 'persistent': true, - 'unique': false, - 'protected': false, - 'defaultValue': '', - 'multiple': false, - 'suffix': '', - 'prefix': '', - 'placeholder': '', - 'key': 'field2', - 'label': 'Field 2', - 'inputMask': '', - 'inputType': 'text', - 'tableView': true, - 'input': true - } - ], - 'clearOnHide': false, - 'key': 'editgridPanel', - 'input': false, - 'title': '', - 'theme': 'default', - 'tableView': false, - 'type': 'panel', - 'breadcrumb': 'default', - 'tags': [], - 'conditional': { - 'eq': '', - 'when': null, - 'show': '' - }, - 'properties': { - '': '' - } - } - ], - 'tags': [], - 'type': 'editgrid', - 'templates': { - 'header': '
    \n {%util.eachComponent(components, function(component) { %} \n
    \n {{ component.label }} \n
    \n {% }) %} \n
    {{value.length}}
    \n
    ', - 'row': '
    \n {%util.eachComponent(components, function(component) { %} \n
    \n {{ row[component.key] }} \n
    \n {% }) %} \n
    \n
    \n
    Edit
    \n
    Delete
    \n
    \n
    \n
    ', - 'footer': '' - }, - 'clearOnHide': true, - 'hidden': false, - 'persistent': true, - 'protected': false, - 'key': 'editgrid', - 'label': '', - 'tableView': true, - 'multiple': false, - 'tree': true, - 'input': true, - 'removeRow': 'Cancel' + ], + tags: [], + type: 'editgrid', + templates: { + header: '
    \n {%util.eachComponent(components, function(component) { %} \n
    \n {{ component.label }} \n
    \n {% }) %} \n
    {{value.length}}
    \n
    ', + row: '
    \n {%util.eachComponent(components, function(component) { %} \n
    \n {{ row[component.key] }} \n
    \n {% }) %} \n
    \n
    \n
    Edit
    \n
    Delete
    \n
    \n
    \n
    ', + footer: '', + }, + clearOnHide: true, + hidden: false, + persistent: true, + protected: false, + key: 'editgrid', + label: '', + tableView: true, + multiple: false, + tree: true, + input: true, + removeRow: 'Cancel', }; diff --git a/src/components/editgrid/fixtures/comp3.js b/src/components/editgrid/fixtures/comp3.js index 198de29c4b..6ca596839e 100644 --- a/src/components/editgrid/fixtures/comp3.js +++ b/src/components/editgrid/fixtures/comp3.js @@ -1,26 +1,30 @@ export default { - 'label': 'Edit Grid', - 'tableView': false, - 'key': 'editGrid', - 'type': 'editgrid', - 'input': true, - 'components': [{ - 'label': 'Container', - 'tableView': false, - 'key': 'container', - 'type': 'container', - 'input': true, - 'components': [{ - 'label': 'Number', - 'mask': false, - 'spellcheck': true, - 'tableView': true, - 'delimiter': false, - 'requireDecimal': false, - 'inputFormat': 'plain', - 'key': 'number', - 'type': 'number', - 'input': true - }] - }] - }; + label: 'Edit Grid', + tableView: false, + key: 'editGrid', + type: 'editgrid', + input: true, + components: [ + { + label: 'Container', + tableView: false, + key: 'container', + type: 'container', + input: true, + components: [ + { + label: 'Number', + mask: false, + spellcheck: true, + tableView: true, + delimiter: false, + requireDecimal: false, + inputFormat: 'plain', + key: 'number', + type: 'number', + input: true, + }, + ], + }, + ], +}; diff --git a/src/components/editgrid/fixtures/comp4.js b/src/components/editgrid/fixtures/comp4.js index b09bbdc6e1..3412f20a6f 100644 --- a/src/components/editgrid/fixtures/comp4.js +++ b/src/components/editgrid/fixtures/comp4.js @@ -1,23 +1,24 @@ export default { - 'label': 'Edit Grid', - 'tableView': false, - 'key': 'editGrid', - 'type': 'editgrid', - 'input': true, - 'components': [{ - 'label': 'Data Map', - 'tableView': false, - 'key': 'dataMap', - 'type': 'datamap', - 'input': true, - 'valueComponent': { - 'type': 'textfield', - 'key': 'key', - 'label': 'Value', - 'input': true, - 'hideLabel': true, - 'tableView': true - } - }] + label: 'Edit Grid', + tableView: false, + key: 'editGrid', + type: 'editgrid', + input: true, + components: [ + { + label: 'Data Map', + tableView: false, + key: 'dataMap', + type: 'datamap', + input: true, + valueComponent: { + type: 'textfield', + key: 'key', + label: 'Value', + input: true, + hideLabel: true, + tableView: true, + }, + }, + ], }; - diff --git a/src/components/editgrid/fixtures/comp5.js b/src/components/editgrid/fixtures/comp5.js index 3ff7fe13fc..04033d7f9d 100644 --- a/src/components/editgrid/fixtures/comp5.js +++ b/src/components/editgrid/fixtures/comp5.js @@ -1,20 +1,20 @@ export default { - label: 'Edit Grid', - tableView: false, - rowDrafts: true, - key: 'editGrid1', - type: 'editgrid', - input: true, - components: [ - { - label: 'Text Field', - tableView: true, - validate: { - required: true - }, - key: 'textField', - type: 'textfield', - input: true - } - ] + label: 'Edit Grid', + tableView: false, + rowDrafts: true, + key: 'editGrid1', + type: 'editgrid', + input: true, + components: [ + { + label: 'Text Field', + tableView: true, + validate: { + required: true, + }, + key: 'textField', + type: 'textfield', + input: true, + }, + ], }; diff --git a/src/components/editgrid/fixtures/comp6.js b/src/components/editgrid/fixtures/comp6.js index e52dfa6415..e1eb3cf1c1 100644 --- a/src/components/editgrid/fixtures/comp6.js +++ b/src/components/editgrid/fixtures/comp6.js @@ -1,56 +1,56 @@ -export default { - '_id': '5ece2d11b8c2fe102c726057', - 'type': 'form', - 'owner': '5e05a6b7549cdc2ece30c6b0', - 'components': [ - { - 'label': 'Edit Grid', - 'tableView': false, - 'modal': true, - 'validate': { - 'required': true - }, - 'key': 'editGrid', - 'type': 'editgrid', - 'input': true, - 'components': [ +export default { + _id: '5ece2d11b8c2fe102c726057', + type: 'form', + owner: '5e05a6b7549cdc2ece30c6b0', + components: [ { - 'label': 'Text Field', - 'tableView': true, - 'validate': { - 'required': true, - 'minLength': 4 - }, - 'key': 'textField', - 'type': 'textfield', - 'input': true - } - ] - }, - { - 'label': 'Submit', - 'showValidations': false, - 'tableView': false, - 'key': 'submit', - 'type': 'button', - 'input': true - } - ], - 'controller': '', - 'revisions': '', - '_vid': 0, - 'title': 'modalEditGridConfirm', - 'display': 'form', - 'access': [ - { - 'roles': [ - '5e96e79ee1c3ad3178454100', - '5e96e79ee1c3ad3178454101', - '5e96e79ee1c3ad3178454102' - ], - 'type': 'read_all' - } - ], - 'name': 'modalEditGridConfirm', - 'path': 'modaleditgridconfirm' + label: 'Edit Grid', + tableView: false, + modal: true, + validate: { + required: true, + }, + key: 'editGrid', + type: 'editgrid', + input: true, + components: [ + { + label: 'Text Field', + tableView: true, + validate: { + required: true, + minLength: 4, + }, + key: 'textField', + type: 'textfield', + input: true, + }, + ], + }, + { + label: 'Submit', + showValidations: false, + tableView: false, + key: 'submit', + type: 'button', + input: true, + }, + ], + controller: '', + revisions: '', + _vid: 0, + title: 'modalEditGridConfirm', + display: 'form', + access: [ + { + roles: [ + '5e96e79ee1c3ad3178454100', + '5e96e79ee1c3ad3178454101', + '5e96e79ee1c3ad3178454102', + ], + type: 'read_all', + }, + ], + name: 'modalEditGridConfirm', + path: 'modaleditgridconfirm', }; diff --git a/src/components/editgrid/fixtures/comp7.js b/src/components/editgrid/fixtures/comp7.js index 493cf2cea3..eb79477f42 100644 --- a/src/components/editgrid/fixtures/comp7.js +++ b/src/components/editgrid/fixtures/comp7.js @@ -1,121 +1,121 @@ export default { - label: 'Edit Grid', - tableView: false, - validate: { - custom: "valid = true;\ndata.editGrid.forEach((r) => {\n if (r.textField === data.name) {\n valid = 'Invalid Name';\n }\n});", - required: false, - customPrivate: false, - strictDateValidation: false, + label: 'Edit Grid', + tableView: false, + validate: { + custom: "valid = true;\ndata.editGrid.forEach((r) => {\n if (r.textField === data.name) {\n valid = 'Invalid Name';\n }\n});", + required: false, + customPrivate: false, + strictDateValidation: false, + multiple: false, + unique: false, + }, + rowDrafts: false, + key: 'editGrid', + type: 'editgrid', + input: true, + components: [ + { + label: 'Checkbox', + tableView: false, + defaultValue: true, + key: 'checkbox', + type: 'checkbox', + input: true, + }, + { + label: 'Text Field', + tableView: true, + key: 'editGridChild', + conditional: { + show: true, + when: 'editGrid.checkbox', + eq: 'true', + }, + type: 'textfield', + input: true, + }, + { + title: 'Child', + collapsible: false, + key: 'child', + type: 'panel', + label: 'Panel', + input: false, + tableView: false, + components: [ + { + label: 'Panel Child', + tableView: true, + key: 'panelChild', + conditional: { + show: true, + when: 'editGrid.checkbox', + eq: 'true', + }, + type: 'textfield', + input: true, + }, + ], + }, + ], + placeholder: '', + prefix: '', + customClass: '', + suffix: '', multiple: false, - unique: false - }, - rowDrafts: false, - key: 'editGrid', - type: 'editgrid', - input: true, - components: [ - { - label: 'Checkbox', - tableView: false, - defaultValue: true, - key: 'checkbox', - type: 'checkbox', - input: true + defaultValue: null, + protected: false, + unique: false, + persistent: true, + hidden: false, + clearOnHide: true, + refreshOn: '', + redrawOn: '', + modalEdit: false, + labelPosition: 'top', + description: '', + errorLabel: '', + tooltip: '', + hideLabel: false, + tabindex: '', + disabled: false, + autofocus: false, + dbIndex: false, + customDefaultValue: '', + calculateValue: '', + calculateServer: false, + widget: null, + attributes: {}, + validateOn: 'change', + conditional: { + show: null, + when: null, + eq: '', }, - { - label: 'Text Field', - tableView: true, - key: 'editGridChild', - conditional: { - show: true, - when: 'editGrid.checkbox', - eq: 'true' - }, - type: 'textfield', - input: true + overlay: { + style: '', + left: '', + top: '', + width: '', + height: '', }, - { - title: 'Child', - collapsible: false, - key: 'child', - type: 'panel', - label: 'Panel', - input: false, - tableView: false, - components: [ - { - label: 'Panel Child', - tableView: true, - key: 'panelChild', - conditional: { - show: true, - when: 'editGrid.checkbox', - eq: 'true' - }, - type: 'textfield', - input: true - } - ] - } - ], - placeholder: '', - prefix: '', - customClass: '', - suffix: '', - multiple: false, - defaultValue: null, - protected: false, - unique: false, - persistent: true, - hidden: false, - clearOnHide: true, - refreshOn: '', - redrawOn: '', - modalEdit: false, - labelPosition: 'top', - description: '', - errorLabel: '', - tooltip: '', - hideLabel: false, - tabindex: '', - disabled: false, - autofocus: false, - dbIndex: false, - customDefaultValue: '', - calculateValue: '', - calculateServer: false, - widget: null, - attributes: {}, - validateOn: 'change', - conditional: { - show: null, - when: null, - eq: '' - }, - overlay: { - style: '', - left: '', - top: '', - width: '', - height: '' - }, - allowCalculateOverride: false, - encrypted: false, - showCharCount: false, - showWordCount: false, - properties: {}, - allowMultipleMasks: false, - tree: true, - disableAddingRemovingRows: false, - removeRow: 'Cancel', - defaultOpen: false, - openWhenEmpty: false, - modal: false, - inlineEdit: false, - templates: { - header: "
    \n {% util.eachComponent(components, function(component) { %}\n {% if (!component.hasOwnProperty('tableView') || component.tableView) { %}\n
    {{ component.label }}
    \n {% } %}\n {% }) %}\n
    ", - row: "
    \n {% util.eachComponent(components, function(component) { %}\n {% if (!component.hasOwnProperty('tableView') || component.tableView) { %}\n
    \n {{ getView(component, row[component.key]) }}\n
    \n {% } %}\n {% }) %}\n {% if (!instance.disabled) { %}\n
    \n
    \n \n {% if (!instance.hasRemoveButtons || instance.hasRemoveButtons()) { %}\n \n {% } %}\n
    \n
    \n {% } %}\n
    ", - footer: '' - }, - id: 'e10uov' + allowCalculateOverride: false, + encrypted: false, + showCharCount: false, + showWordCount: false, + properties: {}, + allowMultipleMasks: false, + tree: true, + disableAddingRemovingRows: false, + removeRow: 'Cancel', + defaultOpen: false, + openWhenEmpty: false, + modal: false, + inlineEdit: false, + templates: { + header: '
    \n {% util.eachComponent(components, function(component) { %}\n {% if (!component.hasOwnProperty(\'tableView\') || component.tableView) { %}\n
    {{ component.label }}
    \n {% } %}\n {% }) %}\n
    ', + row: '
    \n {% util.eachComponent(components, function(component) { %}\n {% if (!component.hasOwnProperty(\'tableView\') || component.tableView) { %}\n
    \n {{ getView(component, row[component.key]) }}\n
    \n {% } %}\n {% }) %}\n {% if (!instance.disabled) { %}\n
    \n
    \n \n {% if (!instance.hasRemoveButtons || instance.hasRemoveButtons()) { %}\n \n {% } %}\n
    \n
    \n {% } %}\n
    ', + footer: '', + }, + id: 'e10uov', }; diff --git a/src/components/editgrid/fixtures/comp8.js b/src/components/editgrid/fixtures/comp8.js index 18cd9d8053..a020201263 100644 --- a/src/components/editgrid/fixtures/comp8.js +++ b/src/components/editgrid/fixtures/comp8.js @@ -1,154 +1,154 @@ export default { - label: 'HTML5', - tableView: false, - rowDrafts: false, - key: 'editGrid1', - type: 'editgrid', - input: true, - components: [ - { - label: 'Make', - widget: 'choicesjs', - placeholder: 'Select your make', - tableView: true, - data: { - values: [ - { - label: 'Chevy', - value: 'chevy' - }, - { - value: 'honda', - label: 'Honda' - }, - { - label: 'Ford', - value: 'ford' - }, - { - label: 'Toyota', - value: 'toyota' - } - ] - }, - selectThreshold: 0.3, - validate: { - required: true - }, - key: 'make', - type: 'select', - indexeddb: { - filter: {} - }, - input: true - }, - { - label: 'Model', - widget: 'choicesjs', - placeholder: 'Select your model', - tableView: true, - dataSrc: 'url', - data: { - values: [ - { - label: '', - value: '' - } - ], - url: 'https://vpic.nhtsa.dot.gov/api/vehicles/getmodelsformake/{{ row.make }}?format=json', - headers: [ - { - key: '', - value: '' - } - ] - }, - valueProperty: 'Model_Name', - template: '{{ item.Model_Name }}', - refreshOn: 'data', - clearOnRefresh: true, - selectThreshold: 0.3, - clearOnHide: false, - validate: { - required: true - }, - key: 'model', - type: 'select', - indexeddb: { - filter: {} - }, - selectValues: 'Results', - input: true, - disableLimit: false, - lazyLoad: false - } - ], - placeholder: '', - prefix: '', - customClass: '', - suffix: '', - multiple: false, - defaultValue: null, - protected: false, - unique: false, - persistent: true, - hidden: false, - clearOnHide: true, - refreshOn: '', - redrawOn: '', - modalEdit: false, - labelPosition: 'top', - description: '', - errorLabel: '', - tooltip: '', - hideLabel: false, - tabindex: '', - disabled: false, - autofocus: false, - dbIndex: false, - customDefaultValue: '', - calculateValue: '', - calculateServer: false, - widget: null, - attributes: {}, - validateOn: 'change', - validate: { - required: false, - custom: '', - customPrivate: false, - strictDateValidation: false, + label: 'HTML5', + tableView: false, + rowDrafts: false, + key: 'editGrid1', + type: 'editgrid', + input: true, + components: [ + { + label: 'Make', + widget: 'choicesjs', + placeholder: 'Select your make', + tableView: true, + data: { + values: [ + { + label: 'Chevy', + value: 'chevy', + }, + { + value: 'honda', + label: 'Honda', + }, + { + label: 'Ford', + value: 'ford', + }, + { + label: 'Toyota', + value: 'toyota', + }, + ], + }, + selectThreshold: 0.3, + validate: { + required: true, + }, + key: 'make', + type: 'select', + indexeddb: { + filter: {}, + }, + input: true, + }, + { + label: 'Model', + widget: 'choicesjs', + placeholder: 'Select your model', + tableView: true, + dataSrc: 'url', + data: { + values: [ + { + label: '', + value: '', + }, + ], + url: 'https://vpic.nhtsa.dot.gov/api/vehicles/getmodelsformake/{{ row.make }}?format=json', + headers: [ + { + key: '', + value: '', + }, + ], + }, + valueProperty: 'Model_Name', + template: '{{ item.Model_Name }}', + refreshOn: 'data', + clearOnRefresh: true, + selectThreshold: 0.3, + clearOnHide: false, + validate: { + required: true, + }, + key: 'model', + type: 'select', + indexeddb: { + filter: {}, + }, + selectValues: 'Results', + input: true, + disableLimit: false, + lazyLoad: false, + }, + ], + placeholder: '', + prefix: '', + customClass: '', + suffix: '', multiple: false, - unique: false - }, - conditional: { - show: null, - when: null, - eq: '' - }, - overlay: { - style: '', - left: '', - top: '', - width: '', - height: '' - }, - allowCalculateOverride: false, - encrypted: false, - showCharCount: false, - showWordCount: false, - properties: {}, - allowMultipleMasks: false, - tree: true, - disableAddingRemovingRows: false, - removeRow: 'Cancel', - defaultOpen: false, - openWhenEmpty: false, - modal: false, - inlineEdit: false, - templates: { - header: "
    \n {% util.eachComponent(components, function(component) { %}\n {% if (!component.hasOwnProperty('tableView') || component.tableView) { %}\n
    {{ component.label }}
    \n {% } %}\n {% }) %}\n
    ", - row: "
    \n {% util.eachComponent(components, function(component) { %}\n {% if (!component.hasOwnProperty('tableView') || component.tableView) { %}\n
    \n {{ getView(component, row[component.key]) }}\n
    \n {% } %}\n {% }) %}\n {% if (!instance.disabled) { %}\n
    \n
    \n \n {% if (!instance.hasRemoveButtons || instance.hasRemoveButtons()) { %}\n \n {% } %}\n
    \n
    \n {% } %}\n
    ", - footer: '' - }, - id: 'e7p0y9a' + defaultValue: null, + protected: false, + unique: false, + persistent: true, + hidden: false, + clearOnHide: true, + refreshOn: '', + redrawOn: '', + modalEdit: false, + labelPosition: 'top', + description: '', + errorLabel: '', + tooltip: '', + hideLabel: false, + tabindex: '', + disabled: false, + autofocus: false, + dbIndex: false, + customDefaultValue: '', + calculateValue: '', + calculateServer: false, + widget: null, + attributes: {}, + validateOn: 'change', + validate: { + required: false, + custom: '', + customPrivate: false, + strictDateValidation: false, + multiple: false, + unique: false, + }, + conditional: { + show: null, + when: null, + eq: '', + }, + overlay: { + style: '', + left: '', + top: '', + width: '', + height: '', + }, + allowCalculateOverride: false, + encrypted: false, + showCharCount: false, + showWordCount: false, + properties: {}, + allowMultipleMasks: false, + tree: true, + disableAddingRemovingRows: false, + removeRow: 'Cancel', + defaultOpen: false, + openWhenEmpty: false, + modal: false, + inlineEdit: false, + templates: { + header: '
    \n {% util.eachComponent(components, function(component) { %}\n {% if (!component.hasOwnProperty(\'tableView\') || component.tableView) { %}\n
    {{ component.label }}
    \n {% } %}\n {% }) %}\n
    ', + row: '
    \n {% util.eachComponent(components, function(component) { %}\n {% if (!component.hasOwnProperty(\'tableView\') || component.tableView) { %}\n
    \n {{ getView(component, row[component.key]) }}\n
    \n {% } %}\n {% }) %}\n {% if (!instance.disabled) { %}\n
    \n
    \n \n {% if (!instance.hasRemoveButtons || instance.hasRemoveButtons()) { %}\n \n {% } %}\n
    \n
    \n {% } %}\n
    ', + footer: '', + }, + id: 'e7p0y9a', }; diff --git a/src/components/editgrid/fixtures/comp9.js b/src/components/editgrid/fixtures/comp9.js index 19fd1ef786..a8b81beef5 100644 --- a/src/components/editgrid/fixtures/comp9.js +++ b/src/components/editgrid/fixtures/comp9.js @@ -1,60 +1,81 @@ export default { - type: 'form', - components: [ - { - label: 'Edit Grid', - tableView: false, - rowDrafts: false, - key: 'editGrid', - type: 'editgrid', - input: true, - components: [ - { label: 'Text Field', tableView: true, key: 'textField', type: 'textfield', input: true }, - { - label: 'Checkbox', - tableView: true, - key: 'checkbox', - type: 'checkbox', - input: true, - defaultValue: false - }, - { - label: 'Text Area', - autoExpand: false, - tableView: true, - key: 'textArea', - conditional: { show: true, when: 'editGrid.checkbox', eq: 'true' }, - type: 'textarea', - input: true - }, - { - label: 'Container', - tableView: false, - key: 'container', - conditional: { show: true, when: 'editGrid.textField', eq: 'show' }, - type: 'container', - input: true, - components: [ - { - label: 'Number', - mask: false, - spellcheck: true, - tableView: true, - delimiter: false, - requireDecimal: false, - inputFormat: 'plain', - key: 'number1', - type: 'number', - input: true - } - ] - } - ] - }, - { type: 'button', label: 'Submit', key: 'submit', disableOnInvalid: true, input: true, tableView: false } - ], - title: 'editGrid conditional', - display: 'form', - name: 'editGridConditional', - path: 'editgridconditional', + type: 'form', + components: [ + { + label: 'Edit Grid', + tableView: false, + rowDrafts: false, + key: 'editGrid', + type: 'editgrid', + input: true, + components: [ + { + label: 'Text Field', + tableView: true, + key: 'textField', + type: 'textfield', + input: true, + }, + { + label: 'Checkbox', + tableView: true, + key: 'checkbox', + type: 'checkbox', + input: true, + defaultValue: false, + }, + { + label: 'Text Area', + autoExpand: false, + tableView: true, + key: 'textArea', + conditional: { + show: true, + when: 'editGrid.checkbox', + eq: 'true', + }, + type: 'textarea', + input: true, + }, + { + label: 'Container', + tableView: false, + key: 'container', + conditional: { + show: true, + when: 'editGrid.textField', + eq: 'show', + }, + type: 'container', + input: true, + components: [ + { + label: 'Number', + mask: false, + spellcheck: true, + tableView: true, + delimiter: false, + requireDecimal: false, + inputFormat: 'plain', + key: 'number1', + type: 'number', + input: true, + }, + ], + }, + ], + }, + { + type: 'button', + label: 'Submit', + key: 'submit', + disableOnInvalid: true, + input: true, + tableView: false, + }, + ], + title: 'editGrid conditional', + display: 'form', + name: 'editGridConditional', + path: 'editgridconditional', }; diff --git a/src/components/editgrid/fixtures/formsWithEditGridAndConditions.js b/src/components/editgrid/fixtures/formsWithEditGridAndConditions.js index 024f448bd4..cfbee5531b 100644 --- a/src/components/editgrid/fixtures/formsWithEditGridAndConditions.js +++ b/src/components/editgrid/fixtures/formsWithEditGridAndConditions.js @@ -1,942 +1,951 @@ const form1 = { - title: 'form1', - name: 'form1', - path: 'form1', - type: 'form', - display: 'form', - components: [ - { - label: 'Checkbox', - tableView: false, - key: 'checkbox', - type: 'checkbox', - input: true, - }, - { - collapsible: false, - key: 'panel', - conditional: { - show: true, - conjunction: 'all', - conditions: [ - { - component: 'checkbox', - operator: 'isEqual', - value: true, - }, - ], - }, - type: 'panel', - label: 'Panel', - input: false, - tableView: false, - components: [ + title: 'form1', + name: 'form1', + path: 'form1', + type: 'form', + display: 'form', + components: [ { - label: 'Radio', - optionsLabelPosition: 'right', - inline: false, - tableView: false, - values: [ - { - label: 'yes', - value: 'yes', - shortcut: '', - }, - { - label: 'no', - value: 'no', - shortcut: '', - }, - ], - key: 'radio', - type: 'radio', - input: true, + label: 'Checkbox', + tableView: false, + key: 'checkbox', + type: 'checkbox', + input: true, }, { - label: 'Edit Grid', - tableView: false, - rowDrafts: false, - key: 'editGrid', - conditional: { - show: true, - conjunction: 'all', - conditions: [ - { - component: 'radio', - operator: 'isEqual', - value: 'yes', - }, - ], - }, - type: 'editgrid', - displayAsTable: false, - input: true, - components: [ - { - label: 'Text Field', - applyMaskOn: 'change', - tableView: true, - key: 'textField', - type: 'textfield', - input: true, - }, - { - label: 'Number', - applyMaskOn: 'change', - mask: false, - tableView: true, - delimiter: false, - requireDecimal: false, - inputFormat: 'plain', - truncateMultipleSpaces: false, - calculateValue: 'value = row.textField.length;', - key: 'number', - type: 'number', - input: true, + collapsible: false, + key: 'panel', + conditional: { + show: true, + conjunction: 'all', + conditions: [ + { + component: 'checkbox', + operator: 'isEqual', + value: true, + }, + ], }, - ], - }, - ], - }, - { - type: 'button', - label: 'Submit', - key: 'submit', - disableOnInvalid: true, - input: true, - tableView: false, - }, - ], -}; - -const form2 = { - title: 'form2', - name: 'testyyy', - path: 'testyyy', - type: 'form', - display: 'form', - components: [ - { - label: 'Text Area', - autoExpand: false, - tableView: true, - key: 'textArea', - type: 'textarea', - input: true, - }, - { - label: 'Edit Grid', - tableView: false, - rowDrafts: false, - key: 'editGrid1', - type: 'editgrid', - displayAsTable: false, - input: true, - components: [ - { - label: 'Set Panel Value', - action: 'custom', - showValidations: false, - tableView: false, - key: 'setPanelValue', - type: 'button', - custom: - "var rowIndex = instance.rowIndex;\nvar rowComponents = instance.parent?.editRows[rowIndex]?.components;\nvar panel = rowComponents?.find(comp => comp.component.key === 'panel');\npanel.setValue({radio: 'a', editGrid: [{textField:'testyyyy' }]});", - input: true, - }, - { - collapsible: false, - key: 'panel', - type: 'panel', - label: 'Panel', - input: false, - tableView: false, - components: [ - { - label: 'Radio', - optionsLabelPosition: 'right', - inline: false, - tableView: false, - values: [ + type: 'panel', + label: 'Panel', + input: false, + tableView: false, + components: [ { - label: 'a', - value: 'a', - shortcut: '', - }, - { - label: 'b', - value: 'b', - shortcut: '', + label: 'Radio', + optionsLabelPosition: 'right', + inline: false, + tableView: false, + values: [ + { + label: 'yes', + value: 'yes', + shortcut: '', + }, + { + label: 'no', + value: 'no', + shortcut: '', + }, + ], + key: 'radio', + type: 'radio', + input: true, }, - ], - key: 'radio', - type: 'radio', - input: true, - }, - { - title: 'Grid Panel', - collapsible: false, - key: 'panel1', - customConditional: "show = row.radio === 'a'", - type: 'panel', - label: 'Grid Panel', - input: false, - tableView: false, - components: [ { - label: 'Edit Grid', - openWhenEmpty: true, - disableAddingRemovingRows: true, - tableView: false, - rowDrafts: false, - key: 'editGrid', - conditional: { - show: true, - conjunction: 'all', - conditions: [ - { - component: 'radio', - operator: 'isEqual', - value: 'a', - }, - ], - }, - type: 'editgrid', - displayAsTable: false, - input: true, - components: [ - { - label: 'Text Field', - tableView: true, - key: 'textField', - type: 'textfield', - input: true, + label: 'Edit Grid', + tableView: false, + rowDrafts: false, + key: 'editGrid', + conditional: { + show: true, + conjunction: 'all', + conditions: [ + { + component: 'radio', + operator: 'isEqual', + value: 'yes', + }, + ], }, - ], + type: 'editgrid', + displayAsTable: false, + input: true, + components: [ + { + label: 'Text Field', + applyMaskOn: 'change', + tableView: true, + key: 'textField', + type: 'textfield', + input: true, + }, + { + label: 'Number', + applyMaskOn: 'change', + mask: false, + tableView: true, + delimiter: false, + requireDecimal: false, + inputFormat: 'plain', + truncateMultipleSpaces: false, + calculateValue: 'value = row.textField.length;', + key: 'number', + type: 'number', + input: true, + }, + ], }, - ], - }, - ], + ], + }, + { + type: 'button', + label: 'Submit', + key: 'submit', + disableOnInvalid: true, + input: true, + tableView: false, }, - ], - }, - { - type: 'button', - label: 'Submit', - key: 'submit', - disableOnInvalid: true, - input: true, - tableView: false, - }, - ], + ], }; -const form3 = { - title: 'form3', - name: 'form3', - path: 'form3', - type: 'form', - display: 'form', - components: [ - { - label: 'Tabs', - components: [ +const form2 = { + title: 'form2', + name: 'testyyy', + path: 'testyyy', + type: 'form', + display: 'form', + components: [ + { + label: 'Text Area', + autoExpand: false, + tableView: true, + key: 'textArea', + type: 'textarea', + input: true, + }, { - label: 'tab1', - key: 'generalInformationTab', - components: [ - { - title: 'Fill first this tab, then the second one', - theme: 'primary', - collapsible: false, - key: 'riskInfo', - type: 'panel', - label: 'Risk Information', - tableView: false, - input: false, - components: [ + label: 'Edit Grid', + tableView: false, + rowDrafts: false, + key: 'editGrid1', + type: 'editgrid', + displayAsTable: false, + input: true, + components: [ { - label: 'Select the second option', - optionsLabelPosition: 'right', - tableView: false, - defaultValue: { - creditRisk: false, - marketRisk: false, - operationalRisk: false, - counterpartyCreditRisk: false, - creditValuationRiskAdjustment: false, - }, - values: [ - { - label: 'Do not select', - value: 'creditRisk', - shortcut: '', - }, - { - label: 'Select this one', - value: 'marketRisk', - shortcut: '', - }, - { - label: 'Do not select', - value: 'operationalRisk', - shortcut: '', - }, - { - label: 'Do not select', - value: 'counterpartyCreditRisk', - shortcut: '', - }, - { - label: 'Do not select', - value: 'creditValuationRiskAdjustment', - shortcut: '', - }, - ], - key: 'affectedRiskTypes', - type: 'selectboxes', - input: true, - inputType: 'checkbox', + label: 'Set Panel Value', + action: 'custom', + showValidations: false, + tableView: false, + key: 'setPanelValue', + type: 'button', + custom: "var rowIndex = instance.rowIndex;\nvar rowComponents = instance.parent?.editRows[rowIndex]?.components;\nvar panel = rowComponents?.find(comp => comp.component.key === 'panel');\npanel.setValue({radio: 'a', editGrid: [{textField:'testyyyy' }]});", + input: true, }, - ], - }, - { - title: '1.4 Details of change', - theme: 'primary', - collapsible: false, - key: 'changeInformationPanel', - type: 'panel', - label: 'Change information', - input: false, - tableView: false, - components: [ { - title: 'select options according to the label', - collapsible: false, - key: 'rwaImpactPanel', - type: 'panel', - label: 'Panel', - input: false, - tableView: false, - components: [ - { - label: 'here select yes', - optionsLabelPosition: 'right', - customClass: 'tooltip-text-left', - inline: true, - tableView: true, - values: [ - { - label: 'Yes', - value: 'yes', - shortcut: '', - }, + collapsible: false, + key: 'panel', + type: 'panel', + label: 'Panel', + input: false, + tableView: false, + components: [ { - label: 'No', - value: 'no', - shortcut: '', - }, - ], - key: 'rwaImpact', - type: 'radio', - labelWidth: 100, - input: true, - }, - { - label: - 'here select the first option, then go to the second tab', - optionsLabelPosition: 'right', - customClass: 'ml-3', - inline: false, - tableView: false, - values: [ - { - label: 'Select this one', - value: 'EUParent', - shortcut: '', + label: 'Radio', + optionsLabelPosition: 'right', + inline: false, + tableView: false, + values: [ + { + label: 'a', + value: 'a', + shortcut: '', + }, + { + label: 'b', + value: 'b', + shortcut: '', + }, + ], + key: 'radio', + type: 'radio', + input: true, }, { - label: 'Do not select', - value: 'other', - shortcut: '', + title: 'Grid Panel', + collapsible: false, + key: 'panel1', + customConditional: "show = row.radio === 'a'", + type: 'panel', + label: 'Grid Panel', + input: false, + tableView: false, + components: [ + { + label: 'Edit Grid', + openWhenEmpty: true, + disableAddingRemovingRows: true, + tableView: false, + rowDrafts: false, + key: 'editGrid', + conditional: { + show: true, + conjunction: 'all', + conditions: [ + { + component: 'radio', + operator: 'isEqual', + value: 'a', + }, + ], + }, + type: 'editgrid', + displayAsTable: false, + input: true, + components: [ + { + label: 'Text Field', + tableView: true, + key: 'textField', + type: 'textfield', + input: true, + }, + ], + }, + ], }, - ], - key: 'euParentInstitution', - conditional: { - show: true, - conjunction: 'all', - conditions: [ - { - component: 'rwaImpact', - operator: 'isEqual', - value: 'yes', - }, - ], - }, - type: 'radio', - input: true, - }, - ], + ], }, - ], - }, - ], + ], }, { - label: 'tab3', - key: 'marketRiskTab', - components: [ - { - label: 'mr', - tableView: true, - key: 'mr', - conditional: { - show: true, - conjunction: 'all', - conditions: [ - { - component: 'affectedRiskTypes', - operator: 'isEqual', - value: 'marketRisk', - }, - ], - }, - type: 'container', - input: true, - components: [ + type: 'button', + label: 'Submit', + key: 'submit', + disableOnInvalid: true, + input: true, + tableView: false, + }, + ], +}; + +const form3 = { + title: 'form3', + name: 'form3', + path: 'form3', + type: 'form', + display: 'form', + components: [ + { + label: 'Tabs', + components: [ { - label: 'Quantitative Information', - tableView: false, - key: 'quantitativeInformation', - type: 'container', - input: true, - components: [ - { - title: 'Fill this tab after tab1', - theme: 'primary', - customClass: 'tooltip-text-left', - collapsible: false, - key: 'quantitativeInformation', - type: 'panel', - label: '3.2 Quantitative information', - input: false, - tableView: false, - components: [ - { - label: 'Here select yes', - labelPosition: 'left-left', - optionsLabelPosition: 'right', - inline: true, - tableView: false, - values: [ - { - label: 'Yes', - value: 'yes', - shortcut: '', - }, - { - label: 'No', - value: 'no', - shortcut: '', - }, - ], - key: 'cva', - customConditional: - "show = _.get(data, 'affectedRiskTypes.creditValuationRiskAdjustment') === false && data.rwaImpact === 'yes';", - type: 'radio', - labelWidth: 100, - input: true, - }, + label: 'tab1', + key: 'generalInformationTab', + components: [ { - label: 'Do not select', - tableView: false, - defaultValue: false, - key: 'sameRiskCategories', - conditional: { - show: true, - conjunction: 'all', - conditions: [ - { - component: 'rwaImpact', - operator: 'isEqual', - value: 'yes', - }, + title: 'Fill first this tab, then the second one', + theme: 'primary', + collapsible: false, + key: 'riskInfo', + type: 'panel', + label: 'Risk Information', + tableView: false, + input: false, + components: [ + { + label: 'Select the second option', + optionsLabelPosition: 'right', + tableView: false, + defaultValue: { + creditRisk: false, + marketRisk: false, + operationalRisk: false, + counterpartyCreditRisk: false, + creditValuationRiskAdjustment: false, + }, + values: [ + { + label: 'Do not select', + value: 'creditRisk', + shortcut: '', + }, + { + label: 'Select this one', + value: 'marketRisk', + shortcut: '', + }, + { + label: 'Do not select', + value: 'operationalRisk', + shortcut: '', + }, + { + label: 'Do not select', + value: 'counterpartyCreditRisk', + shortcut: '', + }, + { + label: 'Do not select', + value: 'creditValuationRiskAdjustment', + shortcut: '', + }, + ], + key: 'affectedRiskTypes', + type: 'selectboxes', + input: true, + inputType: 'checkbox', + }, ], - }, - type: 'checkbox', - input: true, }, { - label: 'Do not select', - tableView: true, - key: 'sameImpactAcrossEntities', - conditional: { - show: true, - conjunction: 'all', - conditions: [ - { - component: 'euParentInstitution', - operator: 'isEqual', - value: 'EUParent', - }, + title: '1.4 Details of change', + theme: 'primary', + collapsible: false, + key: 'changeInformationPanel', + type: 'panel', + label: 'Change information', + input: false, + tableView: false, + components: [ + { + title: 'select options according to the label', + collapsible: false, + key: 'rwaImpactPanel', + type: 'panel', + label: 'Panel', + input: false, + tableView: false, + components: [ + { + label: 'here select yes', + optionsLabelPosition: 'right', + customClass: 'tooltip-text-left', + inline: true, + tableView: true, + values: [ + { + label: 'Yes', + value: 'yes', + shortcut: '', + }, + { + label: 'No', + value: 'no', + shortcut: '', + }, + ], + key: 'rwaImpact', + type: 'radio', + labelWidth: 100, + input: true, + }, + { + label: 'here select the first option, then go to the second tab', + optionsLabelPosition: 'right', + customClass: 'ml-3', + inline: false, + tableView: false, + values: [ + { + label: 'Select this one', + value: 'EUParent', + shortcut: '', + }, + { + label: 'Do not select', + value: 'other', + shortcut: '', + }, + ], + key: 'euParentInstitution', + conditional: { + show: true, + conjunction: 'all', + conditions: [ + { + component: 'rwaImpact', + operator: 'isEqual', + value: 'yes', + }, + ], + }, + type: 'radio', + input: true, + }, + ], + }, ], - }, - type: 'checkbox', - optionsLabelPosition: 'right', - input: true, - defaultValue: false, }, + ], + }, + { + label: 'tab3', + key: 'marketRiskTab', + components: [ { - label: - 'Try to add a row in this grid, it will disappear', - tableView: true, - templates: { - header: - '
    \n
    Legal entities
    \n
    Level of consolidation
    \n
    Max change of risk number
    \n
    \n
    ', - row: "
    \r\n
    \r\n {{ _.get(row, 'legalEntity.longName', '') }}\r\n
    \r\n
    \r\n {{ _.get(row, 'consolidationLevel', '') }}\r\n
    \r\n
    \r\n \r\n\t{% \r\n\tvar items = [\r\n _.get(row, 'VaRRelChange1Day', ''),\r\n _.get(row, 'VaRRelChange', ''),\r\n _.get(row, 'sVarRelChange1Day', ''),\r\n _.get(row, 'sVarRelChange', ''),\r\n _.get(row, 'IRCRelChange1Day', ''),\r\n _.get(row, 'IRCRelChange', ''),\r\n _.get(row, 'CRMRelChange1Day', ''),\r\n _.get(row, 'CRMRelChange', ''),\r\n ].filter((i) => typeof i !== 'undefined' && !isNaN(i))\r\n .map((i) => Math.abs(i));\r\n\tif (items.length) { %}\r\n {{Math.max(\r\n ...items).toFixed(3)}}%\r\n {% } else { %}\r\n {{0.000}}%\r\n {% } %}\r\n
    \r\n
    \r\n {% if (instance.options.readOnly) { %}\r\n
    \r\n
    \r\n \r\n
    \r\n
    \r\n {% } else { %}\r\n
    \r\n \r\n \r\n
    \r\n {% } %}\r\n
    \r\n
    ", - }, - addAnother: 'Add legal entity', - modal: true, - saveRow: 'Close', - rowDrafts: false, - key: 'impactsPerEntity', - conditional: { - show: true, - conjunction: 'all', - conditions: [ - { - component: 'rwaImpact', - operator: 'isEqual', - value: 'yes', - }, - ], - }, - type: 'editgrid', - displayAsTable: false, - alwaysEnabled: true, - input: true, - components: [ - { - title: - 'Try to add a row in this grid, it will disappear', - theme: 'primary', - collapsible: false, - key: 'entitiesPanel', - type: 'panel', - label: 'Panel', - input: false, - tableView: false, - components: [ - { - title: - 'Try to add a row in this grid, it will disappear', - collapsible: false, - key: 'periodImpactEstimationPanel', - customConditional: - "show = (\n (_.get(data, 'mr.quantitativeInformation.sameImpactAcrossEntities') === false) ||\n (_.get(data, 'euParentInstitution') === 'other')\n)", - type: 'panel', - label: 'Panel', - input: false, - tableView: false, - components: [ + label: 'mr', + tableView: true, + key: 'mr', + conditional: { + show: true, + conjunction: 'all', + conditions: [ { - label: 'Number', - applyMaskOn: 'change', - mask: false, - tableView: true, - delimiter: false, - requireDecimal: false, - inputFormat: 'plain', - truncateMultipleSpaces: false, - key: 'number', - type: 'number', - input: true, + component: 'affectedRiskTypes', + operator: 'isEqual', + value: 'marketRisk', }, - ], - }, - ], + ], }, - ], - path: 'mrEditGrid', + type: 'container', + input: true, + components: [ + { + label: 'Quantitative Information', + tableView: false, + key: 'quantitativeInformation', + type: 'container', + input: true, + components: [ + { + title: 'Fill this tab after tab1', + theme: 'primary', + customClass: 'tooltip-text-left', + collapsible: false, + key: 'quantitativeInformation', + type: 'panel', + label: '3.2 Quantitative information', + input: false, + tableView: false, + components: [ + { + label: 'Here select yes', + labelPosition: 'left-left', + optionsLabelPosition: + 'right', + inline: true, + tableView: false, + values: [ + { + label: 'Yes', + value: 'yes', + shortcut: '', + }, + { + label: 'No', + value: 'no', + shortcut: '', + }, + ], + key: 'cva', + customConditional: + "show = _.get(data, 'affectedRiskTypes.creditValuationRiskAdjustment') === false && data.rwaImpact === 'yes';", + type: 'radio', + labelWidth: 100, + input: true, + }, + { + label: 'Do not select', + tableView: false, + defaultValue: false, + key: 'sameRiskCategories', + conditional: { + show: true, + conjunction: 'all', + conditions: [ + { + component: + 'rwaImpact', + operator: + 'isEqual', + value: 'yes', + }, + ], + }, + type: 'checkbox', + input: true, + }, + { + label: 'Do not select', + tableView: true, + key: 'sameImpactAcrossEntities', + conditional: { + show: true, + conjunction: 'all', + conditions: [ + { + component: + 'euParentInstitution', + operator: + 'isEqual', + value: 'EUParent', + }, + ], + }, + type: 'checkbox', + optionsLabelPosition: + 'right', + input: true, + defaultValue: false, + }, + { + label: 'Try to add a row in this grid, it will disappear', + tableView: true, + templates: { + header: '
    \n
    Legal entities
    \n
    Level of consolidation
    \n
    Max change of risk number
    \n
    \n
    ', + row: "
    \r\n
    \r\n {{ _.get(row, 'legalEntity.longName', '') }}\r\n
    \r\n
    \r\n {{ _.get(row, 'consolidationLevel', '') }}\r\n
    \r\n
    \r\n \r\n\t{% \r\n\tvar items = [\r\n _.get(row, 'VaRRelChange1Day', ''),\r\n _.get(row, 'VaRRelChange', ''),\r\n _.get(row, 'sVarRelChange1Day', ''),\r\n _.get(row, 'sVarRelChange', ''),\r\n _.get(row, 'IRCRelChange1Day', ''),\r\n _.get(row, 'IRCRelChange', ''),\r\n _.get(row, 'CRMRelChange1Day', ''),\r\n _.get(row, 'CRMRelChange', ''),\r\n ].filter((i) => typeof i !== 'undefined' && !isNaN(i))\r\n .map((i) => Math.abs(i));\r\n\tif (items.length) { %}\r\n {{Math.max(\r\n ...items).toFixed(3)}}%\r\n {% } else { %}\r\n {{0.000}}%\r\n {% } %}\r\n
    \r\n
    \r\n {% if (instance.options.readOnly) { %}\r\n
    \r\n
    \r\n \r\n
    \r\n
    \r\n {% } else { %}\r\n
    \r\n \r\n \r\n
    \r\n {% } %}\r\n
    \r\n
    ", + }, + addAnother: + 'Add legal entity', + modal: true, + saveRow: 'Close', + rowDrafts: false, + key: 'impactsPerEntity', + conditional: { + show: true, + conjunction: 'all', + conditions: [ + { + component: + 'rwaImpact', + operator: + 'isEqual', + value: 'yes', + }, + ], + }, + type: 'editgrid', + displayAsTable: false, + alwaysEnabled: true, + input: true, + components: [ + { + title: 'Try to add a row in this grid, it will disappear', + theme: 'primary', + collapsible: false, + key: 'entitiesPanel', + type: 'panel', + label: 'Panel', + input: false, + tableView: false, + components: [ + { + title: 'Try to add a row in this grid, it will disappear', + collapsible: false, + key: 'periodImpactEstimationPanel', + customConditional: + "show = (\n (_.get(data, 'mr.quantitativeInformation.sameImpactAcrossEntities') === false) ||\n (_.get(data, 'euParentInstitution') === 'other')\n)", + type: 'panel', + label: 'Panel', + input: false, + tableView: false, + components: + [ + { + label: 'Number', + applyMaskOn: + 'change', + mask: false, + tableView: true, + delimiter: false, + requireDecimal: false, + inputFormat: + 'plain', + truncateMultipleSpaces: false, + key: 'number', + type: 'number', + input: true, + }, + ], + }, + ], + }, + ], + path: 'mrEditGrid', + }, + ], + }, + ], + }, + ], }, - ], - }, - ], + ], }, - ], - }, - ], + ], + key: 'nmcTab', + type: 'tabs', + tableView: false, + input: false, + keyModified: true, }, - ], - key: 'nmcTab', - type: 'tabs', - tableView: false, - input: false, - keyModified: true, - }, - { - label: 'Submit', - action: 'saveState', - showValidations: false, - tableView: false, - key: 'submit', - type: 'button', - input: true, - alwaysEnabled: false, - state: 'draft', - }, - ], + { + label: 'Submit', + action: 'saveState', + showValidations: false, + tableView: false, + key: 'submit', + type: 'button', + input: true, + alwaysEnabled: false, + state: 'draft', + }, + ], }; const form4 = { - title: 'form4', - name: 'form4', - path: 'form4', - type: 'form', - display: 'form', - components: [ - { - label: 'Text Field', - applyMaskOn: 'change', - tableView: true, - key: 'textField', - type: 'textfield', - input: true, - }, - { - label: 'Edit Grid', - tableView: false, - calculateValue: - 'if (options.server){\r\nvalue = [{ "textArea": "test"}];\r\n}', - calculateServer: true, - rowDrafts: false, - key: 'editGrid', - type: 'editgrid', - displayAsTable: false, - input: true, - components: [ + title: 'form4', + name: 'form4', + path: 'form4', + type: 'form', + display: 'form', + components: [ { - label: 'Text Area', - applyMaskOn: 'change', - autoExpand: false, - tableView: true, - key: 'textArea', - type: 'textarea', - input: true, + label: 'Text Field', + applyMaskOn: 'change', + tableView: true, + key: 'textField', + type: 'textfield', + input: true, }, - ], - }, - { - type: 'button', - label: 'Submit', - key: 'submit', - disableOnInvalid: true, - input: true, - tableView: false, - }, - ], -}; - -const form5 = { - title: 'form5', - name: 'form5', - path: 'form5', - type: 'form', - display: 'form', - components: [ - { - label: 'generalInformation', - tableView: false, - key: 'generalInformation', - type: 'container', - input: true, - components: [ { - title: 'Select here one entry', - theme: 'primary', - collapsible: false, - key: 'sectionOutsourcingSupervisedEntites', - type: 'panel', - label: 'Outsourcing supervised entities', - tableView: false, - input: false, - components: [ - { - label: 'Select the only option here', - widget: 'choicesjs', - description: '
    ', - tableView: true, - multiple: true, - dataSrc: 'json', - data: { - json: [ - { - id: 6256, - longName: 'Bank_DE', - leiCode: 'LEI6256', - countryCode: 'DE', - }, - ], - }, - template: - '{{ item.longName }} [{{item.countryCode}}] {{item.leiCode}}', - customOptions: { - searchResultLimit: 100, - fuseOptions: { - threshold: 0.1, - distance: 9000, + label: 'Edit Grid', + tableView: false, + calculateValue: + 'if (options.server){\r\nvalue = [{ "textArea": "test"}];\r\n}', + calculateServer: true, + rowDrafts: false, + key: 'editGrid', + type: 'editgrid', + displayAsTable: false, + input: true, + components: [ + { + label: 'Text Area', + applyMaskOn: 'change', + autoExpand: false, + tableView: true, + key: 'textArea', + type: 'textarea', + input: true, }, - }, - validate: { - required: true, - }, - key: 'listSupervisedEntitiesCovered', - type: 'select', - input: true, - searchThreshold: 0.3, - }, - ], - path: 'section12OutsourcingSupervisedEntities', + ], }, { - title: '1.5 Country-specific questions', - collapsible: false, - key: 'countrySpecificQuestionsPanel', - customConditional: - 'var listSupervisedEntitiesCovered = _.get(data, "generalInformation.listSupervisedEntitiesCovered", []);\r\nshow = listSupervisedEntitiesCovered.some(entity => (entity.countryCode == "LU") || (entity.countryCode == "DE"));', - type: 'panel', - label: 'Panel', - input: false, - tableView: false, - components: [ - { - label: 'DE specific questions', - tableView: false, - key: 'deSpecific', - customConditional: - 'var listSupervisedEntitiesCovered = _.get(data, "generalInformation.listSupervisedEntitiesCovered", []);\r\nshow = listSupervisedEntitiesCovered.some(entity => entity.countryCode == "DE");', - type: 'container', - input: true, - components: [ + type: 'button', + label: 'Submit', + key: 'submit', + disableOnInvalid: true, + input: true, + tableView: false, + }, + ], +}; + +const form5 = { + title: 'form5', + name: 'form5', + path: 'form5', + type: 'form', + display: 'form', + components: [ + { + label: 'generalInformation', + tableView: false, + key: 'generalInformation', + type: 'container', + input: true, + components: [ { - title: 'Additional questions for DE entities', - collapsible: false, - key: 'panel', - type: 'panel', - label: 'Panel', - input: false, - tableView: false, - components: [ - { - title: 'Sub-outsourcing', - collapsible: false, - key: 'suboutsourcing', - type: 'panel', - label: 'Panel', - input: false, - tableView: false, - components: [ + title: 'Select here one entry', + theme: 'primary', + collapsible: false, + key: 'sectionOutsourcingSupervisedEntites', + type: 'panel', + label: 'Outsourcing supervised entities', + tableView: false, + input: false, + components: [ { - label: 'Here select yes', - optionsLabelPosition: 'right', - customClass: 'tooltip-text-left', - inline: true, - tableView: false, - values: [ - { - label: 'Yes', - value: 'yes', - shortcut: '', + label: 'Select the only option here', + widget: 'choicesjs', + description: '
    ', + tableView: true, + multiple: true, + dataSrc: 'json', + data: { + json: [ + { + id: 6256, + longName: 'Bank_DE', + leiCode: 'LEI6256', + countryCode: 'DE', + }, + ], }, - { - label: 'No', - value: 'no', - shortcut: '', + template: + '{{ item.longName }} [{{item.countryCode}}] {{item.leiCode}}', + customOptions: { + searchResultLimit: 100, + fuseOptions: { + threshold: 0.1, + distance: 9000, + }, }, - ], - validate: { - required: true, - }, - key: 'criticalPartsToBeOutsourcedSuboutsourcer', - type: 'radio', - labelWidth: 100, - input: true, + validate: { + required: true, + }, + key: 'listSupervisedEntitiesCovered', + type: 'select', + input: true, + searchThreshold: 0.3, }, + ], + path: 'section12OutsourcingSupervisedEntities', + }, + { + title: '1.5 Country-specific questions', + collapsible: false, + key: 'countrySpecificQuestionsPanel', + customConditional: + 'var listSupervisedEntitiesCovered = _.get(data, "generalInformation.listSupervisedEntitiesCovered", []);\r\nshow = listSupervisedEntitiesCovered.some(entity => (entity.countryCode == "LU") || (entity.countryCode == "DE"));', + type: 'panel', + label: 'Panel', + input: false, + tableView: false, + components: [ { - label: 'add a row, it will be removed upon save', - customClass: 'ml-3', - tableView: true, - templates: { - header: - '
    \r\n
    Name
    \r\n
    Location
    \r\n
    Location of the data
    \r\n
    \r\n
    ', - row: '
    \r\n
    \r\n {{ _.get(row, \'nameSuboutsourcer\', \'\') }}\r\n
    \r\n
    \r\n {{ _.get(row, \'locationSuboutsourcer.name\', \'\') }}\r\n
    \r\n
    \r\n {{ _.get(row, \'locationDataSub.name\', \'\') }}\r\n
    \r\n
    \r\n {% if (instance.options.readOnly) { %}\r\n
    \r\n
    \r\n \r\n
    \r\n
    \r\n {% } else { %}\r\n
    \r\n \r\n \r\n
    \r\n {% } %}\r\n
    \r\n
    ', - }, - addAnother: 'Add suboutsourcer', - modal: true, - saveRow: 'Close', - validate: { - required: true, - }, - rowDrafts: false, - key: 'suboutsourcers', - conditional: { - show: true, - conjunction: 'all', - conditions: [ - { - component: - 'generalInformation.deSpecific.criticalPartsToBeOutsourcedSuboutsourcer', - operator: 'isEqual', - value: 'yes', - }, - ], - }, - type: 'editgrid', - displayAsTable: false, - input: true, - components: [ - { - title: 'Sub-outsourcer(s)', - theme: 'primary', - collapsible: false, - key: 'suboutsourcerS', - type: 'panel', - label: 'Panel', - input: false, - tableView: false, - components: [ + label: 'DE specific questions', + tableView: false, + key: 'deSpecific', + customConditional: + 'var listSupervisedEntitiesCovered = _.get(data, "generalInformation.listSupervisedEntitiesCovered", []);\r\nshow = listSupervisedEntitiesCovered.some(entity => entity.countryCode == "DE");', + type: 'container', + input: true, + components: [ { - label: 'This edit grid row will disappear', - applyMaskOn: 'change', - tableView: true, - validate: { - required: true, - maxLength: 100, - }, - key: 'nameSuboutsourcer', - type: 'textfield', - input: true, + title: 'Additional questions for DE entities', + collapsible: false, + key: 'panel', + type: 'panel', + label: 'Panel', + input: false, + tableView: false, + components: [ + { + title: 'Sub-outsourcing', + collapsible: false, + key: 'suboutsourcing', + type: 'panel', + label: 'Panel', + input: false, + tableView: false, + components: [ + { + label: 'Here select yes', + optionsLabelPosition: + 'right', + customClass: + 'tooltip-text-left', + inline: true, + tableView: false, + values: [ + { + label: 'Yes', + value: 'yes', + shortcut: '', + }, + { + label: 'No', + value: 'no', + shortcut: '', + }, + ], + validate: { + required: true, + }, + key: 'criticalPartsToBeOutsourcedSuboutsourcer', + type: 'radio', + labelWidth: 100, + input: true, + }, + { + label: 'add a row, it will be removed upon save', + customClass: 'ml-3', + tableView: true, + templates: { + header: '
    \r\n
    Name
    \r\n
    Location
    \r\n
    Location of the data
    \r\n
    \r\n
    ', + row: '
    \r\n
    \r\n {{ _.get(row, \'nameSuboutsourcer\', \'\') }}\r\n
    \r\n
    \r\n {{ _.get(row, \'locationSuboutsourcer.name\', \'\') }}\r\n
    \r\n
    \r\n {{ _.get(row, \'locationDataSub.name\', \'\') }}\r\n
    \r\n
    \r\n {% if (instance.options.readOnly) { %}\r\n
    \r\n
    \r\n \r\n
    \r\n
    \r\n {% } else { %}\r\n
    \r\n \r\n \r\n
    \r\n {% } %}\r\n
    \r\n
    ', + }, + addAnother: + 'Add suboutsourcer', + modal: true, + saveRow: 'Close', + validate: { + required: true, + }, + rowDrafts: false, + key: 'suboutsourcers', + conditional: { + show: true, + conjunction: 'all', + conditions: [ + { + component: + 'generalInformation.deSpecific.criticalPartsToBeOutsourcedSuboutsourcer', + operator: + 'isEqual', + value: 'yes', + }, + ], + }, + type: 'editgrid', + displayAsTable: false, + input: true, + components: [ + { + title: 'Sub-outsourcer(s)', + theme: 'primary', + collapsible: false, + key: 'suboutsourcerS', + type: 'panel', + label: 'Panel', + input: false, + tableView: false, + components: [ + { + label: 'This edit grid row will disappear', + applyMaskOn: + 'change', + tableView: true, + validate: { + required: true, + maxLength: 100, + }, + key: 'nameSuboutsourcer', + type: 'textfield', + input: true, + }, + ], + }, + ], + }, + ], + }, + ], }, - ], - }, - ], + ], }, - ], - }, - ], + ], }, - ], - }, - ], + ], }, - ], - }, - { - label: 'Submit', - action: 'saveState', - showValidations: false, - tableView: false, - key: 'submit', - type: 'button', - input: true, - state: 'draft', - }, - ], + { + label: 'Submit', + action: 'saveState', + showValidations: false, + tableView: false, + key: 'submit', + type: 'button', + input: true, + state: 'draft', + }, + ], }; const form6 = { - title: 'form6', - name: 'form6', - path: 'form6', - type: 'form', - display: 'form', - components: [ - { - label: 'Text Field', - applyMaskOn: 'change', - tableView: true, - key: 'textField', - type: 'textfield', - input: true, - }, - { - label: 'Container', - tableView: false, - key: 'container', - type: 'container', - input: true, - components: [ + title: 'form6', + name: 'form6', + path: 'form6', + type: 'form', + display: 'form', + components: [ { - label: 'Edit Grid', - tableView: false, - rowDrafts: false, - key: 'editGrid', - logic: [ - { - name: 'ret', - trigger: { - type: 'simple', - simple: { - show: true, - conjunction: 'all', - conditions: [ - { - component: 'textField', - operator: 'isEqual', - value: 'show', - }, - ], - }, - }, - actions: [ + label: 'Text Field', + applyMaskOn: 'change', + tableView: true, + key: 'textField', + type: 'textfield', + input: true, + }, + { + label: 'Container', + tableView: false, + key: 'container', + type: 'container', + input: true, + components: [ { - name: 'ter', - type: 'value', - value: - "value = [{number:1, textArea: 'test'}, {number:2, textArea: 'test2'}]", + label: 'Edit Grid', + tableView: false, + rowDrafts: false, + key: 'editGrid', + logic: [ + { + name: 'ret', + trigger: { + type: 'simple', + simple: { + show: true, + conjunction: 'all', + conditions: [ + { + component: 'textField', + operator: 'isEqual', + value: 'show', + }, + ], + }, + }, + actions: [ + { + name: 'ter', + type: 'value', + value: "value = [{number:1, textArea: 'test'}, {number:2, textArea: 'test2'}]", + }, + ], + }, + ], + type: 'editgrid', + displayAsTable: false, + input: true, + components: [ + { + label: 'Number', + applyMaskOn: 'change', + mask: false, + tableView: false, + delimiter: false, + requireDecimal: false, + inputFormat: 'plain', + truncateMultipleSpaces: false, + key: 'number', + type: 'number', + input: true, + }, + { + label: 'Text Area', + applyMaskOn: 'change', + autoExpand: false, + tableView: true, + key: 'textArea', + type: 'textarea', + input: true, + }, + ], }, - ], - }, - ], - type: 'editgrid', - displayAsTable: false, - input: true, - components: [ - { - label: 'Number', - applyMaskOn: 'change', - mask: false, - tableView: false, - delimiter: false, - requireDecimal: false, - inputFormat: 'plain', - truncateMultipleSpaces: false, - key: 'number', - type: 'number', - input: true, - }, - { - label: 'Text Area', - applyMaskOn: 'change', - autoExpand: false, - tableView: true, - key: 'textArea', - type: 'textarea', - input: true, - }, - ], + ], + }, + { + type: 'button', + label: 'Submit', + key: 'submit', + disableOnInvalid: true, + input: true, + tableView: false, }, - ], - }, - { - type: 'button', - label: 'Submit', - key: 'submit', - disableOnInvalid: true, - input: true, - tableView: false, - }, - ], + ], }; export default { form1, form2, form3, form4, form5, form6 }; diff --git a/src/components/editgrid/fixtures/index.js b/src/components/editgrid/fixtures/index.js index 007b3841ec..72eff71e23 100644 --- a/src/components/editgrid/fixtures/index.js +++ b/src/components/editgrid/fixtures/index.js @@ -16,4 +16,23 @@ import comp15 from './comp15'; import withOpenWhenEmptyAndConditions from './comp-with-conditions-and-openWhenEmpty'; import compOpenWhenEmpty from './comp-openWhenEmpty'; import compWithCustomDefaultValue from './comp-with-custom-default-value'; -export { comp1, comp2, comp3, comp10, comp11, comp12, comp13, comp14, comp15, comp4, comp5, comp6, comp7, comp8, comp9, compOpenWhenEmpty, withOpenWhenEmptyAndConditions, compWithCustomDefaultValue }; +export { + comp1, + comp2, + comp3, + comp10, + comp11, + comp12, + comp13, + comp14, + comp15, + comp4, + comp5, + comp6, + comp7, + comp8, + comp9, + compOpenWhenEmpty, + withOpenWhenEmptyAndConditions, + compWithCustomDefaultValue, +}; diff --git a/src/components/email/Email.form.js b/src/components/email/Email.form.js index 12258dd955..6a8ecd4ce7 100644 --- a/src/components/email/Email.form.js +++ b/src/components/email/Email.form.js @@ -2,15 +2,18 @@ import baseEditForm from '../textfield/TextField.form'; import EmailEditFormDisplay from './editForm/Email.edit.display'; import EmailEditFormValidation from './editForm/Email.edit.validation'; -export default function(...extend) { - return baseEditForm([ - { - key: 'display', - components: EmailEditFormDisplay, - }, - { - key: 'validation', - components: EmailEditFormValidation, - }, - ], ...extend); +export default function (...extend) { + return baseEditForm( + [ + { + key: 'display', + components: EmailEditFormDisplay, + }, + { + key: 'validation', + components: EmailEditFormValidation, + }, + ], + ...extend, + ); } diff --git a/src/components/email/Email.js b/src/components/email/Email.js index 6104d63521..3818774e2c 100644 --- a/src/components/email/Email.js +++ b/src/components/email/Email.js @@ -1,54 +1,56 @@ import TextFieldComponent from '../textfield/TextField'; export default class EmailComponent extends TextFieldComponent { - static schema(...extend) { - return TextFieldComponent.schema({ - type: 'email', - label: 'Email', - key: 'email', - inputType: 'email', - kickbox: { - enabled: false - } - }, ...extend); - } + static schema(...extend) { + return TextFieldComponent.schema( + { + type: 'email', + label: 'Email', + key: 'email', + inputType: 'email', + kickbox: { + enabled: false, + }, + }, + ...extend, + ); + } - static get builderInfo() { - return { - title: 'Email', - group: 'advanced', - icon: 'at', - documentation: '/userguide/form-building/advanced-components#email', - weight: 10, - schema: EmailComponent.schema() - }; - } + static get builderInfo() { + return { + title: 'Email', + group: 'advanced', + icon: 'at', + documentation: '/userguide/form-building/advanced-components#email', + weight: 10, + schema: EmailComponent.schema(), + }; + } - init() { - super.init(); - this.validators.push('email'); - } + init() { + super.init(); + this.validators.push('email'); + } - get defaultSchema() { - return EmailComponent.schema(); - } + get defaultSchema() { + return EmailComponent.schema(); + } - get inputInfo() { - const info = super.inputInfo; - info.attr.type = this.component.mask ? 'password' : 'email'; - return info; - } + get inputInfo() { + const info = super.inputInfo; + info.attr.type = this.component.mask ? 'password' : 'email'; + return info; + } - normalizeValue(value, flags = {}) { - value = super.normalizeValue(value, flags); - if (this.options.server && !!value) { - if (Array.isArray(value)) { - value = value.map(val => val.toLowerCase()); - } - else { - value = value.toLowerCase(); - } + normalizeValue(value, flags = {}) { + value = super.normalizeValue(value, flags); + if (this.options.server && !!value) { + if (Array.isArray(value)) { + value = value.map((val) => val.toLowerCase()); + } else { + value = value.toLowerCase(); + } + } + return value; } - return value; - } } diff --git a/src/components/email/Email.unit.js b/src/components/email/Email.unit.js index f6c54c83b6..fc9742ddc4 100644 --- a/src/components/email/Email.unit.js +++ b/src/components/email/Email.unit.js @@ -4,213 +4,282 @@ import { Formio } from './../../Formio'; import assert from 'power-assert'; import _ from 'lodash'; -import { - comp1, - comp2 -} from './fixtures'; - -describe('Email Component', function() { - it('Should build a email component', function() { - return Harness.testCreate(EmailComponent, comp1); - }); - - it('Should provide min/max length validation', function(done) { - const form = _.cloneDeep(comp2); - form.components[0].validate = { minLength: 7, maxLength: 10 }; - - const validValues = [ - '', - 'test@te.st', - 't__t@t.st', - '_t@test.st' - ]; - - const invalidMin = [ - 't@t.st', - ]; - - const invalidMax = [ - 't@test.test', - 'test@test.test', - ]; - - const testValidity = (values, valid, message, lastValue) => { - _.each(values, (value) => { - const element = document.createElement('div'); - - Formio.createForm(element, form).then(form => { - form.setPristine(false); - - const component = form.getComponent('email'); - const changed = component.setValue(value); - const error = message; - - if (value) { - assert.equal(changed, true, 'Should set value'); - } - - setTimeout(() => { - if (valid) { - assert.equal(!!component.error, false, 'Should not contain error'); - } - else { - assert.equal(!!component.error, true, 'Should contain error'); - assert.equal(component.error.message, error, 'Should contain error message'); - assert.equal(component.element.classList.contains('has-error'), true, 'Should contain error class'); - assert.equal(component.refs.messageContainer.textContent.trim(), error, 'Should show error'); - } - - if (_.isEqual(value, lastValue)) { - done(); - } - }, 300); - }).catch(done); - }); - }; - - testValidity(validValues, true); - testValidity(invalidMin, false, 'Email must have at least 7 characters.'); - testValidity(invalidMax, false, 'Email must have no more than 10 characters.', invalidMax[invalidMax.length-1]); - }); - - it('Should provide pattern validation', function(done) { - const form = _.cloneDeep(comp2); - form.components[0].validate = { pattern: '^[0-9]+@[0-9]+\\.[a-z]{2,4}$' }; - - const validValues = [ - '000@12.ts', - '123456@1234.com', - '123456@1234.come', - '' - ]; - - const invalidValues = [ - '123_456@1234.com', - '123456@12.34.com', - 'test@123.com', - '00000@123t.com' - ]; - - const testValidity = (values, valid, message, lastValue) => { - _.each(values, (value) => { - const element = document.createElement('div'); - - Formio.createForm(element, form).then(form => { - form.setPristine(false); - - const component = form.getComponent('email'); - const changed = component.setValue(value); - const error = message; - - if (value) { - assert.equal(changed, true, 'Should set value'); - } - - setTimeout(() => { - if (valid) { - assert.equal(!!component.error, false, 'Should not contain error'); - } - else { - assert.equal(!!component.error, true, 'Should contain error'); - assert.equal(component.error.message.trim(), error, 'Should contain error message'); - assert.equal(component.element.classList.contains('has-error'), true, 'Should contain error class'); - assert.equal(component.refs.messageContainer.textContent.trim(), error, 'Should show error'); - } - - if (_.isEqual(value, lastValue)) { - done(); - } - }, 300); - }).catch(done); - }); - }; - - testValidity(validValues, true); - testValidity(invalidValues, - false, - 'Email does not match the pattern ^[0-9]+@[0-9]+\\.[a-z]{2,4}$', - invalidValues[invalidValues.length-1] - ); - }); - - it('Should provide email validation', function(done) { - const form = _.cloneDeep(comp2); - - const validValues = [ - '', - 'test_test@test.test', - '123456@1234.test', - 'TEST@TEST.test', - 'te.st_te%st@test.com', - 'te/st___te%st@test.com', - '"John..Doe"@example.com', - 'test-test@test.com', - 'test-test@te-st.com', - '0-0-0-0-0@12-3-t.com' - ]; - - const invalidValues = [ - 'te.st_te%st@test.123', - ' ', - 'test.test', - 'test-te st@test.com', - ' test_test@test.test', - '.test_te%st@test.com', - 'te/st___t,e%st@test.com', - 'te[st]t@test.com', - 'test@test.com ', - 'test@', - 'test@test', - 'test@test.', - '@test.com', - 'test@.com', - '00000@123t.m', - '00000@123t.m-com', - 'test(test)@mail.com', - 'John..Doe@example.com', - 'john.smith(comment)@example.com', - 'test-test.@test.com' - ]; - - const testValidity = (values, valid, message, lastValue) => { - _.each(values, (value) => { - const element = document.createElement('div'); - - Formio.createForm(element, form).then(form => { - form.setPristine(false); - - const component = form.getComponent('email'); - const changed = component.setValue(value); - const error = message; - - if (value) { - assert.equal(changed, true, 'Should set value'); - } - - setTimeout(() => { - if (valid) { - assert.equal(!!component.error, false, 'Should not contain error'); - } - else { - assert.equal(!!component.error, true, 'Should contain error'); - assert.equal(component.error.message.trim(), error, 'Should contain error message'); - assert.equal(component.element.classList.contains('has-error'), true, 'Should contain error class'); - assert.equal(component.refs.messageContainer.textContent.trim(), error, 'Should show error'); - } - - if (_.isEqual(value, lastValue)) { - done(); - } - }, 300); - }).catch(done); - }); - }; - - testValidity(validValues, true); - testValidity(invalidValues, - false, - 'Email must be a valid email.', - invalidValues[invalidValues.length-1] - ); - }); +import { comp1, comp2 } from './fixtures'; + +describe('Email Component', function () { + it('Should build a email component', function () { + return Harness.testCreate(EmailComponent, comp1); + }); + + it('Should provide min/max length validation', function (done) { + const form = _.cloneDeep(comp2); + form.components[0].validate = { minLength: 7, maxLength: 10 }; + + const validValues = ['', 'test@te.st', 't__t@t.st', '_t@test.st']; + + const invalidMin = ['t@t.st']; + + const invalidMax = ['t@test.test', 'test@test.test']; + + const testValidity = (values, valid, message, lastValue) => { + _.each(values, (value) => { + const element = document.createElement('div'); + + Formio.createForm(element, form) + .then((form) => { + form.setPristine(false); + + const component = form.getComponent('email'); + const changed = component.setValue(value); + const error = message; + + if (value) { + assert.equal(changed, true, 'Should set value'); + } + + setTimeout(() => { + if (valid) { + assert.equal( + !!component.error, + false, + 'Should not contain error', + ); + } else { + assert.equal( + !!component.error, + true, + 'Should contain error', + ); + assert.equal( + component.error.message, + error, + 'Should contain error message', + ); + assert.equal( + component.element.classList.contains( + 'has-error', + ), + true, + 'Should contain error class', + ); + assert.equal( + component.refs.messageContainer.textContent.trim(), + error, + 'Should show error', + ); + } + + if (_.isEqual(value, lastValue)) { + done(); + } + }, 300); + }) + .catch(done); + }); + }; + + testValidity(validValues, true); + testValidity( + invalidMin, + false, + 'Email must have at least 7 characters.', + ); + testValidity( + invalidMax, + false, + 'Email must have no more than 10 characters.', + invalidMax[invalidMax.length - 1], + ); + }); + + it('Should provide pattern validation', function (done) { + const form = _.cloneDeep(comp2); + form.components[0].validate = { + pattern: '^[0-9]+@[0-9]+\\.[a-z]{2,4}$', + }; + + const validValues = [ + '000@12.ts', + '123456@1234.com', + '123456@1234.come', + '', + ]; + + const invalidValues = [ + '123_456@1234.com', + '123456@12.34.com', + 'test@123.com', + '00000@123t.com', + ]; + + const testValidity = (values, valid, message, lastValue) => { + _.each(values, (value) => { + const element = document.createElement('div'); + + Formio.createForm(element, form) + .then((form) => { + form.setPristine(false); + + const component = form.getComponent('email'); + const changed = component.setValue(value); + const error = message; + + if (value) { + assert.equal(changed, true, 'Should set value'); + } + + setTimeout(() => { + if (valid) { + assert.equal( + !!component.error, + false, + 'Should not contain error', + ); + } else { + assert.equal( + !!component.error, + true, + 'Should contain error', + ); + assert.equal( + component.error.message.trim(), + error, + 'Should contain error message', + ); + assert.equal( + component.element.classList.contains( + 'has-error', + ), + true, + 'Should contain error class', + ); + assert.equal( + component.refs.messageContainer.textContent.trim(), + error, + 'Should show error', + ); + } + + if (_.isEqual(value, lastValue)) { + done(); + } + }, 300); + }) + .catch(done); + }); + }; + + testValidity(validValues, true); + testValidity( + invalidValues, + false, + 'Email does not match the pattern ^[0-9]+@[0-9]+\\.[a-z]{2,4}$', + invalidValues[invalidValues.length - 1], + ); + }); + + it('Should provide email validation', function (done) { + const form = _.cloneDeep(comp2); + + const validValues = [ + '', + 'test_test@test.test', + '123456@1234.test', + 'TEST@TEST.test', + 'te.st_te%st@test.com', + 'te/st___te%st@test.com', + '"John..Doe"@example.com', + 'test-test@test.com', + 'test-test@te-st.com', + '0-0-0-0-0@12-3-t.com', + ]; + + const invalidValues = [ + 'te.st_te%st@test.123', + ' ', + 'test.test', + 'test-te st@test.com', + ' test_test@test.test', + '.test_te%st@test.com', + 'te/st___t,e%st@test.com', + 'te[st]t@test.com', + 'test@test.com ', + 'test@', + 'test@test', + 'test@test.', + '@test.com', + 'test@.com', + '00000@123t.m', + '00000@123t.m-com', + 'test(test)@mail.com', + 'John..Doe@example.com', + 'john.smith(comment)@example.com', + 'test-test.@test.com', + ]; + + const testValidity = (values, valid, message, lastValue) => { + _.each(values, (value) => { + const element = document.createElement('div'); + + Formio.createForm(element, form) + .then((form) => { + form.setPristine(false); + + const component = form.getComponent('email'); + const changed = component.setValue(value); + const error = message; + + if (value) { + assert.equal(changed, true, 'Should set value'); + } + + setTimeout(() => { + if (valid) { + assert.equal( + !!component.error, + false, + 'Should not contain error', + ); + } else { + assert.equal( + !!component.error, + true, + 'Should contain error', + ); + assert.equal( + component.error.message.trim(), + error, + 'Should contain error message', + ); + assert.equal( + component.element.classList.contains( + 'has-error', + ), + true, + 'Should contain error class', + ); + assert.equal( + component.refs.messageContainer.textContent.trim(), + error, + 'Should show error', + ); + } + + if (_.isEqual(value, lastValue)) { + done(); + } + }, 300); + }) + .catch(done); + }); + }; + + testValidity(validValues, true); + testValidity( + invalidValues, + false, + 'Email must be a valid email.', + invalidValues[invalidValues.length - 1], + ); + }); }); diff --git a/src/components/email/editForm/Email.edit.display.js b/src/components/email/editForm/Email.edit.display.js index 1e8370f506..353f4c6633 100644 --- a/src/components/email/editForm/Email.edit.display.js +++ b/src/components/email/editForm/Email.edit.display.js @@ -1,18 +1,18 @@ export default [ - { - key: 'inputMask', - ignore: true, - }, - { - key: 'allowMultipleMasks', - ignore: true, - }, - { - key: 'showWordCount', - ignore: true - }, - { - key: 'showCharCount', - ignore: true - } + { + key: 'inputMask', + ignore: true, + }, + { + key: 'allowMultipleMasks', + ignore: true, + }, + { + key: 'showWordCount', + ignore: true, + }, + { + key: 'showCharCount', + ignore: true, + }, ]; diff --git a/src/components/email/editForm/Email.edit.validation.js b/src/components/email/editForm/Email.edit.validation.js index 99c2da18a5..8d4eaa4cf5 100644 --- a/src/components/email/editForm/Email.edit.validation.js +++ b/src/components/email/editForm/Email.edit.validation.js @@ -1,26 +1,27 @@ export default [ - { - key: 'validate.minWords', - ignore: true - }, - { - key: 'validate.maxWords', - ignore: true - }, - { - type: 'panel', - label: 'Kickbox', - title: 'Kickbox', - weight: 102, - key: 'kickbox', - components: [ - { - type: 'checkbox', - label: 'Enable', - tooltip: 'Enable Kickbox validation for this email field.', - description: 'Validate this email using the Kickbox email validation service.', - key: 'kickbox.enabled' - } - ] - } + { + key: 'validate.minWords', + ignore: true, + }, + { + key: 'validate.maxWords', + ignore: true, + }, + { + type: 'panel', + label: 'Kickbox', + title: 'Kickbox', + weight: 102, + key: 'kickbox', + components: [ + { + type: 'checkbox', + label: 'Enable', + tooltip: 'Enable Kickbox validation for this email field.', + description: + 'Validate this email using the Kickbox email validation service.', + key: 'kickbox.enabled', + }, + ], + }, ]; diff --git a/src/components/email/fixtures/comp1.js b/src/components/email/fixtures/comp1.js index c2908bc333..c68de7689c 100644 --- a/src/components/email/fixtures/comp1.js +++ b/src/components/email/fixtures/comp1.js @@ -1,26 +1,24 @@ export default { - 'conditional': { - 'eq': '', - 'when': null, - 'show': '' - }, - 'tags': [ - - ], - 'type': 'email', - 'kickbox': { - 'enabled': false - }, - 'persistent': true, - 'unique': false, - 'protected': false, - 'defaultValue': '', - 'suffix': '', - 'prefix': '', - 'placeholder': 'Enter your email address', - 'key': 'email', - 'label': 'Email', - 'inputType': 'email', - 'tableView': true, - 'input': true + conditional: { + eq: '', + when: null, + show: '', + }, + tags: [], + type: 'email', + kickbox: { + enabled: false, + }, + persistent: true, + unique: false, + protected: false, + defaultValue: '', + suffix: '', + prefix: '', + placeholder: 'Enter your email address', + key: 'email', + label: 'Email', + inputType: 'email', + tableView: true, + input: true, }; diff --git a/src/components/email/fixtures/comp2.js b/src/components/email/fixtures/comp2.js index 43722e61ce..05a5e8094f 100644 --- a/src/components/email/fixtures/comp2.js +++ b/src/components/email/fixtures/comp2.js @@ -1,11 +1,24 @@ export default { - type: 'form', - components: [ - { label: 'Email', tableView: true, key: 'email', type: 'email', input: true }, - { label: 'Submit', showValidations: false, tableView: false, key: 'submit', type: 'button', input: true } - ], - title: 'test11', - display: 'form', - name: 'test11', - path: 'test11', + type: 'form', + components: [ + { + label: 'Email', + tableView: true, + key: 'email', + type: 'email', + input: true, + }, + { + label: 'Submit', + showValidations: false, + tableView: false, + key: 'submit', + type: 'button', + input: true, + }, + ], + title: 'test11', + display: 'form', + name: 'test11', + path: 'test11', }; diff --git a/src/components/email/fixtures/values.js b/src/components/email/fixtures/values.js index b17ef74ee8..4e9b9ed765 100644 --- a/src/components/email/fixtures/values.js +++ b/src/components/email/fixtures/values.js @@ -1,4 +1 @@ -export default [ - 'admin@example.com', - 'test@example.com', -]; +export default ['admin@example.com', 'test@example.com']; diff --git a/src/components/fieldset/Fieldset.form.js b/src/components/fieldset/Fieldset.form.js index 8ac01aeaf8..888c09989b 100644 --- a/src/components/fieldset/Fieldset.form.js +++ b/src/components/fieldset/Fieldset.form.js @@ -1,10 +1,13 @@ import nestedComponentForm from '../_classes/nested/NestedComponent.form'; import FieldSetEditDisplay from './editForm/Fieldset.edit.display'; -export default function(...extend) { - return nestedComponentForm([ - { - key: 'display', - components: FieldSetEditDisplay - }, - ], ...extend); +export default function (...extend) { + return nestedComponentForm( + [ + { + key: 'display', + components: FieldSetEditDisplay, + }, + ], + ...extend, + ); } diff --git a/src/components/fieldset/Fieldset.js b/src/components/fieldset/Fieldset.js index 9b71fe87e0..4e58de2a1f 100644 --- a/src/components/fieldset/Fieldset.js +++ b/src/components/fieldset/Fieldset.js @@ -1,48 +1,52 @@ import NestedComponent from '../_classes/nested/NestedComponent'; export default class FieldsetComponent extends NestedComponent { - static schema(...extend) { - return NestedComponent.schema({ - label: 'Field Set', - key: 'fieldSet', - type: 'fieldset', - legend: '', - components: [], - input: false, - persistent: false - }, ...extend); - } + static schema(...extend) { + return NestedComponent.schema( + { + label: 'Field Set', + key: 'fieldSet', + type: 'fieldset', + legend: '', + components: [], + input: false, + persistent: false, + }, + ...extend, + ); + } - static get builderInfo() { - return { - title: 'Field Set', - icon: 'th-large', - group: 'layout', - documentation: '/userguide/form-building/layout-components#field-set', - showPreview: false, - weight: 20, - schema: FieldsetComponent.schema() - }; - } + static get builderInfo() { + return { + title: 'Field Set', + icon: 'th-large', + group: 'layout', + documentation: + '/userguide/form-building/layout-components#field-set', + showPreview: false, + weight: 20, + schema: FieldsetComponent.schema(), + }; + } - static savedValueTypes() { - return []; - } + static savedValueTypes() { + return []; + } - get defaultSchema() { - return FieldsetComponent.schema(); - } + get defaultSchema() { + return FieldsetComponent.schema(); + } - get className() { - return `${this.transform('class', 'form-group')} ${super.className}`; - } + get className() { + return `${this.transform('class', 'form-group')} ${super.className}`; + } - get templateName() { - return 'fieldset'; - } + get templateName() { + return 'fieldset'; + } - constructor(...args) { - super(...args); - this.noField = true; - } + constructor(...args) { + super(...args); + this.noField = true; + } } diff --git a/src/components/fieldset/Fieldset.unit.js b/src/components/fieldset/Fieldset.unit.js index 7d6b515eb6..a8ac867a13 100644 --- a/src/components/fieldset/Fieldset.unit.js +++ b/src/components/fieldset/Fieldset.unit.js @@ -1,14 +1,14 @@ import Harness from '../../../test/harness'; import FieldsetComponent from './Fieldset'; -import { - comp1 -} from './fixtures'; +import { comp1 } from './fixtures'; -describe('Fieldset Component', function() { - it('Should build a fieldset component', function() { - return Harness.testCreate(FieldsetComponent, comp1).then((component) => { - Harness.testElements(component, 'input[type="text"]', 2); +describe('Fieldset Component', function () { + it('Should build a fieldset component', function () { + return Harness.testCreate(FieldsetComponent, comp1).then( + (component) => { + Harness.testElements(component, 'input[type="text"]', 2); + }, + ); }); - }); }); diff --git a/src/components/fieldset/editForm/Fieldset.edit.display.js b/src/components/fieldset/editForm/Fieldset.edit.display.js index 9692ccf97b..f03bd0342b 100644 --- a/src/components/fieldset/editForm/Fieldset.edit.display.js +++ b/src/components/fieldset/editForm/Fieldset.edit.display.js @@ -1,42 +1,42 @@ export default [ - { - key: 'labelPosition', - ignore: true - }, - { - key: 'placeholder', - ignore: true - }, - { - key: 'description', - ignore: true - }, - { - key: 'hideLabel', - ignore: true - }, - { - key: 'autofocus', - ignore: true - }, - { - key: 'tableView', - ignore: true - }, - { - key: 'label', - hidden: true, - calculateValue(context) { - return context.data.legend; - } - }, - { - weight: 1, - type: 'textfield', - input: true, - key: 'legend', - label: 'Legend', - placeholder: 'Legend', - tooltip: 'The legend for this Fieldset.' - }, + { + key: 'labelPosition', + ignore: true, + }, + { + key: 'placeholder', + ignore: true, + }, + { + key: 'description', + ignore: true, + }, + { + key: 'hideLabel', + ignore: true, + }, + { + key: 'autofocus', + ignore: true, + }, + { + key: 'tableView', + ignore: true, + }, + { + key: 'label', + hidden: true, + calculateValue(context) { + return context.data.legend; + }, + }, + { + weight: 1, + type: 'textfield', + input: true, + key: 'legend', + label: 'Legend', + placeholder: 'Legend', + tooltip: 'The legend for this Fieldset.', + }, ]; diff --git a/src/components/fieldset/fixtures/comp1.js b/src/components/fieldset/fixtures/comp1.js index fbf6a18af2..0970e59110 100644 --- a/src/components/fieldset/fixtures/comp1.js +++ b/src/components/fieldset/fixtures/comp1.js @@ -1,85 +1,79 @@ export default { - 'key': 'fieldset1', - 'input': false, - 'tableView': true, - 'legend': 'User Information', - 'components': [ - { - 'input': true, - 'tableView': true, - 'inputType': 'text', - 'inputMask': '', - 'label': 'First Name', - 'key': 'firstName', - 'placeholder': '', - 'prefix': '', - 'suffix': '', - 'multiple': false, - 'defaultValue': '', - 'protected': false, - 'unique': false, - 'persistent': true, - 'clearOnHide': true, - 'validate': { - 'required': false, - 'minLength': '', - 'maxLength': '', - 'pattern': '', - 'custom': '', - 'customPrivate': false - }, - 'conditional': { - 'show': '', - 'when': null, - 'eq': '' - }, - 'type': 'textfield', - 'tags': [ - - ] + key: 'fieldset1', + input: false, + tableView: true, + legend: 'User Information', + components: [ + { + input: true, + tableView: true, + inputType: 'text', + inputMask: '', + label: 'First Name', + key: 'firstName', + placeholder: '', + prefix: '', + suffix: '', + multiple: false, + defaultValue: '', + protected: false, + unique: false, + persistent: true, + clearOnHide: true, + validate: { + required: false, + minLength: '', + maxLength: '', + pattern: '', + custom: '', + customPrivate: false, + }, + conditional: { + show: '', + when: null, + eq: '', + }, + type: 'textfield', + tags: [], + }, + { + input: true, + tableView: true, + inputType: 'text', + inputMask: '', + label: 'Last Name', + key: 'lastName', + placeholder: '', + prefix: '', + suffix: '', + multiple: false, + defaultValue: '', + protected: false, + unique: false, + persistent: true, + clearOnHide: true, + validate: { + required: false, + minLength: '', + maxLength: '', + pattern: '', + custom: '', + customPrivate: false, + }, + conditional: { + show: '', + when: null, + eq: '', + }, + type: 'textfield', + tags: [], + }, + ], + type: 'fieldset', + tags: [], + conditional: { + show: '', + when: null, + eq: '', }, - { - 'input': true, - 'tableView': true, - 'inputType': 'text', - 'inputMask': '', - 'label': 'Last Name', - 'key': 'lastName', - 'placeholder': '', - 'prefix': '', - 'suffix': '', - 'multiple': false, - 'defaultValue': '', - 'protected': false, - 'unique': false, - 'persistent': true, - 'clearOnHide': true, - 'validate': { - 'required': false, - 'minLength': '', - 'maxLength': '', - 'pattern': '', - 'custom': '', - 'customPrivate': false - }, - 'conditional': { - 'show': '', - 'when': null, - 'eq': '' - }, - 'type': 'textfield', - 'tags': [ - - ] - } - ], - 'type': 'fieldset', - 'tags': [ - - ], - 'conditional': { - 'show': '', - 'when': null, - 'eq': '' - } }; diff --git a/src/components/file/File.form.js b/src/components/file/File.form.js index 686005eb02..e814e411ad 100644 --- a/src/components/file/File.form.js +++ b/src/components/file/File.form.js @@ -4,25 +4,28 @@ import FileEditDisplay from './editForm/File.edit.display'; import FileEditFile from './editForm/File.edit.file'; import FileEditValidation from './editForm/File.edit.validation'; -export default function(...extend) { - return Components.baseEditForm([ - { - key: 'display', - components: FileEditDisplay - }, - { - key: 'data', - components: FileEditData - }, - { - label: 'File', - key: 'file', - weight: 5, - components: FileEditFile - }, - { - key: 'validation', - components: FileEditValidation - }, - ], ...extend); +export default function (...extend) { + return Components.baseEditForm( + [ + { + key: 'display', + components: FileEditDisplay, + }, + { + key: 'data', + components: FileEditData, + }, + { + label: 'File', + key: 'file', + weight: 5, + components: FileEditFile, + }, + { + key: 'validation', + components: FileEditValidation, + }, + ], + ...extend, + ); } diff --git a/src/components/file/File.js b/src/components/file/File.js index 9ca3aebe98..ff4a0e3da5 100644 --- a/src/components/file/File.js +++ b/src/components/file/File.js @@ -1,5 +1,9 @@ import Field from '../_classes/field/Field'; -import { componentValueTypes, getComponentSavedTypes, uniqueName } from '../../utils/utils'; +import { + componentValueTypes, + getComponentSavedTypes, + uniqueName, +} from '../../utils/utils'; import download from 'downloadjs'; import _ from 'lodash'; import fileProcessor from '../../providers/processor/fileProcessor'; @@ -12,1179 +16,1336 @@ let webViewCamera = 'undefined' !== typeof window ? navigator.camera : Camera; let htmlCanvasElement; if (typeof window !== 'undefined') { - htmlCanvasElement = window.HTMLCanvasElement; -} -else if (typeof global !== 'undefined') { - htmlCanvasElement = global.HTMLCanvasElement; + htmlCanvasElement = window.HTMLCanvasElement; +} else if (typeof global !== 'undefined') { + htmlCanvasElement = global.HTMLCanvasElement; } if (htmlCanvasElement && !htmlCanvasElement.prototype.toBlob) { - Object.defineProperty(HTMLCanvasElement.prototype, 'toBlob', { - value: function(callback, type, quality) { - var canvas = this; - setTimeout(function() { - var binStr = atob(canvas.toDataURL(type, quality).split(',')[1]), - len = binStr.length, - arr = new Uint8Array(len); - - for (var i = 0; i < len; i++) { - arr[i] = binStr.charCodeAt(i); - } - - callback(new Blob([arr], { type: type || 'image/png' })); - }); - } - }); + Object.defineProperty(HTMLCanvasElement.prototype, 'toBlob', { + value: function (callback, type, quality) { + var canvas = this; + setTimeout(function () { + var binStr = atob( + canvas.toDataURL(type, quality).split(',')[1], + ), + len = binStr.length, + arr = new Uint8Array(len); + + for (var i = 0; i < len; i++) { + arr[i] = binStr.charCodeAt(i); + } + + callback(new Blob([arr], { type: type || 'image/png' })); + }); + }, + }); } const createRandomString = () => Math.random().toString(36).substring(2, 15); export default class FileComponent extends Field { - static schema(...extend) { - return Field.schema({ - type: 'file', - label: 'Upload', - key: 'file', - image: false, - privateDownload: false, - imageSize: '200', - filePattern: '*', - fileMinSize: '0KB', - fileMaxSize: '1GB', - uploadOnly: false, - }, ...extend); - } - - static get builderInfo() { - return { - title: 'File', - group: 'premium', - icon: 'file', - documentation: '/userguide/form-building/premium-components#file', - weight: 100, - schema: FileComponent.schema(), - }; - } - - static get serverConditionSettings() { - return FileComponent.conditionOperatorsSettings; - } - - static get conditionOperatorsSettings() { - return { - ...super.conditionOperatorsSettings, - operators: ['isEmpty', 'isNotEmpty'], - }; - } - - static savedValueTypes(schema) { - schema = schema || {}; - - return getComponentSavedTypes(schema) || [componentValueTypes.object]; - } - - init() { - super.init(); - webViewCamera = navigator.camera || Camera; - const fileReaderSupported = (typeof FileReader !== 'undefined'); - const formDataSupported = typeof window !== 'undefined' ? Boolean(window.FormData) : false; - const progressSupported = (typeof window !== 'undefined' && window.XMLHttpRequest) ? ('upload' in new XMLHttpRequest) : false; - - this.support = { - filereader: fileReaderSupported, - formdata: formDataSupported, - hasWarning: !fileReaderSupported || !formDataSupported || !progressSupported, - progress: progressSupported, - }; - this.cameraMode = false; - this.fileDropHidden = false; - this.filesToSync = { - filesToUpload: [], - filesToDelete: [], - }; - this.isSyncing = false; - this.abortUploads = []; - } - - get dataReady() { - return this.filesReady || Promise.resolve(); - } - - get defaultSchema() { - return FileComponent.schema(); - } - - loadImage(fileInfo) { - if (this.component.privateDownload) { - fileInfo.private = true; - } - return this.fileService.downloadFile(fileInfo).then((result) => result.url); - } - - get emptyValue() { - return []; - } - - getValueAsString(value) { - if (_.isArray(value)) { - return _.map(value, 'originalName').join(', '); - } - - return _.get(value, 'originalName', ''); - } - - getValue() { - return this.dataValue; - } - - get defaultValue() { - const value = super.defaultValue; - return Array.isArray(value) ? value : []; - } - - get hasTypes() { - return this.component.fileTypes && - Array.isArray(this.component.fileTypes) && - this.component.fileTypes.length !== 0 && - (this.component.fileTypes[0].label !== '' || this.component.fileTypes[0].value !== ''); - } - - get fileDropHidden() { - return this._fileBrowseHidden; - } - - set fileDropHidden(value) { - if (typeof value !== 'boolean' || this.component.multiple) { - return; - } - this._fileBrowseHidden = value; - } - - get shouldSyncFiles() { - return Boolean(this.filesToSync.filesToDelete.length || this.filesToSync.filesToUpload.length); - } - - get autoSync() { - return _.get(this, 'component.autoSync', false); - } - - get columnsSize() { - const actionsColumn = this.disabled ? 0 : this.autoSync ? 2 : 1; - const typeColumn = this.hasTypes ? 2 : 0; - const sizeColumn = 2; - const nameColumn = 12 - actionsColumn - typeColumn - sizeColumn; - - return { - name: nameColumn, - size: sizeColumn, - type: typeColumn, - actions: actionsColumn, - }; - } - - render() { - const { filesToDelete, filesToUpload } = this.filesToSync; - return super.render(this.renderTemplate('file', { - fileSize: this.fileSize, - files: this.dataValue || [], - filesToDelete, - filesToUpload, - disabled: this.disabled, - support: this.support, - fileDropHidden: this.fileDropHidden, - showSyncButton: this.autoSync && (filesToDelete.length || filesToUpload.length), - isSyncing: this.isSyncing, - columns: this.columnsSize, - })); - } - - getVideoStream(constraints) { - return navigator.mediaDevices.getUserMedia({ - video: { - width: { min: 640, ideal: 1920 }, - height: { min: 360, ideal: 1080 }, - aspectRatio: { ideal: 16 / 9 }, - ...constraints, - }, - audio: false, - }); - } - - stopVideoStream(videoStream) { - videoStream.getVideoTracks().forEach((track) => track.stop()); - } - - getFrame(videoPlayer) { - return new Promise((resolve) => { - const canvas = document.createElement('canvas'); - canvas.height = videoPlayer.videoHeight; - canvas.width = videoPlayer.videoWidth; - const context = canvas.getContext('2d'); - context.drawImage(videoPlayer, 0, 0); - canvas.toBlob(resolve); - }); - } + static schema(...extend) { + return Field.schema( + { + type: 'file', + label: 'Upload', + key: 'file', + image: false, + privateDownload: false, + imageSize: '200', + filePattern: '*', + fileMinSize: '0KB', + fileMaxSize: '1GB', + uploadOnly: false, + }, + ...extend, + ); + } - startVideo() { - this.getVideoStream() - .then((stream) => { - this.videoStream = stream; + static get builderInfo() { + return { + title: 'File', + group: 'premium', + icon: 'file', + documentation: '/userguide/form-building/premium-components#file', + weight: 100, + schema: FileComponent.schema(), + }; + } - const { videoPlayer } = this.refs; - if (!videoPlayer) { - console.warn('Video player not found in template.'); - this.cameraMode = false; - this.redraw(); - return; - } + static get serverConditionSettings() { + return FileComponent.conditionOperatorsSettings; + } - videoPlayer.srcObject = stream; - const width = parseInt(this.component.webcamSize) || 320; - videoPlayer.setAttribute('width', width); - videoPlayer.play(); - }) - .catch((err) => { - console.error(err); + static get conditionOperatorsSettings() { + return { + ...super.conditionOperatorsSettings, + operators: ['isEmpty', 'isNotEmpty'], + }; + } + + static savedValueTypes(schema) { + schema = schema || {}; + + return getComponentSavedTypes(schema) || [componentValueTypes.object]; + } + + init() { + super.init(); + webViewCamera = navigator.camera || Camera; + const fileReaderSupported = typeof FileReader !== 'undefined'; + const formDataSupported = + typeof window !== 'undefined' ? Boolean(window.FormData) : false; + const progressSupported = + typeof window !== 'undefined' && window.XMLHttpRequest + ? 'upload' in new XMLHttpRequest() + : false; + + this.support = { + filereader: fileReaderSupported, + formdata: formDataSupported, + hasWarning: + !fileReaderSupported || + !formDataSupported || + !progressSupported, + progress: progressSupported, + }; this.cameraMode = false; - this.redraw(); - }); - } + this.fileDropHidden = false; + this.filesToSync = { + filesToUpload: [], + filesToDelete: [], + }; + this.isSyncing = false; + this.abortUploads = []; + } - stopVideo() { - if (this.videoStream) { - this.stopVideoStream(this.videoStream); - this.videoStream = null; + get dataReady() { + return this.filesReady || Promise.resolve(); } - } - takePicture() { - const { videoPlayer } = this.refs; - if (!videoPlayer) { - console.warn('Video player not found in template.'); - this.cameraMode = false; - this.redraw(); - return; + get defaultSchema() { + return FileComponent.schema(); } - this.getFrame(videoPlayer) - .then((frame) => { - frame.name = `photo-${Date.now()}.png`; - this.handleFilesToUpload([frame]); - this.cameraMode = false; - this.redraw(); - }); - } - - browseFiles(attrs = {}) { - return new Promise((resolve) => { - const fileInput = this.ce('input', { - type: 'file', - style: 'height: 0; width: 0; visibility: hidden;', - tabindex: '-1', - ...attrs, - }); - document.body.appendChild(fileInput); - - fileInput.addEventListener('change', () => { - resolve(fileInput.files); - document.body.removeChild(fileInput); - }, true); - - // There is no direct way to trigger a file dialog. To work around this, create an input of type file and trigger - // a click event on it. - if (typeof fileInput.trigger === 'function') { - fileInput.trigger('click'); - } - else { - fileInput.click(); - } - }); - } - - set cameraMode(value) { - this._cameraMode = value; - - if (value) { - this.startVideo(); - } - else { - this.stopVideo(); - } - } - - get cameraMode() { - return this._cameraMode; - } - - get useWebViewCamera() { - return this.imageUpload && webViewCamera; - } - - get imageUpload() { - return Boolean(this.component.image); - } - - get browseOptions() { - const options = {}; - - if (this.component.multiple) { - options.multiple = true; - } - if (this.component.capture) { - options.capture = this.component.capture; - } - //use "accept" attribute only for desktop devices because of its limited support by mobile browsers - const filePattern = this.component.filePattern.trim() || ''; - if (!this.isMobile.any) { - const imagesPattern = 'image/*'; - - if (this.imageUpload && (!filePattern || filePattern === '*')) { - options.accept = imagesPattern; - } - else if (this.imageUpload && !filePattern.includes(imagesPattern)) { - options.accept = `${imagesPattern},${filePattern}`; - } - else { - options.accept = filePattern; - } - } - // if input capture is set, we need the "accept" attribute to determine which device to launch - else if (this.component.capture) { - if (filePattern.includes('video')) { - options.accept = 'video/*'; - } - else if (filePattern.includes('audio')) { - options.accept = 'audio/*'; - } - else { - options.accept = 'image/*'; - } - } - - return options; - } - - get actions() { - return { - abort: this.abortRequest.bind(this), - }; - } - - attach(element) { - this.loadRefs(element, { - fileDrop: 'single', - fileBrowse: 'single', - galleryButton: 'single', - cameraButton: 'single', - takePictureButton: 'single', - toggleCameraMode: 'single', - videoPlayer: 'single', - fileLink: 'multiple', - removeLink: 'multiple', - fileToSyncRemove: 'multiple', - fileImage: 'multiple', - fileType: 'multiple', - fileProcessingLoader: 'single', - syncNow: 'single', - restoreFile: 'multiple', - progress: 'multiple', - }); - // Ensure we have an empty input refs. We need this for the setValue method to redraw the control when it is set. - this.refs.input = []; - const superAttach = super.attach(element); - - if (this.refs.fileDrop) { - // if (!this.statuses.length) { - // this.refs.fileDrop.removeAttribute('hidden'); - // } - const _this = this; - this.addEventListener(this.refs.fileDrop, 'dragover', function(event) { - this.className = 'fileSelector fileDragOver'; - event.preventDefault(); - }); - this.addEventListener(this.refs.fileDrop, 'dragleave', function(event) { - this.className = 'fileSelector'; - event.preventDefault(); - }); - this.addEventListener(this.refs.fileDrop, 'drop', function(event) { - this.className = 'fileSelector'; - event.preventDefault(); - _this.handleFilesToUpload(event.dataTransfer.files); - }); - } - - this.addEventListener(element, 'click', (event) => { - this.handleAction(event); - }); + loadImage(fileInfo) { + if (this.component.privateDownload) { + fileInfo.private = true; + } + return this.fileService + .downloadFile(fileInfo) + .then((result) => result.url); + } - if (this.refs.fileBrowse) { - this.addEventListener(this.refs.fileBrowse, 'click', (event) => { - event.preventDefault(); - this.browseFiles(this.browseOptions) - .then((files) => { - this.handleFilesToUpload(files); - }); - }); - } - - this.refs.fileLink.forEach((fileLink, index) => { - this.addEventListener(fileLink, 'click', (event) => { - event.preventDefault(); - this.getFile(this.dataValue[index]); - }); - }); + get emptyValue() { + return []; + } - this.refs.removeLink.forEach((removeLink, index) => { - this.addEventListener(removeLink, 'click', (event) => { - event.preventDefault(); - const fileInfo = this.dataValue[index]; - this.handleFileToRemove(fileInfo); - }); - }); + getValueAsString(value) { + if (_.isArray(value)) { + return _.map(value, 'originalName').join(', '); + } - this.refs.fileToSyncRemove.forEach((fileToSyncRemove, index) => { - this.addEventListener(fileToSyncRemove, 'click', (event) => { - event.preventDefault(); - this.filesToSync.filesToUpload.splice(index, 1); - this.redraw(); - }); - }); + return _.get(value, 'originalName', ''); + } - this.refs.restoreFile.forEach((fileToRestore, index) => { - this.addEventListener(fileToRestore, 'click', (event) => { - event.preventDefault(); - const fileInfo = this.filesToSync.filesToDelete[index]; - delete fileInfo.status; - delete fileInfo.message; - this.filesToSync.filesToDelete.splice(index, 1); - this.dataValue.push(fileInfo); - this.triggerChange(); - this.redraw(); - }); - }); + getValue() { + return this.dataValue; + } - if (this.refs.galleryButton && webViewCamera) { - this.addEventListener(this.refs.galleryButton, 'click', (event) => { - event.preventDefault(); - webViewCamera.getPicture((success) => { - window.resolveLocalFileSystemURL(success, (fileEntry) => { - fileEntry.file((file) => { - const reader = new FileReader(); - reader.onloadend = (evt) => { - const blob = new Blob([new Uint8Array(evt.target.result)], { type: file.type }); - blob.name = file.name; - this.handleFilesToUpload([blob]); - }; - reader.readAsArrayBuffer(file); - }); - } - ); - }, (err) => { - console.error(err); - }, { - sourceType: webViewCamera.PictureSourceType.PHOTOLIBRARY, + get defaultValue() { + const value = super.defaultValue; + return Array.isArray(value) ? value : []; + } + + get hasTypes() { + return ( + this.component.fileTypes && + Array.isArray(this.component.fileTypes) && + this.component.fileTypes.length !== 0 && + (this.component.fileTypes[0].label !== '' || + this.component.fileTypes[0].value !== '') + ); + } + + get fileDropHidden() { + return this._fileBrowseHidden; + } + + set fileDropHidden(value) { + if (typeof value !== 'boolean' || this.component.multiple) { + return; + } + this._fileBrowseHidden = value; + } + + get shouldSyncFiles() { + return Boolean( + this.filesToSync.filesToDelete.length || + this.filesToSync.filesToUpload.length, + ); + } + + get autoSync() { + return _.get(this, 'component.autoSync', false); + } + + get columnsSize() { + const actionsColumn = this.disabled ? 0 : this.autoSync ? 2 : 1; + const typeColumn = this.hasTypes ? 2 : 0; + const sizeColumn = 2; + const nameColumn = 12 - actionsColumn - typeColumn - sizeColumn; + + return { + name: nameColumn, + size: sizeColumn, + type: typeColumn, + actions: actionsColumn, + }; + } + + render() { + const { filesToDelete, filesToUpload } = this.filesToSync; + return super.render( + this.renderTemplate('file', { + fileSize: this.fileSize, + files: this.dataValue || [], + filesToDelete, + filesToUpload, + disabled: this.disabled, + support: this.support, + fileDropHidden: this.fileDropHidden, + showSyncButton: + this.autoSync && + (filesToDelete.length || filesToUpload.length), + isSyncing: this.isSyncing, + columns: this.columnsSize, + }), + ); + } + + getVideoStream(constraints) { + return navigator.mediaDevices.getUserMedia({ + video: { + width: { min: 640, ideal: 1920 }, + height: { min: 360, ideal: 1080 }, + aspectRatio: { ideal: 16 / 9 }, + ...constraints, + }, + audio: false, }); - }); - } - - if (this.refs.cameraButton && webViewCamera) { - this.addEventListener(this.refs.cameraButton, 'click', (event) => { - event.preventDefault(); - webViewCamera.getPicture((success) => { - window.resolveLocalFileSystemURL(success, (fileEntry) => { - fileEntry.file((file) => { - const reader = new FileReader(); - reader.onloadend = (evt) => { - const blob = new Blob([new Uint8Array(evt.target.result)], { type: file.type }); - blob.name = file.name; - this.handleFilesToUpload([blob]); - }; - reader.readAsArrayBuffer(file); - }); - } - ); - }, (err) => { - console.error(err); - }, { - sourceType: webViewCamera.PictureSourceType.CAMERA, - encodingType: webViewCamera.EncodingType.PNG, - mediaType: webViewCamera.MediaType.PICTURE, - saveToPhotoAlbum: true, - correctOrientation: false, + } + + stopVideoStream(videoStream) { + videoStream.getVideoTracks().forEach((track) => track.stop()); + } + + getFrame(videoPlayer) { + return new Promise((resolve) => { + const canvas = document.createElement('canvas'); + canvas.height = videoPlayer.videoHeight; + canvas.width = videoPlayer.videoWidth; + const context = canvas.getContext('2d'); + context.drawImage(videoPlayer, 0, 0); + canvas.toBlob(resolve); }); - }); } - if (this.refs.takePictureButton) { - this.addEventListener(this.refs.takePictureButton, 'click', (event) => { - event.preventDefault(); - this.takePicture(); - }); + startVideo() { + this.getVideoStream() + .then((stream) => { + this.videoStream = stream; + + const { videoPlayer } = this.refs; + if (!videoPlayer) { + console.warn('Video player not found in template.'); + this.cameraMode = false; + this.redraw(); + return; + } + + videoPlayer.srcObject = stream; + const width = parseInt(this.component.webcamSize) || 320; + videoPlayer.setAttribute('width', width); + videoPlayer.play(); + }) + .catch((err) => { + console.error(err); + this.cameraMode = false; + this.redraw(); + }); } - if (this.refs.toggleCameraMode) { - this.addEventListener(this.refs.toggleCameraMode, 'click', (event) => { - event.preventDefault(); - this.cameraMode = !this.cameraMode; - this.redraw(); - }); + stopVideo() { + if (this.videoStream) { + this.stopVideoStream(this.videoStream); + this.videoStream = null; + } } - this.refs.fileType.forEach((fileType, index) => { - if (!this.dataValue[index]) { - return; - } + takePicture() { + const { videoPlayer } = this.refs; + if (!videoPlayer) { + console.warn('Video player not found in template.'); + this.cameraMode = false; + this.redraw(); + return; + } + + this.getFrame(videoPlayer).then((frame) => { + frame.name = `photo-${Date.now()}.png`; + this.handleFilesToUpload([frame]); + this.cameraMode = false; + this.redraw(); + }); + } - this.dataValue[index].fileType = this.dataValue[index].fileType || this.component.fileTypes[0].label; + browseFiles(attrs = {}) { + return new Promise((resolve) => { + const fileInput = this.ce('input', { + type: 'file', + style: 'height: 0; width: 0; visibility: hidden;', + tabindex: '-1', + ...attrs, + }); + document.body.appendChild(fileInput); + + fileInput.addEventListener( + 'change', + () => { + resolve(fileInput.files); + document.body.removeChild(fileInput); + }, + true, + ); + + // There is no direct way to trigger a file dialog. To work around this, create an input of type file and trigger + // a click event on it. + if (typeof fileInput.trigger === 'function') { + fileInput.trigger('click'); + } else { + fileInput.click(); + } + }); + } - this.addEventListener(fileType, 'change', (event) => { - event.preventDefault(); + set cameraMode(value) { + this._cameraMode = value; - const fileType = this.component.fileTypes.find((typeObj) => typeObj.value === event.target.value); + if (value) { + this.startVideo(); + } else { + this.stopVideo(); + } + } - this.dataValue[index].fileType = fileType.label; - }); - }); + get cameraMode() { + return this._cameraMode; + } - this.addEventListener(this.refs.syncNow, 'click', (event) => { - event.preventDefault(); - this.syncFiles(); - }); + get useWebViewCamera() { + return this.imageUpload && webViewCamera; + } - const fileService = this.fileService; - if (fileService) { - const loadingImages = []; - this.filesReady = new Promise((resolve, reject) => { - this.filesReadyResolve = resolve; - this.filesReadyReject = reject; - }); - this.refs.fileImage.forEach((image, index) => { - loadingImages.push(this.loadImage(this.dataValue[index]).then((url) => (image.src = url))); - }); - if (loadingImages.length) { - Promise.all(loadingImages).then(() => { - this.filesReadyResolve(); - }).catch(() => this.filesReadyReject()); - } - else { - this.filesReadyResolve(); - } - } - return superAttach; - } - - /* eslint-disable max-len */ - fileSize(a, b, c, d, e) { - return `${(b = Math, c = b.log, d = 1024, e = c(a) / c(d) | 0, a / b.pow(d, e)).toFixed(2)} ${e ? `${'kMGTPEZY'[--e]}B` : 'Bytes'}`; - } - - /* eslint-enable max-len */ - - /* eslint-disable max-depth */ - globStringToRegex(str) { - str = str.replace(/\s/g, ''); - - let regexp = '', excludes = []; - if (str.length > 2 && str[0] === '/' && str[str.length - 1] === '/') { - regexp = str.substring(1, str.length - 1); - } - else { - const split = str.split(','); - if (split.length > 1) { - for (let i = 0; i < split.length; i++) { - const r = this.globStringToRegex(split[i]); - if (r.regexp) { - regexp += `(${r.regexp})`; - if (i < split.length - 1) { - regexp += '|'; + get imageUpload() { + return Boolean(this.component.image); + } + + get browseOptions() { + const options = {}; + + if (this.component.multiple) { + options.multiple = true; + } + if (this.component.capture) { + options.capture = this.component.capture; + } + //use "accept" attribute only for desktop devices because of its limited support by mobile browsers + const filePattern = this.component.filePattern.trim() || ''; + if (!this.isMobile.any) { + const imagesPattern = 'image/*'; + + if (this.imageUpload && (!filePattern || filePattern === '*')) { + options.accept = imagesPattern; + } else if ( + this.imageUpload && + !filePattern.includes(imagesPattern) + ) { + options.accept = `${imagesPattern},${filePattern}`; + } else { + options.accept = filePattern; } - } - else { - excludes = excludes.concat(r.excludes); - } } - } - else { - if (str.startsWith('!')) { - excludes.push(`^((?!${this.globStringToRegex(str.substring(1)).regexp}).)*$`); + // if input capture is set, we need the "accept" attribute to determine which device to launch + else if (this.component.capture) { + if (filePattern.includes('video')) { + options.accept = 'video/*'; + } else if (filePattern.includes('audio')) { + options.accept = 'audio/*'; + } else { + options.accept = 'image/*'; + } } - else { - if (str.startsWith('.')) { - str = `*${str}`; - } - regexp = `^${str.replace(new RegExp('[.\\\\+*?\\[\\^\\]$(){}=!<>|:\\-]', 'g'), '\\$&')}$`; - regexp = regexp.replace(/\\\*/g, '.*').replace(/\\\?/g, '.'); + + return options; + } + + get actions() { + return { + abort: this.abortRequest.bind(this), + }; + } + + attach(element) { + this.loadRefs(element, { + fileDrop: 'single', + fileBrowse: 'single', + galleryButton: 'single', + cameraButton: 'single', + takePictureButton: 'single', + toggleCameraMode: 'single', + videoPlayer: 'single', + fileLink: 'multiple', + removeLink: 'multiple', + fileToSyncRemove: 'multiple', + fileImage: 'multiple', + fileType: 'multiple', + fileProcessingLoader: 'single', + syncNow: 'single', + restoreFile: 'multiple', + progress: 'multiple', + }); + // Ensure we have an empty input refs. We need this for the setValue method to redraw the control when it is set. + this.refs.input = []; + const superAttach = super.attach(element); + + if (this.refs.fileDrop) { + // if (!this.statuses.length) { + // this.refs.fileDrop.removeAttribute('hidden'); + // } + const _this = this; + this.addEventListener( + this.refs.fileDrop, + 'dragover', + function (event) { + this.className = 'fileSelector fileDragOver'; + event.preventDefault(); + }, + ); + this.addEventListener( + this.refs.fileDrop, + 'dragleave', + function (event) { + this.className = 'fileSelector'; + event.preventDefault(); + }, + ); + this.addEventListener(this.refs.fileDrop, 'drop', function (event) { + this.className = 'fileSelector'; + event.preventDefault(); + _this.handleFilesToUpload(event.dataTransfer.files); + }); } - } - } - return { regexp, excludes }; - } - - /* eslint-enable max-depth */ - - translateScalars(str) { - if (typeof str === 'string') { - if (str.search(/kb/i) === str.length - 2) { - return parseFloat(str.substring(0, str.length - 2) * 1024); - } - if (str.search(/mb/i) === str.length - 2) { - return parseFloat(str.substring(0, str.length - 2) * 1024 * 1024); - } - if (str.search(/gb/i) === str.length - 2) { - return parseFloat(str.substring(0, str.length - 2) * 1024 * 1024 * 1024); - } - if (str.search(/b/i) === str.length - 1) { - return parseFloat(str.substring(0, str.length - 1)); - } - if (str.search(/s/i) === str.length - 1) { - return parseFloat(str.substring(0, str.length - 1)); - } - if (str.search(/m/i) === str.length - 1) { - return parseFloat(str.substring(0, str.length - 1) * 60); - } - if (str.search(/h/i) === str.length - 1) { - return parseFloat(str.substring(0, str.length - 1) * 3600); - } - } - return str; - } - - validatePattern(file, val) { - if (!val) { - return true; - } - const pattern = this.globStringToRegex(val); - let valid = true; - if (pattern.regexp && pattern.regexp.length) { - const regexp = new RegExp(pattern.regexp, 'i'); - valid = (!_.isNil(file.type) && regexp.test(file.type)) || - (!_.isNil(file.name) && regexp.test(file.name)); - } - valid = pattern.excludes.reduce((result, excludePattern) => { - const exclude = new RegExp(excludePattern, 'i'); - return result && (_.isNil(file.type) || exclude.test(file.type)) && - (_.isNil(file.name) || exclude.test(file.name)); - }, valid); - return valid; - } - - validateMinSize(file, val) { - return file.size + 0.1 >= this.translateScalars(val); - } - - validateMaxSize(file, val) { - return file.size - 0.1 <= this.translateScalars(val); - } - - abortRequest(id) { - const abortUpload = this.abortUploads.find(abortUpload => abortUpload.id === id); - if (abortUpload) { - abortUpload.abort(); - } - } - - handleAction(event) { - const target = event.target; - if (!target.id) { - return; - } - const [action, id] = target.id.split('-'); - if (!action || !id || !this.actions[action]) { - return; - } - - this.actions[action](id); - } - - getFileName(file) { - return uniqueName(file.name, this.component.fileNameTemplate, this.evalContext()); - } - - getInitFileToSync(file) { - const escapedFileName = file.name ? file.name.replaceAll('<', '<').replaceAll('>', '>') : file.name; - return { - id: createRandomString(), - // Get a unique name for this file to keep file collisions from occurring. - dir: this.interpolate(this.component.dir || ''), - name: this.getFileName(file), - originalName: escapedFileName, - fileKey: this.component.fileKey || 'file', - storage: this.component.storage, - options: this.component.options, - file, - size: file.size, - status: 'info', - message: this.t('Processing file. Please wait...'), - hash: '', - }; - } - - async handleSubmissionRevisions(file) { - if (this.root.form.submissionRevisions !== 'true') { - return ''; - } - - const bmf = new BMF(); - const hash = await new Promise((resolve, reject) => { - this.emit('fileUploadingStart'); - bmf.md5(file, (err, md5)=>{ - if (err) { - return reject(err); + + this.addEventListener(element, 'click', (event) => { + this.handleAction(event); + }); + + if (this.refs.fileBrowse) { + this.addEventListener(this.refs.fileBrowse, 'click', (event) => { + event.preventDefault(); + this.browseFiles(this.browseOptions).then((files) => { + this.handleFilesToUpload(files); + }); + }); } - return resolve(md5); - }); - }); - this.emit('fileUploadingEnd'); - - return hash; - } - - validateFileName(file) { - // Check if file with the same name is being uploaded - const fileWithSameNameUploading = this.filesToSync.filesToUpload - .some(fileToSync => fileToSync.file?.name === file.name); - - const fileWithSameNameUploaded = this.dataValue - .some(fileStatus => fileStatus.originalName === file.name); - - return fileWithSameNameUploaded || fileWithSameNameUploading - ? { - status: 'error', - message: this.t(`File with the same name is already ${fileWithSameNameUploading ? 'being ' : ''}uploaded`), - } - : {}; - } - - validateFileSettings(file) { - // Check file pattern - if (this.component.filePattern && !this.validatePattern(file, this.component.filePattern)) { - return { - status: 'error', - message: this.t('File is the wrong type; it must be {{ pattern }}', { - pattern: this.component.filePattern, - }), - }; - } - - // Check file minimum size - if (this.component.fileMinSize && !this.validateMinSize(file, this.component.fileMinSize)) { - return { - status: 'error', - message: this.t('File is too small; it must be at least {{ size }}', { - size: this.component.fileMinSize, - }), - }; - } - - // Check file maximum size - if (this.component.fileMaxSize && !this.validateMaxSize(file, this.component.fileMaxSize)) { - return { - status: 'error', - message: this.t('File is too big; it must be at most {{ size }}', { - size: this.component.fileMaxSize, - }), - }; - } - - return {}; - } - - validateFileService() { - const { fileService } = this; - return !fileService - ? { - status: 'error', - message: this.t('File Service not provided.'), - } - : {}; - } - - validateFile(file) { - const fileServiceValidation = this.validateFileService(); - if (fileServiceValidation.status === 'error') { - return fileServiceValidation; - } - - const fileNameValidation = this.validateFileName(file); - if (fileNameValidation.status === 'error') { - return fileNameValidation; - } - - return this.validateFileSettings(file); - } - - getGroupPermissions() { - let groupKey = null; - let groupPermissions = null; - - //Iterate through form components to find group resource if one exists - this.root.everyComponent((element) => { - if (element.component?.submissionAccess || element.component?.defaultPermission) { - groupPermissions = !element.component.submissionAccess ? [ - { - type: element.component.defaultPermission, - roles: [], - }, - ] : element.component.submissionAccess; - - groupPermissions.forEach((permission) => { - groupKey = ['admin', 'write', 'create'].includes(permission.type) ? element.component.key : null; + + this.refs.fileLink.forEach((fileLink, index) => { + this.addEventListener(fileLink, 'click', (event) => { + event.preventDefault(); + this.getFile(this.dataValue[index]); + }); + }); + + this.refs.removeLink.forEach((removeLink, index) => { + this.addEventListener(removeLink, 'click', (event) => { + event.preventDefault(); + const fileInfo = this.dataValue[index]; + this.handleFileToRemove(fileInfo); + }); + }); + + this.refs.fileToSyncRemove.forEach((fileToSyncRemove, index) => { + this.addEventListener(fileToSyncRemove, 'click', (event) => { + event.preventDefault(); + this.filesToSync.filesToUpload.splice(index, 1); + this.redraw(); + }); }); - } - }); - return { groupKey, groupPermissions }; - } + this.refs.restoreFile.forEach((fileToRestore, index) => { + this.addEventListener(fileToRestore, 'click', (event) => { + event.preventDefault(); + const fileInfo = this.filesToSync.filesToDelete[index]; + delete fileInfo.status; + delete fileInfo.message; + this.filesToSync.filesToDelete.splice(index, 1); + this.dataValue.push(fileInfo); + this.triggerChange(); + this.redraw(); + }); + }); - async triggerFileProcessor(file) { - let processedFile = null; + if (this.refs.galleryButton && webViewCamera) { + this.addEventListener(this.refs.galleryButton, 'click', (event) => { + event.preventDefault(); + webViewCamera.getPicture( + (success) => { + window.resolveLocalFileSystemURL( + success, + (fileEntry) => { + fileEntry.file((file) => { + const reader = new FileReader(); + reader.onloadend = (evt) => { + const blob = new Blob( + [new Uint8Array(evt.target.result)], + { type: file.type }, + ); + blob.name = file.name; + this.handleFilesToUpload([blob]); + }; + reader.readAsArrayBuffer(file); + }); + }, + ); + }, + (err) => { + console.error(err); + }, + { + sourceType: + webViewCamera.PictureSourceType.PHOTOLIBRARY, + }, + ); + }); + } - if (this.root.options.fileProcessor) { - try { - if (this.refs.fileProcessingLoader) { - this.refs.fileProcessingLoader.style.display = 'block'; + if (this.refs.cameraButton && webViewCamera) { + this.addEventListener(this.refs.cameraButton, 'click', (event) => { + event.preventDefault(); + webViewCamera.getPicture( + (success) => { + window.resolveLocalFileSystemURL( + success, + (fileEntry) => { + fileEntry.file((file) => { + const reader = new FileReader(); + reader.onloadend = (evt) => { + const blob = new Blob( + [new Uint8Array(evt.target.result)], + { type: file.type }, + ); + blob.name = file.name; + this.handleFilesToUpload([blob]); + }; + reader.readAsArrayBuffer(file); + }); + }, + ); + }, + (err) => { + console.error(err); + }, + { + sourceType: webViewCamera.PictureSourceType.CAMERA, + encodingType: webViewCamera.EncodingType.PNG, + mediaType: webViewCamera.MediaType.PICTURE, + saveToPhotoAlbum: true, + correctOrientation: false, + }, + ); + }); } - const fileProcessorHandler = fileProcessor(this.fileService, this.root.options.fileProcessor); - processedFile = await fileProcessorHandler(file, this.component.properties); - } - catch (err) { - this.fileDropHidden = false; - return { - status: 'error', - message: this.t('File processing has been failed.'), - }; - } - finally { - if (this.refs.fileProcessingLoader) { - this.refs.fileProcessingLoader.style.display = 'none'; + + if (this.refs.takePictureButton) { + this.addEventListener( + this.refs.takePictureButton, + 'click', + (event) => { + event.preventDefault(); + this.takePicture(); + }, + ); } - } + + if (this.refs.toggleCameraMode) { + this.addEventListener( + this.refs.toggleCameraMode, + 'click', + (event) => { + event.preventDefault(); + this.cameraMode = !this.cameraMode; + this.redraw(); + }, + ); + } + + this.refs.fileType.forEach((fileType, index) => { + if (!this.dataValue[index]) { + return; + } + + this.dataValue[index].fileType = + this.dataValue[index].fileType || + this.component.fileTypes[0].label; + + this.addEventListener(fileType, 'change', (event) => { + event.preventDefault(); + + const fileType = this.component.fileTypes.find( + (typeObj) => typeObj.value === event.target.value, + ); + + this.dataValue[index].fileType = fileType.label; + }); + }); + + this.addEventListener(this.refs.syncNow, 'click', (event) => { + event.preventDefault(); + this.syncFiles(); + }); + + const fileService = this.fileService; + if (fileService) { + const loadingImages = []; + this.filesReady = new Promise((resolve, reject) => { + this.filesReadyResolve = resolve; + this.filesReadyReject = reject; + }); + this.refs.fileImage.forEach((image, index) => { + loadingImages.push( + this.loadImage(this.dataValue[index]).then( + (url) => (image.src = url), + ), + ); + }); + if (loadingImages.length) { + Promise.all(loadingImages) + .then(() => { + this.filesReadyResolve(); + }) + .catch(() => this.filesReadyReject()); + } else { + this.filesReadyResolve(); + } + } + return superAttach; + } + + /* eslint-disable max-len */ + fileSize(a, b, c, d, e) { + return `${((b = Math), + (c = b.log), + (d = 1024), + (e = (c(a) / c(d)) | 0), + a / b.pow(d, e)).toFixed(2)} ${e ? `${'kMGTPEZY'[--e]}B` : 'Bytes'}`; } - return { - file: processedFile, - }; - } + /* eslint-enable max-len */ + + /* eslint-disable max-depth */ + globStringToRegex(str) { + str = str.replace(/\s/g, ''); + + let regexp = '', + excludes = []; + if (str.length > 2 && str[0] === '/' && str[str.length - 1] === '/') { + regexp = str.substring(1, str.length - 1); + } else { + const split = str.split(','); + if (split.length > 1) { + for (let i = 0; i < split.length; i++) { + const r = this.globStringToRegex(split[i]); + if (r.regexp) { + regexp += `(${r.regexp})`; + if (i < split.length - 1) { + regexp += '|'; + } + } else { + excludes = excludes.concat(r.excludes); + } + } + } else { + if (str.startsWith('!')) { + excludes.push( + `^((?!${ + this.globStringToRegex(str.substring(1)).regexp + }).)*$`, + ); + } else { + if (str.startsWith('.')) { + str = `*${str}`; + } + regexp = `^${str.replace( + new RegExp('[.\\\\+*?\\[\\^\\]$(){}=!<>|:\\-]', 'g'), + '\\$&', + )}$`; + regexp = regexp + .replace(/\\\*/g, '.*') + .replace(/\\\?/g, '.'); + } + } + } + return { regexp, excludes }; + } - async prepareFileToUpload(file) { - const fileToSync = this.getInitFileToSync(file); - fileToSync.hash = await this.handleSubmissionRevisions(file); + /* eslint-enable max-depth */ - const { status, message } = this.validateFile(file); - if (status === 'error') { - fileToSync.isValidationError = true; - fileToSync.status = status; - fileToSync.message = message; - return this.filesToSync.filesToUpload.push(fileToSync); + translateScalars(str) { + if (typeof str === 'string') { + if (str.search(/kb/i) === str.length - 2) { + return parseFloat(str.substring(0, str.length - 2) * 1024); + } + if (str.search(/mb/i) === str.length - 2) { + return parseFloat( + str.substring(0, str.length - 2) * 1024 * 1024, + ); + } + if (str.search(/gb/i) === str.length - 2) { + return parseFloat( + str.substring(0, str.length - 2) * 1024 * 1024 * 1024, + ); + } + if (str.search(/b/i) === str.length - 1) { + return parseFloat(str.substring(0, str.length - 1)); + } + if (str.search(/s/i) === str.length - 1) { + return parseFloat(str.substring(0, str.length - 1)); + } + if (str.search(/m/i) === str.length - 1) { + return parseFloat(str.substring(0, str.length - 1) * 60); + } + if (str.search(/h/i) === str.length - 1) { + return parseFloat(str.substring(0, str.length - 1) * 3600); + } + } + return str; } - if (this.component.privateDownload) { - file.private = true; + validatePattern(file, val) { + if (!val) { + return true; + } + const pattern = this.globStringToRegex(val); + let valid = true; + if (pattern.regexp && pattern.regexp.length) { + const regexp = new RegExp(pattern.regexp, 'i'); + valid = + (!_.isNil(file.type) && regexp.test(file.type)) || + (!_.isNil(file.name) && regexp.test(file.name)); + } + valid = pattern.excludes.reduce((result, excludePattern) => { + const exclude = new RegExp(excludePattern, 'i'); + return ( + result && + (_.isNil(file.type) || exclude.test(file.type)) && + (_.isNil(file.name) || exclude.test(file.name)) + ); + }, valid); + return valid; } - const { groupKey, groupPermissions } = this.getGroupPermissions(); + validateMinSize(file, val) { + return file.size + 0.1 >= this.translateScalars(val); + } - const processedFile = await this.triggerFileProcessor(file); - if (processedFile.status === 'error') { - fileToSync.status === 'error'; - fileToSync.message = processedFile.message; - return this.filesToSync.filesToUpload.push(fileToSync); + validateMaxSize(file, val) { + return file.size - 0.1 <= this.translateScalars(val); } - if (this.autoSync) { - fileToSync.message = this.t('Ready to be uploaded into storage'); + abortRequest(id) { + const abortUpload = this.abortUploads.find( + (abortUpload) => abortUpload.id === id, + ); + if (abortUpload) { + abortUpload.abort(); + } } - this.filesToSync.filesToUpload.push({ - ...fileToSync, - message: fileToSync.message, - file: processedFile.file || file, - url: this.interpolate(this.component.url, { file: fileToSync }), - groupPermissions, - groupResourceId: groupKey ? this.currentForm.submission.data[groupKey]._id : null, - }); - } + handleAction(event) { + const target = event.target; + if (!target.id) { + return; + } + const [action, id] = target.id.split('-'); + if (!action || !id || !this.actions[action]) { + return; + } - async prepareFilesToUpload(files) { - // Only allow one upload if not multiple. - if (!this.component.multiple) { - files = Array.prototype.slice.call(files, 0, 1); + this.actions[action](id); } - if (this.component.storage && files && files.length) { - this.fileDropHidden = true; + getFileName(file) { + return uniqueName( + file.name, + this.component.fileNameTemplate, + this.evalContext(), + ); + } - return Promise.all([...files].map(async(file) => { - await this.prepareFileToUpload(file); - this.redraw(); - })); + getInitFileToSync(file) { + const escapedFileName = file.name + ? file.name.replaceAll('<', '<').replaceAll('>', '>') + : file.name; + return { + id: createRandomString(), + // Get a unique name for this file to keep file collisions from occurring. + dir: this.interpolate(this.component.dir || ''), + name: this.getFileName(file), + originalName: escapedFileName, + fileKey: this.component.fileKey || 'file', + storage: this.component.storage, + options: this.component.options, + file, + size: file.size, + status: 'info', + message: this.t('Processing file. Please wait...'), + hash: '', + }; } - else { - return Promise.resolve(); + + async handleSubmissionRevisions(file) { + if (this.root.form.submissionRevisions !== 'true') { + return ''; + } + + const bmf = new BMF(); + const hash = await new Promise((resolve, reject) => { + this.emit('fileUploadingStart'); + bmf.md5(file, (err, md5) => { + if (err) { + return reject(err); + } + return resolve(md5); + }); + }); + this.emit('fileUploadingEnd'); + + return hash; } - } - async handleFilesToUpload(files) { - await this.prepareFilesToUpload(files); - if (!this.autoSync) { - await this.syncFiles(); + validateFileName(file) { + // Check if file with the same name is being uploaded + const fileWithSameNameUploading = this.filesToSync.filesToUpload.some( + (fileToSync) => fileToSync.file?.name === file.name, + ); + + const fileWithSameNameUploaded = this.dataValue.some( + (fileStatus) => fileStatus.originalName === file.name, + ); + + return fileWithSameNameUploaded || fileWithSameNameUploading + ? { + status: 'error', + message: this.t( + `File with the same name is already ${ + fileWithSameNameUploading ? 'being ' : '' + }uploaded`, + ), + } + : {}; } - } - prepareFileToDelete(fileInfo) { - this.filesToSync.filesToDelete.push({ - ...fileInfo, - status: 'info', - message: this.autoSync - ? this.t('Ready to be removed from storage') - : this.t('Preparing file to remove'), - }); + validateFileSettings(file) { + // Check file pattern + if ( + this.component.filePattern && + !this.validatePattern(file, this.component.filePattern) + ) { + return { + status: 'error', + message: this.t( + 'File is the wrong type; it must be {{ pattern }}', + { + pattern: this.component.filePattern, + }, + ), + }; + } - const index = this.dataValue.findIndex(file => file.name === fileInfo.name); - this.splice(index); - this.redraw(); - } + // Check file minimum size + if ( + this.component.fileMinSize && + !this.validateMinSize(file, this.component.fileMinSize) + ) { + return { + status: 'error', + message: this.t( + 'File is too small; it must be at least {{ size }}', + { + size: this.component.fileMinSize, + }, + ), + }; + } + + // Check file maximum size + if ( + this.component.fileMaxSize && + !this.validateMaxSize(file, this.component.fileMaxSize) + ) { + return { + status: 'error', + message: this.t( + 'File is too big; it must be at most {{ size }}', + { + size: this.component.fileMaxSize, + }, + ), + }; + } - handleFileToRemove(fileInfo) { - this.prepareFileToDelete(fileInfo); - if (!this.autoSync) { - this.syncFiles(); + return {}; } - } - async deleteFile(fileInfo) { - const { options = {} } = this.component; + validateFileService() { + const { fileService } = this; + return !fileService + ? { + status: 'error', + message: this.t('File Service not provided.'), + } + : {}; + } - if (fileInfo && (['url', 'indexeddb', 's3', 'azure', 'googledrive'].includes(this.component.storage))) { - const { fileService } = this; - if (fileService && typeof fileService.deleteFile === 'function') { - return await fileService.deleteFile(fileInfo, options); - } - else { - const formio = this.options.formio || (this.root && this.root.formio); + validateFile(file) { + const fileServiceValidation = this.validateFileService(); + if (fileServiceValidation.status === 'error') { + return fileServiceValidation; + } - if (formio) { - return await formio.makeRequest('', fileInfo.url, 'delete'); + const fileNameValidation = this.validateFileName(file); + if (fileNameValidation.status === 'error') { + return fileNameValidation; } - } + + return this.validateFileSettings(file); } - } - async delete() { - if (!this.filesToSync.filesToDelete.length) { - return Promise.resolve(); + getGroupPermissions() { + let groupKey = null; + let groupPermissions = null; + + //Iterate through form components to find group resource if one exists + this.root.everyComponent((element) => { + if ( + element.component?.submissionAccess || + element.component?.defaultPermission + ) { + groupPermissions = !element.component.submissionAccess + ? [ + { + type: element.component.defaultPermission, + roles: [], + }, + ] + : element.component.submissionAccess; + + groupPermissions.forEach((permission) => { + groupKey = ['admin', 'write', 'create'].includes( + permission.type, + ) + ? element.component.key + : null; + }); + } + }); + + return { groupKey, groupPermissions }; } - return await Promise.all(this.filesToSync.filesToDelete.map(async(fileToSync) => { - try { - if (fileToSync.isValidationError) { - return { fileToSync }; + async triggerFileProcessor(file) { + let processedFile = null; + + if (this.root.options.fileProcessor) { + try { + if (this.refs.fileProcessingLoader) { + this.refs.fileProcessingLoader.style.display = 'block'; + } + const fileProcessorHandler = fileProcessor( + this.fileService, + this.root.options.fileProcessor, + ); + processedFile = await fileProcessorHandler( + file, + this.component.properties, + ); + } catch (err) { + this.fileDropHidden = false; + return { + status: 'error', + message: this.t('File processing has been failed.'), + }; + } finally { + if (this.refs.fileProcessingLoader) { + this.refs.fileProcessingLoader.style.display = 'none'; + } + } } - await this.deleteFile(fileToSync); - fileToSync.status = 'success'; - fileToSync.message = this.t('Succefully removed'); - } - catch (response) { - fileToSync.status = 'error'; - fileToSync.message = typeof response === 'string' ? response : response.toString(); - } - finally { - this.redraw(); - } - - return { fileToSync }; - })); - } - - updateProgress(fileInfo, progressEvent) { - fileInfo.progress = parseInt(100.0 * progressEvent.loaded / progressEvent.total); - if (fileInfo.status !== 'progress') { - fileInfo.status = 'progress'; - delete fileInfo.message; - this.redraw(); - } - else { - const progress = Array.prototype.find.call(this.refs.progress, progressElement => progressElement.id === fileInfo.id); - progress.innerHTML = `${fileInfo.progress}% ${this.t('Complete')}`; - progress.style.width = `${fileInfo.progress}%`; - progress.ariaValueNow = fileInfo.progress.toString(); - } - } - - getMultipartOptions(fileToSync) { - let count = 0; - return this.component.useMultipartUpload && this.component.multipart ? { - ...this.component.multipart, - progressCallback: (total) => { - count++; - fileToSync.status = 'progress'; - fileToSync.progress = parseInt(100 * count / total); - delete fileToSync.message; - this.redraw(); - }, - changeMessage: (message) => { - fileToSync.message = message; - this.redraw(); - }, - } : false; - } - - async uploadFile(fileToSync) { - return await this.fileService.uploadFile( - fileToSync.storage, - fileToSync.file, - fileToSync.name, - fileToSync.dir, - // Progress callback - this.updateProgress.bind(this, fileToSync), - fileToSync.url, - fileToSync.options, - fileToSync.fileKey, - fileToSync.groupPermissions, - fileToSync.groupResourceId, - () => {}, - // Abort upload callback - (abort) => this.abortUploads.push({ - id: fileToSync.id, - abort, - }), - this.getMultipartOptions(fileToSync), - ); - } - - async upload() { - if (!this.filesToSync.filesToUpload.length) { - return Promise.resolve(); - } - - return await Promise.all(this.filesToSync.filesToUpload.map(async(fileToSync) => { - let fileInfo = null; - try { - if (fileToSync.isValidationError) { - return { - fileToSync, - fileInfo, - }; + return { + file: processedFile, + }; + } + + async prepareFileToUpload(file) { + const fileToSync = this.getInitFileToSync(file); + fileToSync.hash = await this.handleSubmissionRevisions(file); + + const { status, message } = this.validateFile(file); + if (status === 'error') { + fileToSync.isValidationError = true; + fileToSync.status = status; + fileToSync.message = message; + return this.filesToSync.filesToUpload.push(fileToSync); + } + + if (this.component.privateDownload) { + file.private = true; + } + + const { groupKey, groupPermissions } = this.getGroupPermissions(); + + const processedFile = await this.triggerFileProcessor(file); + if (processedFile.status === 'error') { + fileToSync.status === 'error'; + fileToSync.message = processedFile.message; + return this.filesToSync.filesToUpload.push(fileToSync); + } + + if (this.autoSync) { + fileToSync.message = this.t('Ready to be uploaded into storage'); + } + + this.filesToSync.filesToUpload.push({ + ...fileToSync, + message: fileToSync.message, + file: processedFile.file || file, + url: this.interpolate(this.component.url, { file: fileToSync }), + groupPermissions, + groupResourceId: groupKey + ? this.currentForm.submission.data[groupKey]._id + : null, + }); + } + + async prepareFilesToUpload(files) { + // Only allow one upload if not multiple. + if (!this.component.multiple) { + files = Array.prototype.slice.call(files, 0, 1); + } + + if (this.component.storage && files && files.length) { + this.fileDropHidden = true; + + return Promise.all( + [...files].map(async (file) => { + await this.prepareFileToUpload(file); + this.redraw(); + }), + ); + } else { + return Promise.resolve(); + } + } + + async handleFilesToUpload(files) { + await this.prepareFilesToUpload(files); + if (!this.autoSync) { + await this.syncFiles(); } + } - fileInfo = await this.uploadFile(fileToSync); - fileToSync.status = 'success'; - fileToSync.message = this.t('Succefully uploaded'); - - fileInfo.originalName = fileToSync.originalName; - fileInfo.hash = fileToSync.hash; - } - catch (response) { - fileToSync.status = 'error'; - delete fileToSync.progress; - fileToSync.message = typeof response === 'string' - ? response - : response.type === 'abort' - ? this.t('Request was aborted') - : response.toString(); - } - finally { - delete fileToSync.progress; + prepareFileToDelete(fileInfo) { + this.filesToSync.filesToDelete.push({ + ...fileInfo, + status: 'info', + message: this.autoSync + ? this.t('Ready to be removed from storage') + : this.t('Preparing file to remove'), + }); + + const index = this.dataValue.findIndex( + (file) => file.name === fileInfo.name, + ); + this.splice(index); this.redraw(); - } - - return { - fileToSync, - fileInfo, - }; - })); - } - - async syncFiles() { - this.isSyncing = true; - this.fileDropHidden = true; - this.redraw(); - try { - const [filesToDelete = [], filesToUpload = []] = await Promise.all([this.delete(), this.upload()]); - this.filesToSync.filesToDelete = filesToDelete - .filter(file => file.fileToSync?.status === 'error') - .map(file => file.fileToSync); - this.filesToSync.filesToUpload = filesToUpload - .filter(file => file.fileToSync?.status === 'error') - .map(file => file.fileToSync); - - if (!this.hasValue()) { - this.dataValue =[]; - } - - const data = filesToUpload - .filter(file => file.fileToSync?.status === 'success') - .map(file => file.fileInfo); - this.dataValue.push(...data); - this.triggerChange(); - return Promise.resolve(); - } - catch (err) { - return Promise.reject(); - } - finally { - this.isSyncing = false; - this.fileDropHidden = false; - this.abortUploads = []; - this.redraw(); - } - } - - getFile(fileInfo) { - const { options = {} } = this.component; - const { fileService } = this; - if (!fileService) { - return alert('File Service not provided'); - } - if (this.component.privateDownload) { - fileInfo.private = true; - } - fileService.downloadFile(fileInfo, options).then((file) => { - if (file) { - if (['base64', 'indexeddb'].includes(file.storage)) { - download(file.url, file.originalName || file.name, file.type); + } + + handleFileToRemove(fileInfo) { + this.prepareFileToDelete(fileInfo); + if (!this.autoSync) { + this.syncFiles(); } - else { - window.open(file.url, '_blank'); + } + + async deleteFile(fileInfo) { + const { options = {} } = this.component; + + if ( + fileInfo && + ['url', 'indexeddb', 's3', 'azure', 'googledrive'].includes( + this.component.storage, + ) + ) { + const { fileService } = this; + if (fileService && typeof fileService.deleteFile === 'function') { + return await fileService.deleteFile(fileInfo, options); + } else { + const formio = + this.options.formio || (this.root && this.root.formio); + + if (formio) { + return await formio.makeRequest('', fileInfo.url, 'delete'); + } + } } - } - }) - .catch((response) => { - // Is alert the best way to do this? - // User is expecting an immediate notification due to attempting to download a file. - alert(response); - }); - } + } - focus() { - if ('beforeFocus' in this.parent) { - this.parent.beforeFocus(this); + async delete() { + if (!this.filesToSync.filesToDelete.length) { + return Promise.resolve(); + } + + return await Promise.all( + this.filesToSync.filesToDelete.map(async (fileToSync) => { + try { + if (fileToSync.isValidationError) { + return { fileToSync }; + } + + await this.deleteFile(fileToSync); + fileToSync.status = 'success'; + fileToSync.message = this.t('Succefully removed'); + } catch (response) { + fileToSync.status = 'error'; + fileToSync.message = + typeof response === 'string' + ? response + : response.toString(); + } finally { + this.redraw(); + } + + return { fileToSync }; + }), + ); } - if (this.refs.fileBrowse) { - this.refs.fileBrowse.focus(); + updateProgress(fileInfo, progressEvent) { + fileInfo.progress = parseInt( + (100.0 * progressEvent.loaded) / progressEvent.total, + ); + if (fileInfo.status !== 'progress') { + fileInfo.status = 'progress'; + delete fileInfo.message; + this.redraw(); + } else { + const progress = Array.prototype.find.call( + this.refs.progress, + (progressElement) => progressElement.id === fileInfo.id, + ); + progress.innerHTML = `${ + fileInfo.progress + }% ${this.t('Complete')}`; + progress.style.width = `${fileInfo.progress}%`; + progress.ariaValueNow = fileInfo.progress.toString(); + } } - } - async beforeSubmit() { - try { - if (!this.autoSync) { - return Promise.resolve(); - } + getMultipartOptions(fileToSync) { + let count = 0; + return this.component.useMultipartUpload && this.component.multipart + ? { + ...this.component.multipart, + progressCallback: (total) => { + count++; + fileToSync.status = 'progress'; + fileToSync.progress = parseInt((100 * count) / total); + delete fileToSync.message; + this.redraw(); + }, + changeMessage: (message) => { + fileToSync.message = message; + this.redraw(); + }, + } + : false; + } - await this.syncFiles(); - return this.shouldSyncFiles - ? Promise.reject('Synchronization is failed') - : Promise.resolve(); + async uploadFile(fileToSync) { + return await this.fileService.uploadFile( + fileToSync.storage, + fileToSync.file, + fileToSync.name, + fileToSync.dir, + // Progress callback + this.updateProgress.bind(this, fileToSync), + fileToSync.url, + fileToSync.options, + fileToSync.fileKey, + fileToSync.groupPermissions, + fileToSync.groupResourceId, + () => {}, + // Abort upload callback + (abort) => + this.abortUploads.push({ + id: fileToSync.id, + abort, + }), + this.getMultipartOptions(fileToSync), + ); } - catch (error) { - return Promise.reject(error.message); + + async upload() { + if (!this.filesToSync.filesToUpload.length) { + return Promise.resolve(); + } + + return await Promise.all( + this.filesToSync.filesToUpload.map(async (fileToSync) => { + let fileInfo = null; + try { + if (fileToSync.isValidationError) { + return { + fileToSync, + fileInfo, + }; + } + + fileInfo = await this.uploadFile(fileToSync); + fileToSync.status = 'success'; + fileToSync.message = this.t('Succefully uploaded'); + + fileInfo.originalName = fileToSync.originalName; + fileInfo.hash = fileToSync.hash; + } catch (response) { + fileToSync.status = 'error'; + delete fileToSync.progress; + fileToSync.message = + typeof response === 'string' + ? response + : response.type === 'abort' + ? this.t('Request was aborted') + : response.toString(); + } finally { + delete fileToSync.progress; + this.redraw(); + } + + return { + fileToSync, + fileInfo, + }; + }), + ); } - } - destroy(all) { - this.stopVideo(); - super.destroy(all); - } + async syncFiles() { + this.isSyncing = true; + this.fileDropHidden = true; + this.redraw(); + try { + const [filesToDelete = [], filesToUpload = []] = await Promise.all([ + this.delete(), + this.upload(), + ]); + this.filesToSync.filesToDelete = filesToDelete + .filter((file) => file.fileToSync?.status === 'error') + .map((file) => file.fileToSync); + this.filesToSync.filesToUpload = filesToUpload + .filter((file) => file.fileToSync?.status === 'error') + .map((file) => file.fileToSync); + + if (!this.hasValue()) { + this.dataValue = []; + } + + const data = filesToUpload + .filter((file) => file.fileToSync?.status === 'success') + .map((file) => file.fileInfo); + this.dataValue.push(...data); + this.triggerChange(); + return Promise.resolve(); + } catch (err) { + return Promise.reject(); + } finally { + this.isSyncing = false; + this.fileDropHidden = false; + this.abortUploads = []; + this.redraw(); + } + } + + getFile(fileInfo) { + const { options = {} } = this.component; + const { fileService } = this; + if (!fileService) { + return alert('File Service not provided'); + } + if (this.component.privateDownload) { + fileInfo.private = true; + } + fileService + .downloadFile(fileInfo, options) + .then((file) => { + if (file) { + if (['base64', 'indexeddb'].includes(file.storage)) { + download( + file.url, + file.originalName || file.name, + file.type, + ); + } else { + window.open(file.url, '_blank'); + } + } + }) + .catch((response) => { + // Is alert the best way to do this? + // User is expecting an immediate notification due to attempting to download a file. + alert(response); + }); + } + + focus() { + if ('beforeFocus' in this.parent) { + this.parent.beforeFocus(this); + } + + if (this.refs.fileBrowse) { + this.refs.fileBrowse.focus(); + } + } + + async beforeSubmit() { + try { + if (!this.autoSync) { + return Promise.resolve(); + } + + await this.syncFiles(); + return this.shouldSyncFiles + ? Promise.reject('Synchronization is failed') + : Promise.resolve(); + } catch (error) { + return Promise.reject(error.message); + } + } + + destroy(all) { + this.stopVideo(); + super.destroy(all); + } } diff --git a/src/components/file/File.unit.js b/src/components/file/File.unit.js index d4b0e69b8a..e4918a49bd 100644 --- a/src/components/file/File.unit.js +++ b/src/components/file/File.unit.js @@ -5,270 +5,372 @@ import { comp1, comp2 } from './fixtures'; import { Formio } from './../../Formio'; import _ from 'lodash'; -describe('File Component', function() { - it('Should create a File Component', function() { - return Harness.testCreate(FileComponent, comp1).then((component) => { - const parentNode = document.createElement('div'); - const element = document.createElement('div'); - parentNode.appendChild(element); - component.build(element); - Harness.testElements(component, 'ul.list-group-striped li.list-group-header', 1); - Harness.testElements(component, 'ul.list-group-striped li.list-group-item', 1); - Harness.testElements(component, 'a.browse', 1); - assert(component.checkValidity(component.getValue()), 'Item should be valid'); - component.setValue([ - { - storage: 'base64', - name: 'IMG_5235-ce0abe18-5d3e-4ab4-84ca-b3e06684bc86.jpg', - url: 'data:image/jpg;base64,AAAAIGZ0eXBoZWljAAAAAG1pZjF', - size: 1159732, - type: 'image/jpeg', - originalName: 'IMG_5235.jpg', - }, - { - storage: 'base64', - name: 'IMG_5235-ce0abe18-5d3e-4ab4-84ca-b3e06684bc86.png', - url: 'data:image/jpg;base64,AAAAIGZ0eXBoZWljAAAAAG1pZjF', - size: 34533, - type: 'image/png', - originalName: 'IMG_5235.png', - } - ]); - Harness.testElements(component, 'ul.list-group-striped li.list-group-header', 1); - Harness.testElements(component, 'ul.list-group-striped li.list-group-item', 3); - Harness.testElements(component, 'a.browse', 0); - assert(component.checkValidity(component.getValue()), 'Item should be valid'); +describe('File Component', function () { + it('Should create a File Component', function () { + return Harness.testCreate(FileComponent, comp1).then((component) => { + const parentNode = document.createElement('div'); + const element = document.createElement('div'); + parentNode.appendChild(element); + component.build(element); + Harness.testElements( + component, + 'ul.list-group-striped li.list-group-header', + 1, + ); + Harness.testElements( + component, + 'ul.list-group-striped li.list-group-item', + 1, + ); + Harness.testElements(component, 'a.browse', 1); + assert( + component.checkValidity(component.getValue()), + 'Item should be valid', + ); + component.setValue([ + { + storage: 'base64', + name: 'IMG_5235-ce0abe18-5d3e-4ab4-84ca-b3e06684bc86.jpg', + url: 'data:image/jpg;base64,AAAAIGZ0eXBoZWljAAAAAG1pZjF', + size: 1159732, + type: 'image/jpeg', + originalName: 'IMG_5235.jpg', + }, + { + storage: 'base64', + name: 'IMG_5235-ce0abe18-5d3e-4ab4-84ca-b3e06684bc86.png', + url: 'data:image/jpg;base64,AAAAIGZ0eXBoZWljAAAAAG1pZjF', + size: 34533, + type: 'image/png', + originalName: 'IMG_5235.png', + }, + ]); + Harness.testElements( + component, + 'ul.list-group-striped li.list-group-header', + 1, + ); + Harness.testElements( + component, + 'ul.list-group-striped li.list-group-item', + 3, + ); + Harness.testElements(component, 'a.browse', 0); + assert( + component.checkValidity(component.getValue()), + 'Item should be valid', + ); + }); }); - }); - it('Should hide loader after loading process', function() { - return Harness.testCreate(FileComponent, comp1).then((component) => { - const parentNode = document.createElement('div'); - const element = document.createElement('div'); - parentNode.appendChild(element); - component.build(element); - Harness.testElements(component, 'div.loader-wrapper', 1); - component.setValue([ - { - storage: 'base64', - name: 'IMG_5235-ce0abe18-5d3e-4ab4-84ca-b3e06684bc86.jpg', - url: 'data:image/jpg;base64,AAAAIGZ0eXBoZWljAAAAAG1pZjF', - size: 1159732, - type: 'image/jpeg', - originalName: 'IMG_5235.jpg', - } - ]); - Harness.testElements(component, 'div.loader-wrapper', 0); + it('Should hide loader after loading process', function () { + return Harness.testCreate(FileComponent, comp1).then((component) => { + const parentNode = document.createElement('div'); + const element = document.createElement('div'); + parentNode.appendChild(element); + component.build(element); + Harness.testElements(component, 'div.loader-wrapper', 1); + component.setValue([ + { + storage: 'base64', + name: 'IMG_5235-ce0abe18-5d3e-4ab4-84ca-b3e06684bc86.jpg', + url: 'data:image/jpg;base64,AAAAIGZ0eXBoZWljAAAAAG1pZjF', + size: 1159732, + type: 'image/jpeg', + originalName: 'IMG_5235.jpg', + }, + ]); + Harness.testElements(component, 'div.loader-wrapper', 0); + }); }); - }); - it('Should create a multiple File Component', function() { - comp1.multiple = true; - return Harness.testCreate(FileComponent, comp1).then((component) => { - const parentNode = document.createElement('div'); - const element = document.createElement('div'); - parentNode.appendChild(element); - component.build(element); - Harness.testElements(component, 'ul.list-group-striped li.list-group-header', 1); - Harness.testElements(component, 'ul.list-group-striped li.list-group-item', 1); - Harness.testElements(component, 'a.browse', 1); - assert(component.checkValidity(component.getValue()), 'Item should be valid'); - component.setValue([ - { - storage: 'base64', - name: 'IMG_5235-ce0abe18-5d3e-4ab4-84ca-b3e06684bc86.jpg', - url: 'data:image/jpg;base64,AAAAIGZ0eXBoZWljAAAAAG1pZjF', - size: 1159732, - type: 'image/jpeg', - originalName: 'IMG_5235.jpg', - }, - { - storage: 'base64', - name: 'IMG_5235-ce0abe18-5d3e-4ab4-84ca-b3e06684bc86.png', - url: 'data:image/jpg;base64,AAAAIGZ0eXBoZWljAAAAAG1pZjF', - size: 34533, - type: 'image/png', - originalName: 'IMG_5235.png', - } - ]); - Harness.testElements(component, 'ul.list-group-striped li.list-group-header', 1); - Harness.testElements(component, 'ul.list-group-striped li.list-group-item', 3); - Harness.testElements(component, 'a.browse', 1); - assert(component.checkValidity(component.getValue()), 'Item should be valid'); + it('Should create a multiple File Component', function () { + comp1.multiple = true; + return Harness.testCreate(FileComponent, comp1).then((component) => { + const parentNode = document.createElement('div'); + const element = document.createElement('div'); + parentNode.appendChild(element); + component.build(element); + Harness.testElements( + component, + 'ul.list-group-striped li.list-group-header', + 1, + ); + Harness.testElements( + component, + 'ul.list-group-striped li.list-group-item', + 1, + ); + Harness.testElements(component, 'a.browse', 1); + assert( + component.checkValidity(component.getValue()), + 'Item should be valid', + ); + component.setValue([ + { + storage: 'base64', + name: 'IMG_5235-ce0abe18-5d3e-4ab4-84ca-b3e06684bc86.jpg', + url: 'data:image/jpg;base64,AAAAIGZ0eXBoZWljAAAAAG1pZjF', + size: 1159732, + type: 'image/jpeg', + originalName: 'IMG_5235.jpg', + }, + { + storage: 'base64', + name: 'IMG_5235-ce0abe18-5d3e-4ab4-84ca-b3e06684bc86.png', + url: 'data:image/jpg;base64,AAAAIGZ0eXBoZWljAAAAAG1pZjF', + size: 34533, + type: 'image/png', + originalName: 'IMG_5235.png', + }, + ]); + Harness.testElements( + component, + 'ul.list-group-striped li.list-group-header', + 1, + ); + Harness.testElements( + component, + 'ul.list-group-striped li.list-group-item', + 3, + ); + Harness.testElements(component, 'a.browse', 1); + assert( + component.checkValidity(component.getValue()), + 'Item should be valid', + ); + }); }); - }); - it('Should validate uploaded file according to the pattern', function(done) { - Harness.testCreate(FileComponent, comp1).then((component) => { - const validFiles =[ - { - name: 'test.jpg', - size: 27401, - type: 'image/jpeg' - }, - { - name: 'TypeScript.pdf', - size: 203123, - type: 'application/pdf' - }, - { - name: 'build with dist.png', - size: 137321, - type: 'image/png' - } - ]; + it('Should validate uploaded file according to the pattern', function (done) { + Harness.testCreate(FileComponent, comp1).then((component) => { + const validFiles = [ + { + name: 'test.jpg', + size: 27401, + type: 'image/jpeg', + }, + { + name: 'TypeScript.pdf', + size: 203123, + type: 'application/pdf', + }, + { + name: 'build with dist.png', + size: 137321, + type: 'image/png', + }, + ]; - const invalidFiles = [ - { - name: 'eventsList.xlsx', - size: 16022, - type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' - }, - { - name: 'lazy load.mp4', - size: 9083622, - type: 'video/mp4' - }, - ]; + const invalidFiles = [ + { + name: 'eventsList.xlsx', + size: 16022, + type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + }, + { + name: 'lazy load.mp4', + size: 9083622, + type: 'video/mp4', + }, + ]; - const pattern = ' .jpg, .png, .exe, .pdf '; + const pattern = ' .jpg, .png, .exe, .pdf '; - const checkValidatePattern = (files, valid) => { - files.forEach(file => { - assert.equal(component.validatePattern(file, pattern), valid, `File ${file.name} should ${valid ? '' : 'not'} correspond to the pattern`); - }); - }; + const checkValidatePattern = (files, valid) => { + files.forEach((file) => { + assert.equal( + component.validatePattern(file, pattern), + valid, + `File ${file.name} should ${ + valid ? '' : 'not' + } correspond to the pattern`, + ); + }); + }; - checkValidatePattern(validFiles, true); - checkValidatePattern(invalidFiles, false); - done(); + checkValidatePattern(validFiles, true); + checkValidatePattern(invalidFiles, false); + done(); + }); }); - }); - it('Should display uploaded file in file component only after saving', function(done) { - const form = _.cloneDeep(comp2); - const element = document.createElement('div'); + it('Should display uploaded file in file component only after saving', function (done) { + const form = _.cloneDeep(comp2); + const element = document.createElement('div'); - Formio.createForm(element, form).then(form => { - const value = [ - { - storage: 'base64', - name: '33-0ae897b9-c808-4832-a5e1-4e5d0c725f1b.jpg', - url: 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD…CiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooA//2Q==', - size: 102691, - type: 'image/jpeg', - originalName: '33.jpg', - }, - ]; - const file = form.getComponent('file'); - const openModalButton = file.componentModal.refs.openModal; - const clickEvent = new Event('click'); - openModalButton.dispatchEvent(clickEvent); + Formio.createForm(element, form) + .then((form) => { + const value = [ + { + storage: 'base64', + name: '33-0ae897b9-c808-4832-a5e1-4e5d0c725f1b.jpg', + url: 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD…CiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooA//2Q==', + size: 102691, + type: 'image/jpeg', + originalName: '33.jpg', + }, + ]; + const file = form.getComponent('file'); + const openModalButton = file.componentModal.refs.openModal; + const clickEvent = new Event('click'); + openModalButton.dispatchEvent(clickEvent); - setTimeout(() => { - assert.equal(file.componentModal.isOpened, true); - file.dataValue = value; - file.redraw(); + setTimeout(() => { + assert.equal(file.componentModal.isOpened, true); + file.dataValue = value; + file.redraw(); - setTimeout(() => { - assert.equal(file.refs.fileLink.length, 1); - const modalOverlayButton = file.componentModal.refs.modalOverlay; - modalOverlayButton.dispatchEvent(clickEvent); + setTimeout(() => { + assert.equal(file.refs.fileLink.length, 1); + const modalOverlayButton = + file.componentModal.refs.modalOverlay; + modalOverlayButton.dispatchEvent(clickEvent); - setTimeout(() => { - assert.equal(!!file.componentModal.dialogElement, true); - const dialogYesButton = file.componentModal.dialogElement.refs.dialogYesButton; - dialogYesButton.dispatchEvent(clickEvent); + setTimeout(() => { + assert.equal( + !!file.componentModal.dialogElement, + true, + ); + const dialogYesButton = + file.componentModal.dialogElement.refs + .dialogYesButton; + dialogYesButton.dispatchEvent(clickEvent); - setTimeout(() => { - assert.equal(!!file.componentModal.dialogElement, false); - file.componentModal.closeModal(); + setTimeout(() => { + assert.equal( + !!file.componentModal.dialogElement, + false, + ); + file.componentModal.closeModal(); - setTimeout(() => { - assert.equal(file.componentModal.isOpened, false); - assert.equal(file.refs.fileLink.length, 0); - assert.equal(file.componentModal.refs.openModal.textContent.trim(), 'Click to set value'); - done(); - }, 200); - }, 200); - }, 200); - }, 200); - }, 200); - }).catch(done); - }); + setTimeout(() => { + assert.equal( + file.componentModal.isOpened, + false, + ); + assert.equal(file.refs.fileLink.length, 0); + assert.equal( + file.componentModal.refs.openModal.textContent.trim(), + 'Click to set value', + ); + done(); + }, 200); + }, 200); + }, 200); + }, 200); + }, 200); + }) + .catch(done); + }); - it('Should abort the correct file when user clicks the file remove button', function(done) { - const cmp = _.cloneDeep(comp1); - const abortedFiles = []; - cmp.multiple = true; - cmp.storage = 'url'; + it('Should abort the correct file when user clicks the file remove button', function (done) { + const cmp = _.cloneDeep(comp1); + const abortedFiles = []; + cmp.multiple = true; + cmp.storage = 'url'; - const options = { - fileService: { - uploadFile: function(storage, file, fileName, dir, progressCallback, url, options, fileKey, groupPermissions, groupId, uploadStartCallback, abortCallbackSetter) { - return new Promise((resolve, reject) => { - // complete upload after 1s. - setTimeout(() => { - progressCallback({ loaded: 1, total: 1 }); - }, 10); + const options = { + fileService: { + uploadFile: function ( + storage, + file, + fileName, + dir, + progressCallback, + url, + options, + fileKey, + groupPermissions, + groupId, + uploadStartCallback, + abortCallbackSetter, + ) { + return new Promise((resolve, reject) => { + // complete upload after 1s. + setTimeout(() => { + progressCallback({ loaded: 1, total: 1 }); + }, 10); - const timeout = setTimeout(() => { - const uploadResponse = { - name: fileName, - size: file.size, - type: 'application/pdf', - url: `fake/url/${fileName}` - }; - resolve(uploadResponse); - }, 1000); + const timeout = setTimeout(() => { + const uploadResponse = { + name: fileName, + size: file.size, + type: 'application/pdf', + url: `fake/url/${fileName}`, + }; + resolve(uploadResponse); + }, 1000); - abortCallbackSetter(function() { - abortedFiles.push(file.name); - clearTimeout(timeout); - reject({ - type: 'abort', - }); - }); - }); - } - } - }; + abortCallbackSetter(function () { + abortedFiles.push(file.name); + clearTimeout(timeout); + reject({ + type: 'abort', + }); + }); + }); + }, + }, + }; - Harness.testCreate(FileComponent, cmp, options).then((component) => { - component.root = { everyComponent: () => {}, options: options, form: { submissionRevisions: false, components: [cmp] } }; - const parentNode = document.createElement('div'); - const element = document.createElement('div'); - parentNode.appendChild(element); - component.build(element); + Harness.testCreate(FileComponent, cmp, options).then((component) => { + component.root = { + everyComponent: () => {}, + options: options, + form: { submissionRevisions: false, components: [cmp] }, + }; + const parentNode = document.createElement('div'); + const element = document.createElement('div'); + parentNode.appendChild(element); + component.build(element); - const content = [1]; - const files = [new File(content, 'file.0'), new File([content], 'file.1'), new File([content], 'file.2')]; + const content = [1]; + const files = [ + new File(content, 'file.0'), + new File([content], 'file.1'), + new File([content], 'file.2'), + ]; - component.handleFilesToUpload(files); + component.handleFilesToUpload(files); - setTimeout(function() { - // Table header and 3 rows for files - Harness.testElements(component, '.list-group-item', 4); - assert.equal(component.dataValue.length, 0); - assert.equal(component.filesToSync.filesToUpload.length, 3); - assert.equal(component.filesToSync.filesToUpload[1].status, 'progress'); - assert.equal(component.filesToSync.filesToDelete.length, 0); + setTimeout(function () { + // Table header and 3 rows for files + Harness.testElements(component, '.list-group-item', 4); + assert.equal(component.dataValue.length, 0); + assert.equal(component.filesToSync.filesToUpload.length, 3); + assert.equal( + component.filesToSync.filesToUpload[1].status, + 'progress', + ); + assert.equal(component.filesToSync.filesToDelete.length, 0); - const abortIcon = component.element.querySelectorAll(`#abort-${component.filesToSync.filesToUpload[1].id}`)[0]; - assert.notEqual(abortIcon, null); - abortIcon.click(); + const abortIcon = component.element.querySelectorAll( + `#abort-${component.filesToSync.filesToUpload[1].id}`, + )[0]; + assert.notEqual(abortIcon, null); + abortIcon.click(); - setTimeout(() => { - assert.notEqual(component !== null); - assert(abortedFiles[0] === 'file.1' && abortedFiles.length === 1); - assert.equal(component.filesToSync.filesToUpload[1].status, 'error'); - assert.equal(component.filesToSync.filesToUpload[1].message, 'Request was aborted'); + setTimeout(() => { + assert.notEqual(component !== null); + assert( + abortedFiles[0] === 'file.1' && + abortedFiles.length === 1, + ); + assert.equal( + component.filesToSync.filesToUpload[1].status, + 'error', + ); + assert.equal( + component.filesToSync.filesToUpload[1].message, + 'Request was aborted', + ); - Harness.testElements(component, '.list-group-item', 4); - component.root = null; - done(); - }, 20); - }, 100); + Harness.testElements(component, '.list-group-item', 4); + component.root = null; + done(); + }, 20); + }, 100); + }); }); - }); }); diff --git a/src/components/file/editForm/File.edit.data.js b/src/components/file/editForm/File.edit.data.js index 6765474def..0972798d81 100644 --- a/src/components/file/editForm/File.edit.data.js +++ b/src/components/file/editForm/File.edit.data.js @@ -1,6 +1,6 @@ export default [ - { - key: 'defaultValue', - ignore: true, - }, + { + key: 'defaultValue', + ignore: true, + }, ]; diff --git a/src/components/file/editForm/File.edit.display.js b/src/components/file/editForm/File.edit.display.js index 63f0c68770..14a4baa6b8 100644 --- a/src/components/file/editForm/File.edit.display.js +++ b/src/components/file/editForm/File.edit.display.js @@ -1,23 +1,24 @@ export default [ - { - key: 'placeholder', - ignore: true - }, - { - type: 'checkbox', - label: 'Files Synchronization feature', - tooltip: 'Enable ability to control files synchronization. Files will be auto synced before submit.', - key: 'autoSync', - input: true, - conditional: { - json: { - in: [ - { - var: 'data.storage' - }, - ['s3', 'azure', 'googledrive'] - ], - } - } - }, + { + key: 'placeholder', + ignore: true, + }, + { + type: 'checkbox', + label: 'Files Synchronization feature', + tooltip: + 'Enable ability to control files synchronization. Files will be auto synced before submit.', + key: 'autoSync', + input: true, + conditional: { + json: { + in: [ + { + var: 'data.storage', + }, + ['s3', 'azure', 'googledrive'], + ], + }, + }, + }, ]; diff --git a/src/components/file/editForm/File.edit.file.js b/src/components/file/editForm/File.edit.file.js index 6a79593fc2..9e350d99c9 100644 --- a/src/components/file/editForm/File.edit.file.js +++ b/src/components/file/editForm/File.edit.file.js @@ -2,323 +2,353 @@ import { Formio } from '../../../Formio'; import _ from 'lodash'; export default [ - { - type: 'select', - input: true, - key: 'storage', - label: 'Storage', - placeholder: 'Select your file storage provider', - weight: 0, - tooltip: 'Which storage to save the files in.', - valueProperty: 'value', - dataSrc: 'custom', - data: { - custom() { - return _.map(Formio.Providers.getProviders('storage'), (storage, key) => ({ - label: storage.title, - value: key - })); - } - } - }, - { - type: 'checkbox', - input: true, - key: 'useMultipartUpload', - label: 'Use the S3 Multipart Upload API', - tooltip: "The S3 Multipart Upload API is designed to improve the upload experience for larger objects (> 5GB).", - conditional: { - json: { '===': [{ var: 'data.storage' }, 's3'] } + { + type: 'select', + input: true, + key: 'storage', + label: 'Storage', + placeholder: 'Select your file storage provider', + weight: 0, + tooltip: 'Which storage to save the files in.', + valueProperty: 'value', + dataSrc: 'custom', + data: { + custom() { + return _.map( + Formio.Providers.getProviders('storage'), + (storage, key) => ({ + label: storage.title, + value: key, + }), + ); + }, + }, }, - }, - { - label: 'Multipart Upload', - tableView: false, - key: 'multipart', - type: 'container', - input: true, - components: [ - { - label: 'Part Size (MB)', - applyMaskOn: 'change', - mask: false, + { + type: 'checkbox', + input: true, + key: 'useMultipartUpload', + label: 'Use the S3 Multipart Upload API', + tooltip: + "The S3 Multipart Upload API is designed to improve the upload experience for larger objects (> 5GB).", + conditional: { + json: { '===': [{ var: 'data.storage' }, 's3'] }, + }, + }, + { + label: 'Multipart Upload', tableView: false, - delimiter: false, - requireDecimal: false, - inputFormat: 'plain', - truncateMultipleSpaces: false, - validate: { - min: 5, - max: 5000, + key: 'multipart', + type: 'container', + input: true, + components: [ + { + label: 'Part Size (MB)', + applyMaskOn: 'change', + mask: false, + tableView: false, + delimiter: false, + requireDecimal: false, + inputFormat: 'plain', + truncateMultipleSpaces: false, + validate: { + min: 5, + max: 5000, + }, + key: 'partSize', + type: 'number', + input: true, + defaultValue: 500, + }, + ], + conditional: { + json: { '===': [{ var: 'data.useMultipartUpload' }, true] }, }, - key: 'partSize', - type: 'number', + }, + { + type: 'textfield', input: true, - defaultValue: 500, - }, - ], - conditional: { - json: { '===': [{ var: 'data.useMultipartUpload' }, true] } + key: 'url', + label: 'Url', + weight: 10, + placeholder: 'Enter the url to post the files to.', + tooltip: + "See https://github.com/danialfarid/ng-file-upload#server-side for how to set up the server.", + conditional: { + json: { '===': [{ var: 'data.storage' }, 'url'] }, + }, }, - }, - { - type: 'textfield', - input: true, - key: 'url', - label: 'Url', - weight: 10, - placeholder: 'Enter the url to post the files to.', - tooltip: "See https://github.com/danialfarid/ng-file-upload#server-side for how to set up the server.", - conditional: { - json: { '===': [{ var: 'data.storage' }, 'url'] } - } - }, - { - type: 'textfield', - input: true, - key: 'options.indexeddb', - label: 'Database', - weight: 10, - placeholder: 'Enter the indexeddb database name', - conditional: { - json: { - in: [ - { - var: 'data.storage' - }, - ['indexeddb'] - ], - } - } - }, - { - type: 'textfield', - input: true, - label: 'Table', - key: 'options.indexeddbTable', - weight: 10, - placeholder: 'Enter the name for indexeddb table', - conditional: { - json: { - in: [ - { - var: 'data.storage' - }, - ['indexeddb'] - ], - } - } - }, - { - type: 'textarea', - key: 'options', - label: 'Custom request options', - tooltip: 'Pass your custom xhr options(optional)', - rows: 5, - editor: 'ace', - input: true, - weight: 15, - placeholder: `{ + { + type: 'textfield', + input: true, + key: 'options.indexeddb', + label: 'Database', + weight: 10, + placeholder: 'Enter the indexeddb database name', + conditional: { + json: { + in: [ + { + var: 'data.storage', + }, + ['indexeddb'], + ], + }, + }, + }, + { + type: 'textfield', + input: true, + label: 'Table', + key: 'options.indexeddbTable', + weight: 10, + placeholder: 'Enter the name for indexeddb table', + conditional: { + json: { + in: [ + { + var: 'data.storage', + }, + ['indexeddb'], + ], + }, + }, + }, + { + type: 'textarea', + key: 'options', + label: 'Custom request options', + tooltip: 'Pass your custom xhr options(optional)', + rows: 5, + editor: 'ace', + input: true, + weight: 15, + placeholder: `{ "withCredentials": true, "headers": { "Authorization": "Basic " } }`, - conditional: { - json: { - '===': [{ - var: 'data.storage' - }, 'url'] - } - } - }, - { - type: 'textfield', - input: true, - key: 'fileKey', - label: 'File form-data key', - weight: 17, - placeholder: 'Enter the key name of a file for form data.', - tooltip: 'Key name that you would like to modify for the file while calling API request.', - conditional: { - json: { - '===': [{ - var: 'data.storage' - }, 'url'] - } - } - }, - { - type: 'textfield', - input: true, - key: 'dir', - label: 'Directory', - placeholder: '(optional) Enter a directory for the files', - tooltip: 'This will place all the files uploaded in this field in the directory', - weight: 20, - conditional: { - json: { - '!==': [{ - var: 'data.storage' - }, 'googledrive'] - } - } - }, - { - type: 'textfield', - input: true, - key: 'dir', - label: 'Folder ID', - placeholder: '(optional) Enter an ID of the folder for the files', - tooltip: 'This will place all the files uploaded in this field in the folder', - weight: 20, - conditional: { - json: { - '===': [{ - var: 'data.storage' - }, 'googledrive'] - } - } - }, - { - type: 'textfield', - input: true, - key: 'fileNameTemplate', - label: 'File Name Template', - placeholder: '(optional) { {name} }-{ {guid} }', - tooltip: 'Specify template for name of uploaded file(s). Regular template variables are available (`data`, `component`, `user`, `value`, `moment` etc.), also `fileName`, `guid` variables are available. `guid` part must be present, if not found in template, will be added at the end.', - weight: 25 - }, - { - type: 'checkbox', - input: true, - key: 'image', - label: 'Display as image(s)', - tooltip: 'Instead of a list of linked files, images will be rendered in the view.', - weight: 30 - }, - { - type: 'checkbox', - input: true, - key: 'uploadOnly', - label: 'Upload Only', - tooltip: 'When this is checked, will only allow you to upload file(s) and consequently the download, in this component, will be unavailable.', - weight: 33, - }, - { - type: 'checkbox', - input: true, - key: 'privateDownload', - label: 'Private Download', - tooltip: 'When this is checked, the file download will send a POST request to the download URL with the x-jwt-token header. This will allow your endpoint to create a Private download system.', - weight: 31, - conditional: { - json: { '===': [{ var: 'data.storage' }, 'url'] } - } - }, - { - type: 'textfield', - input: true, - key: 'imageSize', - label: 'Image Size', - placeholder: '100', - tooltip: 'The image size for previewing images.', - weight: 40, - conditional: { - json: { '==': [{ var: 'data.image' }, true] } - } - }, - { - type: 'checkbox', - input: true, - key: 'webcam', - label: 'Enable web camera', - tooltip: 'This will allow using an attached camera to directly take a picture instead of uploading an existing file.', - weight: 32 - }, - { - type: 'textfield', - input: true, - key: 'webcamSize', - label: 'Webcam Width', - placeholder: '320', - tooltip: 'The webcam size for taking pictures.', - weight: 38, - conditional: { - json: { '==': [{ var: 'data.webcam' }, true] } - } - }, - { - type: 'radio', - input: true, - key: 'capture', - label: 'Enable device capture', - tooltip: 'This will allow a mobile device to open the camera or microphone directly in capture mode.', - optionsLabelPosition: 'right', - inline: true, - defaultValue: false, - values: [ - { - label: 'Disabled', - value: 'false' - }, - { - label: 'Environment (rear camera)', - value: 'environment' - }, - { - label: 'User (front camera)', - value: 'user' - } - ] - }, - { - type: 'datagrid', - input: true, - label: 'File Types', - key: 'fileTypes', - tooltip: 'Specify file types to classify the uploads. This is useful if you allow multiple types of uploads but want to allow the user to specify which type of file each is.', - weight: 11, - components: [ - { - label: 'Label', - key: 'label', + conditional: { + json: { + '===': [ + { + var: 'data.storage', + }, + 'url', + ], + }, + }, + }, + { + type: 'textfield', input: true, - type: 'textfield' - }, - { - label: 'Value', - key: 'value', + key: 'fileKey', + label: 'File form-data key', + weight: 17, + placeholder: 'Enter the key name of a file for form data.', + tooltip: + 'Key name that you would like to modify for the file while calling API request.', + conditional: { + json: { + '===': [ + { + var: 'data.storage', + }, + 'url', + ], + }, + }, + }, + { + type: 'textfield', input: true, - type: 'textfield' - } - ] - }, - { - type: 'textfield', - input: true, - key: 'filePattern', - label: 'File Pattern', - placeholder: '.jpg,video/*,application/pdf', - tooltip: 'See https://github.com/danialfarid/ng-file-upload#full-reference for how to specify file patterns.', - weight: 50 - }, - { - type: 'textfield', - input: true, - key: 'fileMinSize', - label: 'File Minimum Size', - placeholder: '1MB', - tooltip: 'See https://github.com/danialfarid/ng-file-upload#full-reference for how to specify file sizes.', - weight: 60 - }, - { - type: 'textfield', - input: true, - key: 'fileMaxSize', - label: 'File Maximum Size', - placeholder: '10MB', - tooltip: 'See https://github.com/danialfarid/ng-file-upload#full-reference for how to specify file sizes.', - weight: 70 - }, + key: 'dir', + label: 'Directory', + placeholder: '(optional) Enter a directory for the files', + tooltip: + 'This will place all the files uploaded in this field in the directory', + weight: 20, + conditional: { + json: { + '!==': [ + { + var: 'data.storage', + }, + 'googledrive', + ], + }, + }, + }, + { + type: 'textfield', + input: true, + key: 'dir', + label: 'Folder ID', + placeholder: '(optional) Enter an ID of the folder for the files', + tooltip: + 'This will place all the files uploaded in this field in the folder', + weight: 20, + conditional: { + json: { + '===': [ + { + var: 'data.storage', + }, + 'googledrive', + ], + }, + }, + }, + { + type: 'textfield', + input: true, + key: 'fileNameTemplate', + label: 'File Name Template', + placeholder: '(optional) { {name} }-{ {guid} }', + tooltip: + 'Specify template for name of uploaded file(s). Regular template variables are available (`data`, `component`, `user`, `value`, `moment` etc.), also `fileName`, `guid` variables are available. `guid` part must be present, if not found in template, will be added at the end.', + weight: 25, + }, + { + type: 'checkbox', + input: true, + key: 'image', + label: 'Display as image(s)', + tooltip: + 'Instead of a list of linked files, images will be rendered in the view.', + weight: 30, + }, + { + type: 'checkbox', + input: true, + key: 'uploadOnly', + label: 'Upload Only', + tooltip: + 'When this is checked, will only allow you to upload file(s) and consequently the download, in this component, will be unavailable.', + weight: 33, + }, + { + type: 'checkbox', + input: true, + key: 'privateDownload', + label: 'Private Download', + tooltip: + 'When this is checked, the file download will send a POST request to the download URL with the x-jwt-token header. This will allow your endpoint to create a Private download system.', + weight: 31, + conditional: { + json: { '===': [{ var: 'data.storage' }, 'url'] }, + }, + }, + { + type: 'textfield', + input: true, + key: 'imageSize', + label: 'Image Size', + placeholder: '100', + tooltip: 'The image size for previewing images.', + weight: 40, + conditional: { + json: { '==': [{ var: 'data.image' }, true] }, + }, + }, + { + type: 'checkbox', + input: true, + key: 'webcam', + label: 'Enable web camera', + tooltip: + 'This will allow using an attached camera to directly take a picture instead of uploading an existing file.', + weight: 32, + }, + { + type: 'textfield', + input: true, + key: 'webcamSize', + label: 'Webcam Width', + placeholder: '320', + tooltip: 'The webcam size for taking pictures.', + weight: 38, + conditional: { + json: { '==': [{ var: 'data.webcam' }, true] }, + }, + }, + { + type: 'radio', + input: true, + key: 'capture', + label: 'Enable device capture', + tooltip: + 'This will allow a mobile device to open the camera or microphone directly in capture mode.', + optionsLabelPosition: 'right', + inline: true, + defaultValue: false, + values: [ + { + label: 'Disabled', + value: 'false', + }, + { + label: 'Environment (rear camera)', + value: 'environment', + }, + { + label: 'User (front camera)', + value: 'user', + }, + ], + }, + { + type: 'datagrid', + input: true, + label: 'File Types', + key: 'fileTypes', + tooltip: + 'Specify file types to classify the uploads. This is useful if you allow multiple types of uploads but want to allow the user to specify which type of file each is.', + weight: 11, + components: [ + { + label: 'Label', + key: 'label', + input: true, + type: 'textfield', + }, + { + label: 'Value', + key: 'value', + input: true, + type: 'textfield', + }, + ], + }, + { + type: 'textfield', + input: true, + key: 'filePattern', + label: 'File Pattern', + placeholder: '.jpg,video/*,application/pdf', + tooltip: + "See https://github.com/danialfarid/ng-file-upload#full-reference for how to specify file patterns.", + weight: 50, + }, + { + type: 'textfield', + input: true, + key: 'fileMinSize', + label: 'File Minimum Size', + placeholder: '1MB', + tooltip: + "See https://github.com/danialfarid/ng-file-upload#full-reference for how to specify file sizes.", + weight: 60, + }, + { + type: 'textfield', + input: true, + key: 'fileMaxSize', + label: 'File Maximum Size', + placeholder: '10MB', + tooltip: + "See https://github.com/danialfarid/ng-file-upload#full-reference for how to specify file sizes.", + weight: 70, + }, ]; diff --git a/src/components/file/editForm/File.edit.validation.js b/src/components/file/editForm/File.edit.validation.js index ed5bc10a65..0aed28bdfc 100644 --- a/src/components/file/editForm/File.edit.validation.js +++ b/src/components/file/editForm/File.edit.validation.js @@ -1,10 +1,10 @@ export default [ - { - key: 'unique', - ignore: true - }, - { - key: 'validateOn', - ignore: true - }, + { + key: 'unique', + ignore: true, + }, + { + key: 'validateOn', + ignore: true, + }, ]; diff --git a/src/components/file/fixtures/comp1.js b/src/components/file/fixtures/comp1.js index 8a5a609cbf..0cc2ac519e 100644 --- a/src/components/file/fixtures/comp1.js +++ b/src/components/file/fixtures/comp1.js @@ -1,16 +1,16 @@ export default { - 'label': 'Upload', - 'tableView': false, - 'storage': 'base64', - 'webcam': false, - 'fileTypes': [ - { - 'label': '', - 'value': '' - } - ], - 'calculateServer': false, - 'key': 'upload', - 'type': 'file', - 'input': true + label: 'Upload', + tableView: false, + storage: 'base64', + webcam: false, + fileTypes: [ + { + label: '', + value: '', + }, + ], + calculateServer: false, + key: 'upload', + type: 'file', + input: true, }; diff --git a/src/components/file/fixtures/comp2.js b/src/components/file/fixtures/comp2.js index eef40e54e0..d96b69cd71 100644 --- a/src/components/file/fixtures/comp2.js +++ b/src/components/file/fixtures/comp2.js @@ -1,29 +1,29 @@ export default { - _id: '6108f1dac7e55d5cabe2627f', - type: 'form', - components: [ - { - label: 'Upload', - tableView: false, - modalEdit: true, - storage: 'base64', - webcam: false, - fileTypes: [{ label: '', value: '' }], - key: 'file', - type: 'file', - input: true, - }, - { - type: 'button', - label: 'Submit', - key: 'submit', - disableOnInvalid: true, - input: true, - tableView: false, - }, - ], - title: 'testFileForm', - display: 'form', - name: 'testFileForm', - path: 'testfileform', + _id: '6108f1dac7e55d5cabe2627f', + type: 'form', + components: [ + { + label: 'Upload', + tableView: false, + modalEdit: true, + storage: 'base64', + webcam: false, + fileTypes: [{ label: '', value: '' }], + key: 'file', + type: 'file', + input: true, + }, + { + type: 'button', + label: 'Submit', + key: 'submit', + disableOnInvalid: true, + input: true, + tableView: false, + }, + ], + title: 'testFileForm', + display: 'form', + name: 'testFileForm', + path: 'testfileform', }; diff --git a/src/components/file/fixtures/comp3.js b/src/components/file/fixtures/comp3.js index 3e6cf953c1..a14c751cd1 100644 --- a/src/components/file/fixtures/comp3.js +++ b/src/components/file/fixtures/comp3.js @@ -1,29 +1,29 @@ export default { - type: 'form', - display: 'form', - components: [ - { - label: 'Upload', - tableView: false, - storage: 'base64', - webcam: false, - fileTypes: [ + type: 'form', + display: 'form', + components: [ { - label: '', - value: '' - } - ], - key: 'file', - type: 'file', - input: true - }, - { - type: 'button', - label: 'Submit', - key: 'submit', - disableOnInvalid: true, - input: true, - tableView: false - } - ], + label: 'Upload', + tableView: false, + storage: 'base64', + webcam: false, + fileTypes: [ + { + label: '', + value: '', + }, + ], + key: 'file', + type: 'file', + input: true, + }, + { + type: 'button', + label: 'Submit', + key: 'submit', + disableOnInvalid: true, + input: true, + tableView: false, + }, + ], }; diff --git a/src/components/file/fixtures/values.js b/src/components/file/fixtures/values.js index 9afbfcf400..0e1fd761cf 100644 --- a/src/components/file/fixtures/values.js +++ b/src/components/file/fixtures/values.js @@ -1,14 +1,13 @@ export default [ - [ - { - imageSrc: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4w', - name: 'epaule-3aa10b33-78f8-4b5f-9cba-7dacff042e3c.svg', - originalName: 'Epaule.svg', - size: 41978, - storage: 'base64', - type: 'image/svg+xml', - url: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCA0MjggODMxIiB3aWR0aD0iNDI4LjBwdCIgaGVpZ2h0PSI4MzEuMHB0Ij4KPHBhdGggZD0iTSAwLjAwIDAuMDAgTCA0MjguMDAgMC4wMCBMIDQyOC4wMCA4MzEuMDAgTCAwLjAwIDgzMS4wMCBMIDAuMDAgMC4wMCBaIiBmaWxsPSIjZmZmZmZmIiAvPgo8cGF0aCBkPSJNIDEyNC4xMSAxMy44MyBDIDEyOC4wMCAxNy4xMSAxMjkuODUgMjEuNTUgMTMwLjc2IDI2LjQ1IEMgMTMyLjE5IDI5LjA5IDEzMy44NSAzMC42NiAxMzMuNTAgMzQuMDAgQyAxMzIuODggNDEuNDAgMTI3LjE1IDQ4LjI5IDEyNC4wOCA1NS4wMCBDIDEyMy45MyA1OS4wNSAxMjMuNjcgNjIuNzcgMTI1LjUxIDY2LjUyIEMgMTI3LjEzIDcwLjQ1IDEzMS4yMSA3Mi42MyAxMzUuMTIgNzMuNjcgQyAxNDIuNzEgNzYuMDMgMTUwLjA5IDc3LjQ0IDE1Ni4zNCA4Mi42NiBDIDE2My41NSA4OC43MyAxNjYuMTYgOTcuODggMTY2LjQ2IDEwNy4wMCBDIDE2Ni43NyAxMTcuMzIgMTY2LjQ2IDEyNy42OSAxNjYuNjAgMTM4LjAyIEMgMTY2LjgyIDE0MC45NyAxNjcuMDQgMTQzLjc2IDE2OC42NSAxNDYuMzQgQyAxNzQuNjIgMTU2Ljk1IDE4MC41OSAxNjguNjQgMTgxLjQ0IDE4MC45OSBDIDE4Mi4xNCAxODUuNzMgMTgxLjY4IDE5MC4zNSAxODMuMjAgMTk0Ljk2IEMgMTg0LjMwIDE5OS42MCAxODcuNDIgMjAyLjMwIDE5MC4xNSAyMDUuOTcgQyAxOTQuNjIgMjExLjcxIDIwMC4xMyAyMTYuODUgMjAzLjkwIDIyMy4wNyBDIDIwNC4xOCAyMjQuOTEgMjA0Ljc4IDIyOC4xMCAyMDIuMDAgMjI4LjEyIEMgMTk5LjA4IDIyNy4yMiAxOTcuNDkgMjI1LjEzIDE5NS4yNiAyMjMuMjIgQyAxOTQuODQgMjI1LjU5IDE5NS42MiAyMjcuNjEgMTk1LjgxIDIyOS45OCBDIDE5Ni4yMCAyMzMuMjcgMTk1LjcwIDIzNi42MyAxOTQuNzEgMjM5Ljc3IEMgMTkzLjU1IDI0Mi45MyAxOTAuNjUgMjQ2LjIzIDE4Ny4wMSAyNDYuMDYgQyAxODIuMjggMjQ2LjU1IDE3OC4zOCAyNDIuMTAgMTc2Ljc5IDIzOC4xMSBDIDE3My45OCAyMzAuODQgMTc0LjY4IDIyMi4xNSAxNzMuNjkgMjE0LjQ5IEMgMTcyLjYwIDIwOC42MiAxNjkuNDkgMjAzLjE0IDE2Ny4xOSAxOTcuNjAgQyAxNjIuMzUgMTg3LjA2IDE1Ny43NCAxNzYuMzMgMTUzLjYzIDE2NS40OCBDIDE1MC4wOSAxNTUuMDcgMTQ5LjI1IDE0My44OSAxNDguNTIgMTMyLjk5IEMgMTQ4LjUyIDEzMS42OCAxNDguMTIgMTMxLjAxIDE0Ny4zMCAxMzAuOTcgQyAxNDYuMDEgMTM4LjM4IDE0My4zNiAxNDUuNTYgMTQyLjIwIDE1My4wMCBDIDE0MC42MCAxNjguNjMgMTQ2LjM4IDE4Mi44NCAxNDguMjkgMTk4Ljk5IEMgMTQ5LjY4IDIwNi45NCAxNTAuMzggMjE0Ljk0IDE1MC45MCAyMjMuMDAgQyAxNTEuMTYgMjM1Ljg1IDE0OC41MSAyNDkuMTkgMTQ1LjU5IDI2MS42OCBDIDE0NC4wMSAyNjcuNjYgMTQyLjA2IDI3NC4zMiAxMzguMzcgMjc5LjM3IEMgMTM2LjQxIDI4MS40OSAxMzcuNzYgMjg1LjM5IDEzNy44NSAyODguMDQgQyAxMzguMDQgMjk0Ljc1IDEzNS43NiAzMDIuMzEgMTM3LjkzIDMwOS4wMSBDIDEzOS45MSAzMTUuMzkgMTQyLjUxIDMyMS4xNiAxNDIuNTkgMzI4LjAwIEMgMTQzLjEzIDM0MS40MSAxMzUuMzQgMzU0LjQyIDEzMS4zOCAzNjcuMDkgQyAxMzAuMjYgMzcyLjIyIDEyOS42NCAzNzcuNTIgMTMxLjQ0IDM4Mi41OCBDIDEzMy4zMSAzODYuNTAgMTM3LjM3IDM4OC42NSAxMzkuNDQgMzkyLjU2IEMgMTQwLjIwIDM5NC4xMyAxNDIuMDYgMzk2LjU5IDE0MC4wMCAzOTcuOTMgQyAxMzUuNzEgNDAwLjY1IDEyOS44NSAzOTkuOTggMTI0Ljk5IDM5OS45OCBDIDEyMi41OCAzOTkuNjAgMTE5LjM2IDM5OS4yMCAxMTcuNTIgMzk3LjQ4IEMgMTE0Ljk2IDM5NC4zNyAxMTMuNjUgMzkwLjAxIDExMy45NyAzODYuMDAgQyAxMTMuMzEgMzc2LjUwIDExOC4xMiAzNjcuODcgMTE3LjIwIDM1OC4wMCBDIDExNy4wMyAzNTEuMjQgMTE1LjEzIDM0NC43NCAxMTQuODUgMzM4LjAwIEMgMTEzLjY5IDMyNy44NiAxMTcuODkgMzE3LjQ5IDExNy4zMyAzMDcuMDAgQyAxMTYuOTggMzAyLjk4IDExNi45MCAyOTguOTggMTE2LjIwIDI5NS4wMCBDIDExNC45MSAyODEuMjkgMTE1LjIyIDI2Ny42NCAxMTQuMTIgMjUzLjk5IEMgMTEzLjgyIDI0My44NiAxMTIuODcgMjMzLjgwIDExMi41MCAyMjMuNjMgQyAxMTEuOTIgMjM1LjM2IDExMS4xMiAyNDcuMzAgMTEwLjEwIDI1OS4wMCBDIDEwOS43OCAyNzAuMjEgMTA5LjE3IDI4MS43OSAxMDkuMTYgMjkzLjAwIEMgMTA4LjI0IDI5Mi45MyAxMDcuNDcgMjkyLjQ1IDEwNi44NSAyOTEuNTQgQyAxMDcuOTIgMjgzLjgyIDEwNy45NCAyNzUuNzkgMTA3Ljk3IDI2OC4wMCBDIDEwOC40MyAyNTAuNzkgMTEwLjQ0IDIzMy4zNSAxMTAuODkgMjE2LjI1IEMgMTA5LjAxIDIxNS4xOCAxMDcuMjAgMjE0LjIxIDEwNS4wNiAyMTMuNzggQyAxMDQuMjkgMjEzLjcyIDEwMy45MCAyMTMuMTAgMTAzLjg4IDIxMS45MCBDIDEwNi45OCAyMTIuMTcgMTA5LjkxIDIxMy45OCAxMTIuOTIgMjE0Ljk2IEMgMTEzLjU2IDIxNC4zOSAxMTQuMjQgMjE0LjA2IDExNC45NiAyMTMuOTkgQyAxMTcuMzMgMjEzLjk3IDExOS4zMSAyMTIuNTcgMTIxLjQxIDIxMS40NiBDIDEyMS43OSAyMTAuNDMgMTIyLjM2IDIwOS45MCAxMjMuMTEgMjA5Ljg3IEMgMTIzLjg0IDIxMy43NCAxMTYuOTIgMjE1LjU4IDExNC4xNCAyMTYuMzMgQyAxMTMuNjIgMjIzLjI5IDExNC40MCAyMzAuMDggMTE0LjkzIDIzNy4wMCBDIDExNS4wMyAyNDEuNjkgMTE1LjU3IDI0Ni4zMyAxMTUuOTggMjUxLjAwIEMgMTE1Ljk3IDI1Ny4wMyAxMTYuNzEgMjYzLjAwIDExNi45MiAyNjkuMDIgQyAxMTcuMTggMjgyLjM1IDExOC4zNSAyOTUuNzIgMTE5LjIyIDMwOS4wMCBDIDExOS42OSAzMTYuNjYgMTE2Ljg5IDMyMy40NCAxMTYuNjcgMzMxLjAwIEMgMTE2LjQyIDMzOS4yMiAxMTcuNjUgMzQ2Ljk1IDExOC45MiAzNTUuMDAgQyAxMTkuNzYgMzYyLjkwIDExNy4xNCAzNzEuMTUgMTE2LjA2IDM3OS4wMCBDIDExNi4xMSAzODQuOTEgMTE1LjA1IDM5MC4yNCAxMTguNjQgMzk1LjM4IEMgMTIyLjI0IDM5OS4zNSAxMjguMjMgMzk4LjMzIDEzMy4wMSAzOTguMDAgQyAxMzQuODAgMzk4LjA3IDEzNy41NiAzOTYuODcgMTM3Ljk5IDM5NC45OSBDIDEzNi40MiAzOTAuMzkgMTMxLjQ4IDM4Ny41NiAxMjkuOTUgMzgyLjk5IEMgMTI4LjUzIDM3OS4zMSAxMjguOTkgMzc0Ljk2IDEyOS4wOCAzNzEuMDAgQyAxMzAuOTQgMzU3Ljc2IDEzOC41MSAzNDYuNjQgMTQwLjI5IDMzMy4wMSBDIDE0MS40NCAzMjYuMjkgMTM5LjYyIDMxOS45NSAxMzcuMzcgMzEzLjY5IEMgMTM1LjUwIDMwOC41NyAxMzUuNjAgMzAzLjM2IDEzNS43OCAyOTguMDAgQyAxMzYuMjMgMjkyLjAyIDEzNS44NCAyODUuOTggMTM2LjAzIDI4MC4wMSBDIDEzNi40OSAyNzguNTggMTM3LjMyIDI3Ny4yNyAxMzguMTEgMjc2LjAwIEMgMTQxLjA2IDI3MS41OSAxNDIuNDUgMjY2LjAyIDE0NC4wNSAyNjEuMDAgQyAxNTEuNjAgMjMzLjY1IDE0OC41NSAyMDUuMjggMTQyLjM3IDE3OC4wMCBDIDE0MC43NiAxNjguOTUgMTM4LjMzIDE1OS4wMSAxNDAuODEgMTQ5Ljk2IEMgMTQzLjI4IDEzOS42MyAxNDYuMzcgMTI5LjU1IDE0Ny44OCAxMTkuMDAgQyAxNDcuNzcgMTE3Ljg5IDE0OC40NSAxMTcuNTQgMTQ5LjkzIDExNy45NSBDIDE0OS4yMiAxMjYuNTMgMTUwLjIzIDEzNS4zNyAxNTEuMjIgMTQzLjkxIEMgMTUyLjM4IDE1MC40MiAxNTMuMDkgMTU2LjY0IDE1NC45MCAxNjMuMDAgQyAxNTcuNDggMTcyLjA4IDE2MS45NyAxODAuODUgMTY1LjY0IDE4OS40NiBDIDE2OS45OCAxOTkuMTkgMTc1LjE2IDIwOC4xMyAxNzYuMDIgMjE5LjAwIEMgMTc1LjgzIDIyNC41NSAxNzYuMzIgMjMwLjA1IDE3Ny40OCAyMzUuNDggQyAxNzguMDggMjM3LjI0IDE3OS4wNCAyNDAuNTggMTgxLjA1IDI0MS4xMyBDIDE4MS42MCAyMzYuOTYgMTc5Ljg1IDIzMi4zMCAxODAuMjIgMjI4LjM3IEwgMTgwLjc5IDIyOC4xMyBDIDE4MS4yMyAyMzIuNzYgMTgxLjg0IDIzNy4zNCAxODEuOTIgMjQyLjAwIEMgMTgyLjE0IDI0My4yOSAxODMuODAgMjQ0LjIwIDE4NS4wNSAyNDMuOTcgQyAxODYuMzAgMjM5LjMxIDE4Mi41NiAyMzMuMTcgMTg0LjY4IDIyOS4yMCBDIDE4NC45OCAyMzQuMTUgMTg1Ljc5IDIzOS4wNCAxODUuODAgMjQ0LjAxIEMgMTg1Ljk2IDI0NC44NyAxODYuNjcgMjQ1LjE2IDE4Ny45NCAyNDQuOTAgQyAxODkuNTIgMjQwLjI2IDE4OC4wNyAyMzUuOTkgMTg4LjE2IDIzMS4yNiBMIDE4OC43NyAyMzEuMjkgQyAxODguNzggMjMyLjg0IDE4OC44MiAyMzQuMzYgMTg5LjE0IDIzNS44OCBDIDE4OS43MiAyMzguNDYgMTg4LjkzIDI0MS4wNSAxODkuNzQgMjQzLjUyIEMgMTkxLjc5IDI0My4wNyAxOTIuNDUgMjQwLjc3IDE5My4wMSAyMzkuMDIgQyAxOTQuNzUgMjMzLjU0IDE5My42MiAyMjcuNzAgMTkyLjY0IDIyMi4xMyBDIDE5Mi41NCAyMjAuOTEgMTkwLjk4IDIxOC43NyAxOTMuMDQgMjE4LjYxIEMgMTk1LjY1IDIyMC4xNCAxOTcuNzQgMjI0LjE0IDIwMC45NSAyMjUuMDcgQyAyMDMuMDQgMjI1LjA4IDIwMS41NCAyMjIuODQgMjAwLjg4IDIyMi4wNCBDIDE5Ni42MCAyMTYuMzggMTkxLjgyIDIxMS4yNSAxODcuMDkgMjA1Ljk4IEMgMTgwLjQ2IDE5OC45MiAxODAuNjcgMTg4LjAxIDE3OS40MyAxNzguOTkgQyAxNzguNjAgMTY5Ljg3IDE3NC44NiAxNjIuMjQgMTcwLjcwIDE1NC4yNSBDIDE2OC44MCAxNTAuNTAgMTY2LjcxIDE0Ny4xOSAxNjUuODEgMTQzLjAxIEMgMTYyLjI2IDEyOC4wMCAxNjYuNzkgMTEzLjc0IDE2My42OCA5OS4xMiBDIDE2Mi4xOCA5Mi44MCAxNTkuNDAgODYuOTggMTUzLjk4IDgzLjE0IEMgMTQ2LjQ1IDc3Ljk1IDEzNi43NSA3Ny4xNSAxMjguOTMgNzMuMjEgQyAxMjMuNDggNjkuODcgMTIxLjQ1IDY0LjExIDEyMS45MCA1Ny45MSBDIDExOS43NSA1OC42NCAxMTguMzAgNjAuMjQgMTE1LjkzIDYwLjQ5IEMgMTExLjEzIDYxLjMyIDEwNy40MiA1OS40NyAxMDMuMTcgNTcuNzEgQyAxMDIuNzQgNjEuMDggMTAyLjQ3IDY0LjY1IDEwMC41OSA2Ny41OSBDIDk3LjkzIDcyLjIyIDkyLjg2IDc0LjQzIDg4LjAzIDc2LjA4IEMgODEuNDIgNzguMTAgNzMuMTkgODAuMTIgNjguMzAgODUuMjkgQyA2Mi45MSA5MC4yNiA2MC42NiA5Ny45MCA2MC4wNSAxMDQuOTkgQyA1OS44OCAxMTQuNjkgNjAuMTggMTI0LjQwIDU5LjkzIDEzNC4wOSBDIDU5LjQ5IDEzOC42NyA1OS40MiAxNDMuOTQgNTcuNDIgMTQ4LjE0IEMgNTMuNTQgMTU1LjY2IDQ5LjAzIDE2My4zOCA0Ni41OSAxNzEuNTEgQyA0NC42NSAxNzkuOTEgNDUuMTEgMTg5LjkzIDQxLjg0IDE5Ny45NCBDIDM5LjgzIDIwMy41OCAzNC44NSAyMDguNDAgMzEuMTIgMjEzLjAyIEMgMjguMTIgMjE3LjA0IDI0LjY0IDIxOS45MiAyMy4xNCAyMjQuOTIgQyAyNi45NSAyMjUuMjQgMzAuNTMgMjE5LjY5IDMzLjczIDIxOC4yNiBMIDM0LjEyIDIxOS45NyBDIDMyLjUzIDIyNS4wOSAzMS4yMCAyMzAuNjIgMzEuMzYgMjM2LjAxIEMgMzEuNTUgMjM3LjAxIDMxLjU3IDI0MC4yNSAzMy4wNCAyNDAuMDYgQyAzMy4yNiAyMzYuNTEgMzMuMTYgMjMyLjk4IDMzLjY0IDIyOS40NCBDIDMzLjk0IDIyOS4wOCAzNC4yNCAyMjguNzEgMzQuNTQgMjI4LjM0IEMgMzQuMTUgMjMyLjYxIDMzLjQwIDIzNi42NyAzMy44NCAyNDEuMDAgQyAzNC43MCAyNDIuNTQgMzcuMzEgMjQyLjQ4IDM3LjQzIDI0MC4zMCBDIDM3LjkzIDI0MC42OCAzOC40MiAyNDEuMDUgMzguOTIgMjQxLjQzIEMgNDEuOTMgMjQwLjQzIDQxLjAyIDIzNS40MiA0MS4yMCAyMzMuMDAgQyA0MS40NyAyMzAuOTAgNDAuODkgMjI3LjY3IDQyLjY1IDIyNi4xOSBDIDQyLjA1IDIzMC40OSA0MS4zNyAyMzQuNTcgNDEuOTkgMjM4Ljk0IEwgNDIuNjcgMjM4LjYwIEMgNDYuMDAgMjMzLjU3IDQ1LjAyIDIyNS41NyA0Ni43MyAyMTkuODkgQyA0OS43NiAyMDguNjcgNTUuMDEgMTk5LjA4IDU5LjQ5IDE4OC40NyBDIDYzLjA4IDE4MC42NCA2Ni45NyAxNzIuNzEgNjkuNDkgMTY0LjQ2IEMgNzAuOTYgMTU5LjgyIDcxLjQ4IDE1NS4xMCA3Mi40NyAxNTAuMzUgQyA3NC4xMyAxMzkuOTMgNzUuNDEgMTI5LjU0IDc0Ljk3IDExOC45NiBDIDc0LjQ5IDExNy42OSA3Ni42MyAxMTYuNjkgNzYuODMgMTE4LjA1IEMgNzguMTggMTMyLjEyIDg0LjUyIDE0NS45NSA4NC45OCAxNjAuMDAgQyA4My4wMyAxODQuOTAgNzMuOTEgMjA3LjUyIDc2LjM2IDIzMi45OSBDIDc3LjI0IDI0My44MyA3OS4xOSAyNTQuNTEgODEuNzEgMjY1LjA4IEMgODMuMDAgMjY5LjMzIDg0LjE3IDI3My40MiA4Ni45NSAyNzYuOTYgQyA4Ny44MCAyNzguMTkgODkuMDggMjc5LjQxIDg5LjE3IDI4MC45NiBDIDg5Ljc0IDI4Ni4zNyA4OC4xMSAyOTEuNTkgODguOTcgMjk3LjAwIEMgODguOTggMzAxLjgyIDg5Ljg5IDMwNy40OCA4Ny45MCAzMTEuOTUgQyA4NS4yOCAzMTguOTcgODMuMDQgMzI2LjM0IDg0LjI5IDMzMy44OSBDIDg2LjU0IDM0NC4wNyA5MC41NiAzNTQuMDIgOTQuNDAgMzYzLjcwIEMgOTYuMjYgMzY4Ljk1IDk2LjIwIDM3NS4wNiA5NS45MSAzODAuNTcgQyA5NS4yMiAzODYuNzEgODguMzAgMzg5LjQyIDg2LjY5IDM5NC45OSBDIDg2LjQ5IDM5Ny43MCA5MC4wNCAzOTguMDcgOTIuMDAgMzk3Ljk4IEMgOTYuNzggMzk4LjAxIDEwMi44OCAzOTkuNzMgMTA2LjI5IDM5NS4yNyBDIDEwOC40NiAzOTAuOTggMTA5Ljg2IDM4NS44MiAxMDkuMTAgMzgxLjAwIEMgMTA4LjQ0IDM3NC45MyAxMDUuNzggMzY5LjI0IDEwNS43NiAzNjMuMDAgQyAxMDUuMDUgMzUyLjM5IDEwOC43OCAzNDIuNTkgMTA4LjA5IDMzMi4wMCBDIDEwNy44NSAzMjQuOTQgMTA1Ljk4IDMxOC4wMyAxMDUuNzMgMzExLjAxIEMgMTA1LjAxIDMwNS4xMSAxMDYuNjggMjk5LjcwIDEwNi44NiAyOTMuOTYgQyAxMDcuNzcgMjk0LjAzIDEwOC41MSAyOTQuNTMgMTA5LjA4IDI5NS40NiBDIDEwOC4xNiAyOTguOTMgMTA4LjI0IDMwMi40OSAxMDcuNTQgMzA2LjAxIEMgMTA2LjQ4IDMxMi41OSAxMDguMzEgMzE4LjQ1IDEwOS4yNSAzMjQuOTAgQyAxMTEuNTIgMzM1LjY2IDEwOC4xNCAzNDguMTkgMTA3LjAyIDM1OS4wMCBDIDEwNy42MyAzNjYuMDQgMTA4LjgxIDM3My4yNCAxMTAuNjkgMzgwLjA3IEMgMTExLjk0IDM4Ni4xOSAxMTAuMjkgMzkyLjAyIDEwNy4zNSAzOTcuMzkgQyAxMDUuNTQgNDAwLjExIDEwMS45MiA0MDAuMTEgOTkuMDAgNDAwLjAyIEMgOTUuMDYgMzk5LjgyIDkwLjU5IDQwMC41NiA4Ni44OCAzOTkuMjUgQyA4NS4zMCAzOTguNjUgODMuNzQgMzk3Ljk4IDgzLjkxIDM5NS45NyBDIDg0Ljc1IDM5MC4yMCA5MS4wNSAzODcuOTAgOTMuMTUgMzgyLjA4IEMgOTYuMDggMzc0LjQyIDkzLjA0IDM2NS4xOSA5MC4xMCAzNTcuOTQgQyA4Ny4xNSAzNDkuNTcgODMuMTkgMzQwLjc5IDgyLjA3IDMzMS45OSBDIDgxLjY1IDMyOC40OCA4Mi4yNiAzMjUuMTQgODMuMDMgMzIxLjc0IEMgODIuNTkgMzIxLjMyIDgyLjE1IDMyMC45MSA4MS43MSAzMjAuNTAgQyA4Mi4zMSAzMjAuMjUgODIuOTAgMzIwLjAxIDgzLjQ5IDMxOS43NyBDIDg0LjQ5IDMxNC42MiA4Ny4wMSAzMTAuNDkgODcuMDAgMzA1LjAwIEMgODYuOTUgMjk3LjY3IDg3LjA0IDI5MC4zMyA4Ni45NSAyODMuMDAgQyA4Ni45NiAyNzkuODMgODQuNzUgMjc3LjM0IDgzLjQ2IDI3NC41NCBDIDc3Ljk3IDI2My41MSA3Ni43NyAyNTEuMDIgNzUuMDcgMjM5LjAwIEMgNzQuODAgMjMyLjM4IDczLjY4IDIyNS41NSA3NC4zOSAyMTkuMDAgQyA3NC45OCAyMDEuNzEgNzkuNTcgMTg2LjAxIDgyLjIxIDE2OS4wMSBDIDgzLjM2IDE2NC4xNyA4My4wMiAxNTguOTUgODIuOTAgMTU0LjAwIEMgODEuMDQgMTQ1LjE1IDc4LjM4IDEzNi40OCA3Ni40OCAxMjcuNjUgQyA3NS43NyAxMzkuMzMgNzQuNzkgMTUwLjY1IDcxLjcwIDE2Mi4wMCBDIDY2Ljc5IDE3OS40NyA1OC4wNyAxOTQuMDIgNTEuNTUgMjEwLjU1IEMgNDguMTMgMjE5LjEzIDQ3LjA1IDIyNy44OCA0NS44OCAyMzYuOTYgQyA0NC42MyAyNDIuMzcgMzguOTkgMjQ0LjA1IDM0LjA4IDI0My42MSBDIDMxLjQ0IDI0My4wNyAzMC4wMCAyNDAuNjMgMjkuNzQgMjM4LjEwIEMgMjguOTkgMjMyLjgyIDMwLjM3IDIyOC4xMCAzMC45OCAyMjIuOTcgQyAyOC4xNSAyMjQuNjggMjYuMjMgMjI2LjkyIDIyLjk5IDIyNy45OSBDIDIxLjA1IDIyNy42NyAxOS4yNSAyMjUuMzkgMjAuNTIgMjIzLjQ4IEMgMjIuMTcgMjIwLjE5IDI1LjE2IDIxNy4zNCAyNy40OSAyMTQuNTAgQyAzMC44NyAyMTAuMjYgMzQuNTMgMjA2LjIwIDM3Ljc4IDIwMS44NiBDIDQyLjk0IDE5NC41NiA0Mi40NCAxODQuOTIgNDMuNzEgMTc2LjUxIEMgNDUuMDQgMTY1LjE3IDUyLjc5IDE1Ni4wNCA1Ni4xNSAxNDUuMDQgQyA1OC4zMSAxMzguNzYgNTguMDQgMTMyLjU0IDU3Ljk1IDEyNi4wMCBDIDU3LjUzIDExOS4zNiA1Ny42OSAxMTIuNjQgNTcuOTggMTA2LjAwIEMgNTguNjkgOTguMTggNjAuODkgOTAuMjggNjYuNTggODQuNTggQyA2OC43MSA4Mi4yNiA3MS4zNCA4MC44NCA3My44MiA3OC45MiBDIDc2LjAyIDc4LjA0IDc4LjI5IDc3LjMwIDgwLjUxIDc2LjQ3IEMgODUuOTQgNzQuNjggOTIuNTAgNzMuNzkgOTcuMDAgNzAuMDEgQyAxMDAuODYgNjYuNTMgMTAxLjI1IDYwLjg2IDEwMC45NCA1Ni4wMSBDIDk5LjY0IDU0LjAyIDk3LjU0IDUyLjc0IDk2LjUxIDUwLjQ2IEMgOTMuODIgNDUuMjEgODkuMTUgNDAuMjggODkuNzcgMzMuOTkgQyA4OS44NCAzMC4xNiA5Mi43NSAyOC4yMSA5My40OSAyNC40NiBDIDk0LjUyIDE5LjgxIDk2LjIwIDE1LjA3IDEwMC4yNSAxMi4yMSBDIDEwNy40MSA3LjA5IDExNy41OCA4LjIzIDEyNC4xMSAxMy44MyBaIiBmaWxsPSIjODE4MTgxIiAvPgo8cGF0aCBkPSJNIDMwNC45OCA5LjYyIEMgMzEwLjIyIDkuNjAgMzE1LjUyIDEwLjEyIDMyMC4xOCAxMi43MiBDIDMyNC45OSAxNS41OSAzMjcuMjMgMjEuNjEgMzI3LjExIDI3LjAwIEMgMzI3LjE5IDMyLjU5IDMyOC40MCAzOS40MiAzMjYuNzEgNDQuNzcgQyAzMjQuODUgNDguNTkgMzIyLjUyIDUxLjQ5IDMyMi4xMyA1NS45OCBDIDMyMi4zNyA1OC41NiAzMjEuMzggNjAuNzAgMzIyLjg3IDYzLjA0IEMgMzI0LjQ4IDY2LjQ3IDMyNy41MCA2Ny45NyAzMzAuODcgNjkuMjIgQyAzNDAuMDMgNzIuOTMgMzQ5LjM5IDc2LjQ0IDM1Ni4xNyA4My45NSBDIDM2Mi4yOCA5MC44NiAzNjMuNTYgMTAwLjEyIDM2Mi41NSAxMDkuMDEgQyAzNjEuOTEgMTE1LjAzIDM2MS43NiAxMjAuOTkgMzYyLjU2IDEyNy4wMSBDIDM2My44NiAxNDQuMjMgMzcyLjMwIDE1OS42NCAzNzcuMTggMTc1Ljk0IEMgMzc4LjY4IDE4MC42MyAzNzkuMjIgMTg1LjAwIDM4MS42NyAxODkuMzIgQyAzODYuMzEgMTk3LjY1IDM5My41MCAyMDMuODEgMzk4LjU1IDIxMS41MCBDIDM5OS41MCAyMTMuMDkgNDAwLjU4IDIxNS45NyAzOTguMTAgMjE2Ljc1IEMgMzk1Ljc0IDIxNy4yNSAzOTIuOTIgMjE1LjcyIDM5MC43MyAyMTQuOTAgQyAzOTAuNjggMjIxLjAxIDM5Mi42NiAyMjYuNzIgMzkwLjk3IDIzMi45MCBDIDM4OC41MiAyMzYuMTkgMzgzLjc3IDIzNi40NCAzODAuMDcgMjM1Ljc2IEMgMzc0LjA5IDIzMy44MCAzNzAuOTcgMjI2LjczIDM2OS45MiAyMjEuMDAgQyAzNjkuMTQgMjE0LjY5IDM3MC40OCAyMDYuMjEgMzY3LjY5IDIwMC4yOCBDIDM2My4zNCAxOTMuNTkgMzU4Ljk1IDE4Ni45NCAzNTQuOTcgMTgwLjAyIEMgMzQ5Ljc2IDE3MS4wMiAzNDUuMzkgMTYxLjY3IDM0NC42MSAxNTEuMTUgQyAzNDMuNjggMTQzLjcyIDM0Mi4yNSAxMzYuMDQgMzQyLjU1IDEyOC41NCBDIDM0Mi4yNCAxMjkuMzMgMzQxLjU4IDEyOS43NiAzNDAuNTcgMTI5LjgyIEMgMzQwLjY3IDEyNS4yMCAzNDIuNzcgMTIwLjcwIDM0Mi45NCAxMTUuOTkgQyAzNDMuMDYgMTE0Ljk1IDM0My4yMyAxMTEuNzUgMzQ0LjkxIDExMi4xMiBDIDM0NS4yNSAxMTIuOTkgMzQ1LjMxIDExMy45NSAzNDUuMDggMTE1LjAxIEMgMzQzLjc0IDEyMi42OSAzNDQuODcgMTMxLjIxIDM0NS4wOSAxMzkuMDAgQyAzNDUuMjMgMTQzLjM1IDM0NS45MSAxNDcuNjQgMzQ2LjIyIDE1MS45OCBDIDM0OC45MiAxNjguNDYgMzU4LjQwIDE4Mi45MCAzNjcuNDMgMTk2LjU3IEMgMzcyLjA4IDIwNC40NSAzNzAuNDkgMjEyLjQ1IDM3MS43MyAyMjEuMDEgQyAzNzIuMTMgMjI0LjM2IDM3NC4wMiAyMjYuNTYgMzc1LjU3IDIyOS40NCBDIDM3Ni41MSAyMzAuODQgMzc3LjI4IDIzMi45NCAzNzkuMjIgMjMzLjA4IEMgMzc5LjMxIDIyOS45OCAzNzcuNjEgMjI2Ljg0IDM3Ny40NiAyMjMuNjUgQyAzNzkuMTAgMjI2LjcxIDM3OS4yOCAyMzAuNzggMzgxLjE2IDIzMy44MyBMIDM4My4zMSAyMzQuMDMgQyAzODMuNjkgMjMwLjUyIDM4MS45NiAyMjcuNDggMzgyLjI1IDIyNC4wMCBDIDM4Mi4zNiAyMjIuNTEgMzgyLjE1IDIyMS41NSAzODEuNTEgMjIwLjIzIEwgMzgyLjQ1IDIyMC41MyBDIDM4Mi43MSAyMjQuOTkgMzgzLjEyIDIyOS4yNSAzODQuNDYgMjMzLjUzIEMgMzg1LjIzIDIzMy45MSAzODYuMTMgMjM0LjA2IDM4Ny4xNiAyMzMuOTggQyAzODcuOTkgMjMyLjcxIDM4Ni45NSAyMzAuODMgMzg2LjcxIDIyOS40NCBDIDM4NS44NCAyMjYuMzcgMzg2LjczIDIyMy4zMyAzODUuMzggMjIwLjMwIEMgMzg3LjEyIDIyMS44NSAzODYuNTQgMjIzLjg3IDM4Ni43NiAyMjYuMDAgQyAzODYuNzQgMjI4LjQwIDM4Ny42MiAyMzAuNTUgMzg4LjAzIDIzMi45NSBDIDM4OC43NyAyMzIuODggMzg5LjI4IDIzMi4yNCAzODkuNTUgMjMxLjAzIEMgMzkwLjE4IDIyNC41NSAzODguMTcgMjE3Ljc4IDM4OC40OSAyMTEuMzkgQyAzOTEuNjUgMjExLjQ5IDM5NC4yMiAyMTQuNDAgMzk3LjYzIDIxNC41MyBDIDM5Ny41NCAyMTMuNzEgMzk3LjI1IDIxMi45NCAzOTYuNzUgMjEyLjI1IEMgMzk0LjM2IDIwOC42OSAzOTEuNjEgMjA1LjQ0IDM4OC42NCAyMDIuMzcgQyAzODQuNzEgMTk3LjgwIDM4MS4xMSAxOTIuNjYgMzc4Ljk0IDE4Ny4wMSBDIDM3Ni40NiAxODEuNDMgMzc1LjQzIDE3NS41MSAzNzMuMzIgMTY5LjgxIEMgMzY5LjY3IDE1OS43OSAzNjUuMzMgMTUwLjQ2IDM2Mi45MiAxNDAuMDAgQyAzNjAuMTIgMTI4LjczIDM2MC4wOCAxMTcuNTIgMzYwLjcxIDEwNi4wMCBDIDM2MC4zMyA5OS4zNiAzNjAuMTIgOTMuMjcgMzU2LjUyIDg3LjQ0IEMgMzUyLjUyIDgwLjIxIDM0My41OSA3Ni45NSAzMzYuNDggNzMuNTggQyAzMzAuMTIgNzAuNjEgMzIyLjc1IDY5LjM4IDMyMC4wNyA2MS45NyBDIDMxNy41NyA1My4yNSAzMjYuMjggNDUuNDggMzI1Ljk5IDM3LjAwIEMgMzI2LjA1IDMxLjYwIDMyNi4xNiAyNS4xMiAzMjQuMDIgMjAuMDUgQyAzMjEuNTAgMTQuNTQgMzE1LjYzIDEyLjQxIDMxMC4wMSAxMS43MCBDIDMwNC43NiAxMC44NCAyOTcuNzYgMTEuNzMgMjk0LjIzIDE2LjE5IEMgMjkxLjQzIDE5LjA1IDI5MS4xOCAyNC4yMCAyOTAuODIgMjguMDAgQyAyOTAuNzMgMzEuMjcgMjg4LjY5IDMzLjcxIDI4OC45OCAzNy4wMCBDIDI4OC45OCA0Mi4xNiAyOTMuNTQgNDYuOTcgMjk0LjkyIDUyLjA1IEMgMjk1Ljk4IDU1Ljk5IDI5Ny4yMiA2MC43MSAyOTMuOTYgNjMuOTkgQyAyODUuOTcgNzMuMDMgMjcwLjE5IDc0LjY4IDI2MC40MCA4MS4zOSBDIDI1Ny44MCA4My41OCAyNTUuNDggODUuODUgMjU0LjI1IDg5LjEwIEMgMjUyLjMwIDk0LjQ0IDI1MS43NCAxMDAuMzQgMjUyLjA4IDEwNi4wMCBDIDI1Mi44MCAxMTcuNDIgMjUxLjMzIDEyOC44NiAyNDguOTAgMTQwLjAwIEMgMjQ3LjM0IDE0Ny40NCAyNDIuNzQgMTUyLjgyIDIzOS41MSAxNTkuNTIgQyAyMzYuNjAgMTY0Ljg1IDIzNi4zMiAxNzAuMzIgMjM0LjY4IDE3NS45MiBDIDIzMi4yMiAxODMuNzEgMjI3LjQxIDE5MS4xMSAyMjIuODAgMTk3LjgxIEMgMjIwLjEyIDIwMS42NSAyMTYuNzIgMjA0LjYyIDIxMy4yMyAyMDcuNjcgQyAyMTUuOTUgMjA4LjI2IDIxOC40MyAyMDcuODQgMjIxLjI0IDIwNy44MiBMIDIyMS41MCAyMDguOTUgQyAyMjAuMjAgMjE0LjM4IDIxOC4zMSAyMjAuMzcgMjE4LjE3IDIyNi4wMSBDIDIxOS41NyAyMjguMTUgMjE4LjMxIDIzMC44MSAyMjAuMDcgMjMyLjUwIEMgMjIyLjI0IDIzMC4yNCAyMjMuNjEgMjI3LjAwIDIyNS4zMyAyMjQuMzQgQyAyMjYuMzAgMjIzLjAyIDIyNi42OCAyMjAuMDcgMjI4LjY4IDIyMC4yNSBDIDIyNy4wNiAyMjQuOTEgMjIzLjkxIDIyOC42NCAyMjEuOTMgMjMzLjA2IEwgMjIzLjA1IDIzMy40OCBMIDIyMy45NyAyMzMuMDMgQyAyMjYuMjkgMjI5LjMyIDIyOC43OSAyMjUuMDcgMjMxLjk1IDIyMi4wNyBMIDIzMi4xOSAyMjMuMDQgQyAyMzAuODMgMjI2LjM3IDIyNi45OCAyMjkuMTkgMjI2LjI2IDIzMi42OCBDIDIyOC44NCAyMzAuNzcgMjMxLjExIDIyOC41MiAyMzIuNzIgMjI1LjczIEMgMjM4LjQ4IDIxNi43NCAyMzcuMDEgMjA0LjkwIDI0MS43OCAxOTQuODggQyAyNDguMDggMTgyLjY4IDI1Ni44NiAxNzAuNzggMjYyLjE5IDE1OC4xMiBDIDI2Ni42NSAxNDguMjQgMjY4LjEwIDEzNi43NiAyNjguMDIgMTI2LjAwIEMgMjY4Ljc4IDEyMS4zMiAyNjcuNTAgMTE2Ljc4IDI2OC4wOCAxMTIuMDcgTCAyNjkuOTIgMTEyLjA4IEMgMjY5LjQ5IDEyNS40MCAyNzUuMzMgMTM4LjM4IDI3NS4wMiAxNTIuMDAgQyAyNzQuOTcgMTU0LjY3IDI3NS4wNiAxNTcuMzMgMjc1LjAyIDE2MC4wMCBDIDI3My44NCAxNzYuNDEgMjcyLjUwIDE5Mi42MSAyNzAuODQgMjA4Ljk5IEMgMjY5Ljk4IDIxNC42MCAyNjkuMzYgMjIwLjMzIDI2OS4wMCAyMjYuMDAgQyAyNjkuNzYgMjM4Ljg1IDI3My4wNCAyNTIuMjMgMjc1LjA5IDI2NC45NyBDIDI3Ni45NSAyNzYuMzkgMjc4LjczIDI4OC40NiAyNzcuMjcgMzAwLjAxIEMgMjc2Ljk3IDMwNi44OCAyNzQuNTUgMzEzLjE4IDI3My44NyAzMjAuMDEgQyAyNzIuOTAgMzI1Ljk5IDI3NC41MSAzMzIuNTkgMjc1Ljg2IDMzOC40MyBDIDI3OS43NCAzNTMuMDUgMjg2LjU1IDM2NS45MyAyODQuNzkgMzgxLjY2IEMgMjgyLjY1IDM4Mi42OCAyODAuMzUgMzgzLjI1IDI3OS45OSAzODUuOTkgQyAyODAuNTkgMzg4LjQ3IDI4My4wNiAzOTAuNTYgMjg1LjAyIDM5Mi4wNSBDIDI4OC40NiAzOTQuMjYgMjkyLjU2IDM5Ni4yNCAyOTYuNjggMzk0LjY4IEMgMjk3LjA1IDM5NC4wOSAyOTcuNDAgMzkzLjQ5IDI5Ny43NCAzOTIuODggQyAyOTkuMTMgMzg2LjE4IDI5OC4xNiAzNzguOTIgMjk3LjUwIDM3Mi4wMSBDIDI5NS4wNCAzNTcuNTcgMjk3LjkwIDM0NC4zOCAyOTguNTkgMzMwLjAwIEMgMjk4LjMwIDMyMy4zMiAyOTcuMzUgMzE2LjY2IDI5Ni45NiAzMTAuMDAgQyAyOTcuMzcgMjk5LjQ4IDI5OC40NSAyODkuMzcgMjk5LjQ3IDI3OC45OSBDIDMwMC4zMCAyNzAuOTQgMjk5LjkwIDI2My4xMSAyOTkuOTcgMjU1LjAwIEMgMzAwLjkzIDI0MC4wMyAzMDIuMzggMjI1LjIwIDMwMy44NSAyMTAuMjggQyAzMDMuMjUgMjEwLjY3IDMwMi42NSAyMTEuMDQgMzAyLjAzIDIxMS40MCBDIDI5Ny4xOCAyMTIuMDEgMjkzLjY4IDIxNC43MiAyODguNDQgMjEzLjc4IEMgMjg3LjIyIDIxMy40MCAyODYuNDcgMjEzLjY1IDI4NS4yOSAyMTMuOTQgQyAyODMuNjUgMjEyLjg3IDI4MS45MiAyMTMuMDcgMjgwLjA2IDIxMi45MyBDIDI3OS42MCAyMTIuMDcgMjc5LjU4IDIxMS4wNyAyNzkuOTkgMjA5Ljk0IEMgMjg0Ljc0IDIwOS45NyAyODkuMTkgMjExLjIzIDI5My45OSAyMTAuNjMgQyAyOTYuODYgMjA5LjY3IDI5OS42NyAyMDguOTAgMzAxLjkyIDIwNi43NSBDIDMwMy40NiAyMDUuMjUgMzA1LjAwIDIwNS44OSAzMDYuOTAgMjA2LjAwIEMgMzA3LjMwIDIwOC40OSAzMDkuMzEgMjA5LjUyIDMxMS4yNSAyMTAuNzUgQyAzMTUuMzMgMjEzLjA3IDMxOS43NSAyMTMuMDggMzI0LjE5IDIxMS44NCBDIDMyNC4yNyAyMTIuNjggMzIzLjc3IDIxMy4zMCAzMjIuNjkgMjEzLjcxIEMgMzE5Ljk0IDIxNC4yOSAzMTcuMTEgMjE0LjE4IDMxNC4zNiAyMTQuODkgQyAzMTMuNDAgMjE0LjM1IDMxMi4zNSAyMTMuOTggMzExLjIzIDIxMy44MCBDIDMwOS4xMiAyMTEuODMgMzA2Ljk5IDIxMi40MyAzMDUuNDMgMjA5LjQ3IEMgMzA2LjE3IDIxNi4yOSAzMDYuMDUgMjIzLjE3IDMwNi45NyAyMzAuMDAgQyAzMDYuODkgMjM3LjAwIDMwNy41NSAyNDQuMjIgMzA2LjIxIDI1MS4wOSBDIDMwNS41NCAyNTkuMDEgMzA1LjM1IDI2Ny4wNyAzMDUuOTYgMjc1LjAwIEMgMzA1Ljk0IDI4My43OSAzMDguMDUgMjkzLjE4IDMwNy4wNSAzMDIuMDAgQyAzMDcuMDQgMzA5LjM1IDMwNS40NyAzMTYuNjcgMzA0Ljk4IDMyNC4wMCBDIDMwNC4yNSAzMzEuMDQgMzA1LjMzIDMzOC4zOSAzMDYuNTUgMzQ1LjMzIEMgMzA5LjAzIDM1OS40NyAzMDUuNzEgMzcyLjA0IDMwNC42OSAzODYuMDAgQyAzMDQuNjIgMzg5LjEzIDMwNC45OSAzOTMuMjQgMzA4LjI0IDM5NC43MiBDIDMxMy41MSAzOTYuMjMgMzE5Ljc5IDM5Mi44MyAzMjIuMDUgMzg3Ljk4IEMgMzIyLjkxIDM4NS42OSAzMjAuNDMgMzg1LjE0IDMxOS4xNCAzODMuODUgQyAzMTUuNTAgMzgwLjEyIDMxNS4yNiAzNzQuNzEgMzE2LjkzIDM2OS45OSBDIDMyMC4wNiAzNTguNDQgMzIyLjYwIDM0Ni43MiAzMjUuNDIgMzM1LjA5IEMgMzI2LjcwIDMyOC43MSAzMjkuNTQgMzIxLjQ5IDMyOC4zNCAzMTQuOTkgQyAzMjcuNzggMzA4LjU2IDMyNi4zMiAzMDIuNTEgMzI2LjQwIDI5Ni4wMCBDIDMyNS45NSAyNjguMTAgMzM3LjM0IDI0My40OCAzMzguMDAgMjE1LjAwIEMgMzM4LjEwIDIwMS4wMyAzMzYuMzYgMTg3LjAzIDMzNS44MiAxNzMuMDkgQyAzMzUuNTQgMTY4LjA3IDMzNC45NiAxNjMuMDMgMzM0Ljk3IDE1OC4wMCBDIDMzNS41OCAxNDguNjAgMzM3LjQ4IDEzOS40MCAzNDAuMDUgMTMwLjM0IEwgMzQxLjQxIDEzMC4yNCBDIDM0MC4wMiAxNDAuMDcgMzM1Ljg3IDE0OC42MyAzMzcuMTQgMTU4Ljk0IEMgMzM2LjY1IDE2Ni4zNyAzMzguMDggMTczLjU1IDMzOC4xNCAxODAuOTkgQyAzMzkuMDIgMTg4LjY0IDMzOS4yMyAxOTYuMzIgMzM5LjgxIDIwMy45OSBDIDM0MC4yOSAyMTUuNjYgMzM5LjQ5IDIyNy41NSAzMzcuMjcgMjM5LjAyIEMgMzMzLjQ1IDI2MS4wMiAzMjYuNTIgMjgyLjM5IDMyOS4xNiAzMDQuOTkgQyAzMzAuODkgMzE0LjM4IDMzMC4wMyAzMjMuNzMgMzI3LjgwIDMzMi45NiBDIDMyNC43OCAzNDYuMzggMzIxLjQ3IDM1OS43MiAzMTguMjMgMzczLjA5IEMgMzE3LjkxIDM3NS40NiAzMTcuNDkgMzc4Ljg2IDMxOS4yNCAzODAuNzggQyAzMjAuNTcgMzgyLjg4IDMyMy41MyAzODMuMjQgMzI0LjY1IDM4NS4zNiBDIDMyNC45MiAzODkuOTggMzIwLjUzIDM5My44NSAzMTYuNzAgMzk1LjY2IEMgMzEzLjEwIDM5Ny42NCAzMDcuNDEgMzk3Ljg1IDMwNC41MSAzOTQuNTMgQyAzMDIuMjEgMzkxLjgxIDMwMy4xNiAzODcuMzcgMzAzLjAwIDM4NC4wMCBDIDMwMy43NiAzNzUuNjYgMzA1LjAwIDM2Ny4zNyAzMDUuNjUgMzU5LjAwIEMgMzA1LjQ1IDM0OC42OCAzMDIuNDggMzM4LjI4IDMwMi45OCAzMjguMDAgQyAzMDIuNzEgMzIzLjU1IDMwMy43NSAzMTkuMzcgMzA0LjM2IDMxNC45OSBDIDMwNy43NiAyOTYuMjYgMzAyLjU0IDI3Ni43NSAzMDQuMzYgMjU4LjAwIEMgMzA0LjY0IDI1MS42NCAzMDUuMjEgMjQ1LjM4IDMwNC45OCAyMzkuMDAgQyAzMDQuODMgMjMzLjQ2IDMwNS40MCAyMjcuNzQgMzA0LjUxIDIyMi4yNyBDIDMwMy4zNyAyMzMuNDQgMzAxLjY1IDI0NC40NSAzMDEuNDggMjU1LjcxIEMgMzAxLjY5IDI2Ny40MiAzMDIuNDQgMjc4LjM4IDMwMC4wNyAyOTAuMDAgQyAyOTguMzYgMjk4LjAxIDI5OS4xOSAzMDUuODcgMjk5LjAxIDMxNC4wMCBDIDI5OS4zNSAzMjAuMDQgMzAwLjQ0IDMyNS45MSAyOTkuOTMgMzMyLjAwIEMgMjk5LjM1IDMzOC42NiAyOTkuMjAgMzQ1LjM2IDI5OC40NSAzNTIuMDAgQyAyOTcuMTEgMzY1LjY5IDMwMi44OSAzNzguNjUgMjk5LjYzIDM5Mi45MiBDIDI5OS4yNiAzOTQuODAgMjk4LjM0IDM5NS43NSAyOTYuOTggMzk2Ljk4IEMgMjkyLjk0IDM5Ny4zNCAyODkuNjQgMzk2Ljg1IDI4Ni4wNiAzOTQuOTAgQyAyODMuMDEgMzkzLjI1IDI3OC42OSAzOTAuNjkgMjc3LjkyIDM4Ny4wMyBDIDI3Ny43MiAzODMuNTYgMjgyLjAzIDM4Mi4yMSAyODIuOTQgMzc5Ljk5IEMgMjgzLjQ2IDM3Mi45NSAyODEuODAgMzY2LjcxIDI4MC40NSAzNTkuOTEgQyAyNzguMTMgMzQ5LjkzIDI3My4wNyAzNDAuMTUgMjcxLjk1IDMzMC4wNCBDIDI3MS4xOSAzMjIuNjUgMjcyLjkzIDMxNi4yMyAyNzQuMDQgMzA5LjAxIEMgMjc1Ljc3IDI5OS43MCAyNzYuODcgMjkwLjUyIDI3NS4xNSAyODEuMTAgQyAyNzMuNjcgMjY1LjkzIDI3MC4zNyAyNTEuMDcgMjY4LjI4IDIzNi4wMCBDIDI2Ny42MSAyMjkuMjggMjY3LjU4IDIyMi43MSAyNjguMjggMjE1Ljk4IEMgMjY5LjE2IDIxMS4yMCAyNjguOTQgMjA2LjM0IDI2OS43NyAyMDEuNTMgQyAyNzAuOTUgMTg0LjA2IDI3My41OSAxNjYuNTIgMjczLjM1IDE0OS4wMCBDIDI3My4zNiAxNDMuMTEgMjcxLjYwIDEzNy43NSAyNzAuNzIgMTMyLjAxIEwgMjcwLjE5IDEzMi4yNSBDIDI2OS4xMSAxMzYuMDcgMjY5LjQwIDE0MC4yNCAyNjguNDMgMTQ0LjE1IEMgMjY3LjEwIDE1My40MyAyNjIuOTMgMTYwLjcwIDI1OS4yMiAxNjkuMTAgQyAyNTMuNzIgMTc5Ljg4IDI0NS4xNSAxODkuMjQgMjQxLjU2IDIwMS4xMSBDIDIzOS40MyAyMDguNTggMjM5LjkwIDIxOC43NiAyMzUuMjggMjI1LjI3IEMgMjMyLjM5IDIyOC44OSAyMjkuMjAgMjM0LjA0IDIyNC43MCAyMzUuNjggQyAyMjEuMjkgMjM2LjM0IDIxNy44MSAyMzMuNzIgMjE2LjAyIDIzMS4wMCBDIDIxNC42OSAyMjguMzYgMjE2LjQwIDIyNC43OSAyMTYuODIgMjIxLjk5IEMgMjE3LjQxIDIxOC4wMiAyMTguOTggMjE0LjA0IDIxOC45NyAyMTAuMDMgQyAyMTYuNzQgMjEwLjAwIDIxNC4xOSAyMTAuNzAgMjEyLjA4IDIwOS45NyBDIDIxMC41NCAyMDguOTAgMjEwLjU3IDIwNy44NCAyMTAuOTYgMjA2LjE0IEMgMjE2LjkyIDIwMy4yMyAyMjAuNjIgMTk4LjQ1IDIyNC4yNCAxOTMuMDkgQyAyMjcuMzMgMTg4LjM0IDIyOS45MiAxODMuMjggMjMxLjkzIDE3Ny45OCBDIDIzMy41NiAxNzMuNTUgMjMzLjk2IDE2OC44OCAyMzUuNDEgMTY0LjQxIEMgMjM2Ljg2IDE1OS41NyAyNDAuMDkgMTU1LjAxIDI0Mi41MSAxNTAuNTQgQyAyNDguNjkgMTQwLjA4IDI1MC42MiAxMjYuOTYgMjUxLjAwIDExNS4wMCBDIDI1MC42NyAxMDUuMTggMjQ4LjQ5IDkzLjE4IDI1NC42NyA4NC42MyBDIDI2MS4wNCA3NS44MyAyNzIuNDcgNzQuMzAgMjgxLjQ3IDY5LjQxIEMgMjg2LjU1IDY2Ljg5IDI5Mi4zOCA2NC4wMCAyOTMuOTMgNTguMDIgQyAyOTQuNzUgNTAuOTMgMjg5LjI3IDQ3LjA0IDI4Ny40NCA0MC45MSBDIDI4NS45NyAzNi44OSAyODguNjEgMzIuMjAgMjg4LjkxIDI4LjAyIEMgMjg4Ljk4IDIzLjQ5IDI4OS43OCAxOC40MyAyOTIuODMgMTQuODkgQyAyOTUuNzIgMTEuMjQgMzAwLjQ3IDkuNzMgMzA0Ljk4IDkuNjIgWiIgZmlsbD0iIzgxODE4MSIgLz4KPHBhdGggZD0iTSAxMjUuNjMgMTguNDAgQyAxMjguMzUgMjIuMDcgMTI4LjEzIDI1Ljk4IDEyOS45OSAyOS45OCBDIDEzMS41OSAzMy41NSAxMzIuMTQgMzcuMzAgMTI5LjU1IDQwLjU3IEMgMTI1LjU1IDQ1LjUwIDEyNS40NiA1MS4yOSAxMjAuNzIgNTUuNzIgQyAxMTcuNzUgNTkuMTAgMTEzLjAxIDU5LjgzIDEwOC45MCA1OC4zMiBDIDEwMy4wMSA1Ni4xMCA5OS4wNCA1MS40MCA5Ni4zNSA0NS44NiBDIDk0Ljg2IDQyLjQxIDkyLjAxIDQwLjU4IDkxLjM5IDM2Ljc1IEMgOTAuOTMgMzMuNzIgOTMuMDggMzAuMTggOTQuODAgMjcuODEgQyA5NS45MCAyMi40NyA5Ni40NyAxNy43NiAxMDEuMDkgMTQuMTcgQyAxMDguNjkgOC4xNyAxMjAuMjEgMTAuNzQgMTI1LjYzIDE4LjQwIFoiIGZpbGw9IiNmZmZmZmYiIC8+CjxwYXRoIGQ9Ik0gMTA1LjkxIDI5Ljk1IEMgMTA2Ljg1IDMxLjA5IDEwNy43NSAzMi4xMSAxMDYuNDUgMzMuNDUgQyAxMDQuOTQgMzUuMzAgMTAxLjc4IDMzLjg3IDEwMi40NyAzMS40NyBDIDEwMy4xNiAyOS42NCAxMDQuMjUgMjkuOTggMTA1LjkxIDI5Ljk1IFoiIGZpbGw9IiM4MTgxODEiIC8+CjxwYXRoIGQ9Ik0gMTE2LjQ1IDMwLjQ2IEMgMTE5LjE1IDI4LjMyIDEyMS42MCAzMi40NiAxMTkuNDUgMzQuNDUgQyAxMTcuMDYgMzYuNzAgMTEzLjU3IDMyLjQzIDExNi40NSAzMC40NiBaIiBmaWxsPSIjODE4MTgxIiAvPgo8cGF0aCBkPSJNIDEwOS4xOCA0Mi4wMiBDIDExMS40NiA0MS45MCAxMTMuNzUgNDIuMDAgMTE2LjAzIDQxLjk5IEwgMTE2LjAzIDQ0LjAyIEMgMTE0LjM0IDQ0LjE4IDExMS44OSA0My41OSAxMTAuNDIgNDQuNDEgQyAxMDkuNjEgNDUuMDcgMTA4Ljc5IDQ1LjI3IDEwNy45OCA0NS4wMSBDIDEwNy42NSA0My43OCAxMDguMDUgNDIuNzggMTA5LjE4IDQyLjAyIFoiIGZpbGw9IiM4MTgxODEiIC8+CjxwYXRoIGQ9Ik0gMTA5Ljk4IDQ2Ljk4IEMgMTExLjY2IDQ2Ljk3IDExMy4zNCA0Ni45NyAxMTUuMDEgNDYuOTggQyAxMTUuMDIgNDguMzMgMTE1LjAzIDQ5LjY4IDExNS4wMyA1MS4wNCBDIDExMy4zNSA1MS4wNSAxMTEuNjYgNTEuMDUgMTA5Ljk4IDUxLjAyIEMgMTA5Ljk4IDQ5LjY3IDEwOS45OCA0OC4zMyAxMDkuOTggNDYuOTggWiIgZmlsbD0iIzgxODE4MSIgLz4KPHBhdGggZD0iTSAzMDUuNTMgNzguMzcgTCAzMDYuNzIgNzguMzIgQyAzMDcuMzUgODAuNTkgMzA2LjQ5IDgyLjcwIDMwNi44NCA4NC45OSBDIDMwNy4zNiA4OC44MSAzMDYuNDMgOTMuNDQgMzA4LjA5IDk2Ljk2IEMgMzA3LjAzIDEwMS44MiAzMDguMTkgMTA2LjE3IDMwOC4wMiAxMTEuMDAgQyAzMDcuODggMTE2LjY3IDMwOC4yNiAxMjIuMzggMzA3Ljc5IDEyOC4wMyBDIDMwNy44NSAxMzAuNDYgMzA4LjM0IDEzMi42MiAzMDcuNDYgMTM1LjAwIEMgMzA4LjI0IDEzOC42NiAzMDcuODcgMTQyLjMwIDMwOC4wOCAxNDYuMDIgQyAzMDcuOTYgMTQ2LjkxIDMwOC4yNyAxNDcuNjggMzA5LjAzIDE0OC4zMyBDIDMwNy45NCAxNTEuNjggMzA3Ljk3IDE1NS41MiAzMDguMTMgMTU5LjAwIEMgMzA4LjYxIDE2MS43NyAzMDcuNTAgMTY0LjA3IDMwOS4wOCAxNjYuNjggQyAzMDcuMTUgMTY4LjEzIDMwOC4yMSAxNjkuOTMgMzA3Ljk2IDE3Mi4wNCBMIDMwNi44NyAxNzIuMDQgQyAzMDcuMTMgMTYxLjg3IDMwNy4xMyAxNTEuNjIgMzA2LjczIDE0MS40NiBMIDMwNS45OCAxNDAuNzUgQyAzMDYuNDAgMTM4LjM4IDMwNi41NSAxMzYuMzAgMzA2LjAxIDEzMy45MyBDIDMwNi4xMyAxMzMuMjggMzA2LjQyIDEzMi42NSAzMDYuODkgMTMyLjA0IEMgMzA2LjUyIDEyOC4zNiAzMDYuMTkgMTI0LjQ2IDMwNi41NyAxMjAuNzcgQyAzMDYuOTEgMTEzLjgzIDMwNi41MSAxMDYuOTQgMzA2LjMzIDEwMC4wMCBDIDMwNS43MSA5Mi43MyAzMDYuMzYgODUuNjIgMzA1LjUzIDc4LjM3IFoiIGZpbGw9IiM4MTgxODEiIC8+CjxwYXRoIGQ9Ik0gMTM5LjAwIDgxLjA1IEwgMTM5LjAxIDgyLjAxIEMgMTM0LjYzIDgyLjA0IDEzMC4yNCA4Mi4wMCAxMjUuODUgODIuMDYgTCAxMjYuMDIgODEuMDIgQyAxMjkuOTIgNzguOTcgMTM1LjAxIDgwLjI0IDEzOS4wMCA4MS4wNSBaIiBmaWxsPSIjODE4MTgxIiAvPgo8cGF0aCBkPSJNIDk0LjA2IDgwLjkyIEMgOTYuMjUgODIuMDYgOTcuNTQgODIuMTAgMTAwLjAyIDgyLjAwIEwgMTAwLjAyIDgyLjk5IEMgOTcuODcgODIuODkgOTYuNzkgODMuMDQgOTUuMDAgODQuMjMgQyA5Mi43NSA4Mi41MCA5MC41MyA4My4wOSA4Ny44OSA4My4wNyBDIDg3LjUzIDgwLjgzIDkyLjcxIDgxLjQzIDk0LjA2IDgwLjkyIFoiIGZpbGw9IiM4MTgxODEiIC8+CjxwYXRoIGQ9Ik0gMzE1Ljk5IDExNS45MyBDIDMxOC40NiAxMTUuMDUgMzE4LjY5IDExOC4xOCAzMjAuODAgMTE5LjIzIEMgMzIxLjIyIDEyMi4wNiAzMjQuODQgMTIwLjkxIDMyNS4xMSAxMjMuMDMgQyAzMjEuNDcgMTIzLjY0IDMxOS4xOCAxMjEuNDggMzE3LjM2IDExOC42MyBDIDMxNi4wOCAxMTguMTUgMzE1LjYyIDExNy4yNiAzMTUuOTkgMTE1LjkzIFoiIGZpbGw9IiM4MTgxODEiIC8+CjxwYXRoIGQ9Ik0gMjgzLjg3IDExOC45OSBDIDI4Ni4zNyAxMTguNzIgMjg4LjI3IDEyMi4xNCAyOTEuMDAgMTIxLjk5IEMgMjkzLjMxIDEyMi4xMSAyOTUuMzQgMTE5LjQ3IDI5Ny4wNSAxMTguODUgQyAyOTcuNjYgMTIyLjEzIDI5NC43MyAxMjEuMzkgMjkzLjg1IDEyNC4wMCBDIDI5MC4xNyAxMjQuNzcgMjg4LjQ0IDEyMi41OCAyODUuMTQgMTIxLjg5IEMgMjg0LjYyIDEyMC45NCAyODQuMjAgMTE5Ljk4IDI4My44NyAxMTguOTkgWiIgZmlsbD0iIzgxODE4MSIgLz4KPHBhdGggZD0iTSAxNDEuMDQgMTI2LjgyIEwgMTQwLjk4IDEyOC44NiBDIDEzOC45MSAxMjkuMzAgMTM3LjcwIDEzMS4xMyAxMzUuNzQgMTMxLjk4IEMgMTMzLjI3IDEzMS45NSAxMzAuNjQgMTMyLjI2IDEyOC4yMCAxMzEuODcgQyAxMjYuMTkgMTMwLjk5IDEyNC4yNyAxMzAuMzEgMTIyLjE2IDEyOS43MyBDIDEyMS4yNCAxMjkuMzggMTIwLjgyIDEyOC43NiAxMjAuOTAgMTI3Ljg3IEMgMTIyLjg2IDEyOC4yNSAxMjQuNzQgMTI4LjgwIDEyNi42MyAxMjkuNDMgQyAxMjkuMTIgMTMwLjMxIDEzMS41MiAxMjkuODUgMTM0LjAyIDEzMC4zOCBDIDEzNS43NSAxMzAuMDEgMTM3LjUwIDEyOS4wNiAxMzguOTcgMTI4LjA3IEMgMTM5LjYwIDEyNy41MyAxNDAuMjkgMTI3LjExIDE0MS4wNCAxMjYuODIgWiIgZmlsbD0iIzgxODE4MSIgLz4KPHBhdGggZD0iTSA4NC44OCAxMjcuOTQgQyA4OC40OCAxMjcuODIgOTAuOTMgMTMwLjU2IDk0LjczIDEzMC4wNSBDIDk2LjYzIDEzMS45OCAxMDEuNDcgMTMxLjAwIDEwMy4yNyAxMjkuMTEgQyAxMDQuNzUgMTI4LjM5IDEwNS4zNSAxMjkuMDEgMTA1LjA2IDEzMC45NiBDIDEwMi4wNiAxMzAuOTAgOTkuMDYgMTMzLjU0IDk1Ljk3IDEzMy4wNSBDIDkzLjE3IDEzMy4wNiA5MS4yNCAxMzAuODMgODguMjYgMTMwLjkxIEMgODcuMzggMTI5Ljc5IDg0Ljg3IDEyOS40NCA4NC44OCAxMjcuOTQgWiIgZmlsbD0iIzgxODE4MSIgLz4KPHBhdGggZD0iTSAyODAuODYgMTY2LjAwIEMgMjgyLjUwIDE2NS45NiAyODQuMTQgMTY1Ljk2IDI4NS43NyAxNjYuMDEgQyAyODcuNDcgMTY4LjA3IDI5MC43MiAxNjcuODkgMjkwLjA0IDE3MS4xOCBDIDI4Ny40MCAxNzAuMjcgMjg0LjkxIDE2Ny43OSAyODIuMTAgMTY3Ljk1IEMgMjgxLjM1IDE2Ny40MyAyODAuOTQgMTY2Ljc4IDI4MC44NiAxNjYuMDAgWiIgZmlsbD0iIzgxODE4MSIgLz4KPHBhdGggZD0iTSAzMjkuNzkgMTY3LjkzIEMgMzMwLjUwIDE2OC40OCAzMzAuOTMgMTY5LjExIDMzMS4wOSAxNjkuODEgQyAzMjkuNjYgMTcxLjY5IDMyNy45NyAxNzAuODkgMzI1LjkxIDE3MS4wNSBDIDMyNS41MCAxNjcuNzYgMzI3LjgxIDE2OS42NyAzMjkuNzkgMTY3LjkzIFoiIGZpbGw9IiM4MTgxODEiIC8+CjxwYXRoIGQ9Ik0gMTM5LjA0IDE3Ni45NiBMIDEzOS4wMiAxNzcuOTYgQyAxMzcuMzggMTc4LjA2IDEzNi40NiAxNzguMDAgMTM1LjM3IDE3OS4zNiBDIDEzMy41NyAxODEuNTIgMTMwLjc3IDE4MS44MyAxMjguOTIgMTgzLjk3IEMgMTI3LjkxIDE4NS4yOSAxMjcuOTYgMTg3LjExIDEyNS45NCAxODcuMTMgQyAxMjUuODggMTgzLjk1IDEyOC4zOSAxODIuMzYgMTMwLjIwIDE4MC4xNSBDIDEzMy41MiAxNzguOTkgMTM1LjI3IDE3Ni40MiAxMzkuMDQgMTc2Ljk2IFoiIGZpbGw9IiM4MTgxODEiIC8+CjxwYXRoIGQ9Ik0gOTMuNzQgMTgxLjA5IEMgOTUuMjAgMTgxLjk0IDk4LjIyIDE4My4xMiA5OC4wOCAxODUuMTcgQyA5NC4xNSAxODMuODQgOTAuMDYgMTgxLjUxIDg1Ljg4IDE4Mi4wMCBDIDg2LjI3IDE3OC42MiA5MS42MiAxODEuMjUgOTMuNzQgMTgxLjA5IFoiIGZpbGw9IiM4MTgxODEiIC8+CjxwYXRoIGQ9Ik0gMjIzLjg2IDIxOC4xMCBDIDIyNC40OSAyMjAuNDQgMjIxLjcwIDIyMy42NCAyMjAuNzIgMjI1LjgxIEwgMjE5LjY4IDIyNi4xOSBDIDIxOS40OCAyMjQuNTcgMjIyLjQ4IDIxOS4wOCAyMjMuODYgMjE4LjEwIFoiIGZpbGw9IiM4MTgxODEiIC8+CjxwYXRoIGQ9Ik0gMTI1LjMzIDQyNS42OCBDIDEyOC4zNiA0MjguMjcgMTMwLjk5IDQzMS4wMSAxMzEuNzkgNDM1LjA3IEMgMTMyLjQ1IDQzOS4zOCAxMzIuMDYgNDQzLjY2IDEzMy4wOSA0NDguMDAgQyAxMzMuNzEgNDUxLjQzIDEzNy42NSA0NTIuNzMgMTM3LjA5IDQ1Ni4zNSBMIDEzNi4wNSA0NTYuMzYgQyAxMzQuNjAgNDU0LjM2IDEzMi45NiA0NTIuMjggMTMyLjA1IDQ0OS45NyBDIDEzMC4yMCA0NDMuODYgMTMxLjY3IDQzNi42MyAxMjcuOTkgNDMxLjAwIEMgMTIzLjY2IDQyNS4yOSAxMTYuMTQgNDIyLjE1IDEwOS4wOSA0MjIuMTcgQyAxMDEuODAgNDIyLjkwIDk0Ljk1IDQyNi42NCA5MC42MSA0MzIuNTggQyA4Ny4zNCA0MzcuMDQgODQuNjYgNDQyLjM5IDg0LjMzIDQ0Ny45OSBDIDg0LjIyIDQ1Ny41OSA5MS4zNSA0NjUuNjkgOTEuMjggNDc1LjAwIEMgOTEuNjQgNDgxLjQzIDg4LjkzIDQ4Ny4zMyA4NS43MyA0OTIuNzQgQyA4Mi42MiA0OTcuOTkgNzguMzcgNTAxLjQyIDc2LjM3IDUwNy4xMCBDIDc1LjE4IDUxMS4wNCA3NC4yOSA1MTUuMDYgNzMuMTYgNTE5LjAyIEMgNzAuMjQgNTMxLjIxIDY0LjYwIDU0Mi45NyA2MC44NyA1NTQuOTMgQyA1Ny4wNSA1NjcuMTUgNTUuMzAgNTc5Ljk2IDU1LjA0IDU5Mi43MyBMIDUzLjQzIDU5Mi44NCBMIDUzLjA3IDU5MS4xOSBDIDU0LjAzIDU4My4wNCA1My43NyA1NzUuMDEgNTUuODAgNTY2Ljk4IEMgNTkuMTkgNTUxLjAwIDY1LjQ0IDUzNi43MyA3MC40NCA1MjEuNDUgQyA3Mi4wNiA1MTYuNzQgNzIuODcgNTExLjg0IDc0LjI4IDUwNy4wOCBDIDc1Ljg3IDUwMS45MSA3OS43NCA0OTguODcgODIuNzkgNDkzLjkzIEMgODUuNjcgNDg5LjM2IDg5LjI1IDQ4My41NSA4OS4wMiA0NzguMDAgQyA4OS44OSA0NjcuOTMgODIuMzIgNDU4LjA4IDgyLjYwIDQ0OC4wMCBDIDgyLjQyIDQ0NC4xNyA4My41NSA0NDAuNzAgODUuNDAgNDM3LjQwIEMgODkuMTkgNDMwLjE0IDk0LjkxIDQyNC4xNiAxMDIuODUgNDIxLjYxIEMgMTEwLjQwIDQxOC45MSAxMTkuMDUgNDIwLjkzIDEyNS4zMyA0MjUuNjggWiIgZmlsbD0iIzgxODE4MSIgLz4KPHBhdGggZD0iTSAzMDAuOTEgNDIxLjUwIEMgMzA3LjM4IDQyMi4yNiAzMTMuMjcgNDI1LjY1IDMxNy42NiA0MzAuMzYgQyAzMjIuMTQgNDM2LjAxIDMyNy4wNSA0NDMuNDYgMzI2LjAwIDQ1MS4wMCBDIDMyNS4xNSA0NTkuNDUgMzE4LjE5IDQ2OC42OCAzMTkuMDQgNDc4LjAwIEMgMzE5Ljk4IDQ4NS42OCAzMjUuNDMgNDkyLjA0IDMzMC44MSA0OTcuMjAgQyAzMzMuNDIgNTAwLjAzIDMzNi40NiA1MDEuODggMzM3LjQyIDUwNS43OSBDIDMzOS4yNCA1MTIuNDAgMzM5Ljk1IDUxOS4zOSAzNDEuNzkgNTI2LjAwIEMgMzQyLjczIDUyOS40MiAzNDQuMjggNTMyLjU5IDM0NS4yOCA1MzUuOTkgQyAzNDguOTkgNTQ4LjkzIDM1Mi4zNyA1NjEuNTggMzUzLjk3IDU3NS4wMCBDIDM1NC4yMCA1NzguNTYgMzU0LjM3IDU4Mi4wMyAzNTUuMTQgNTg1LjUzIEMgMzU0LjUzIDU4Ni40NCAzNTMuNzcgNTg2LjkzIDM1Mi44NiA1ODcuMDAgQyAzNTIuNzQgNTc0LjEwIDM0OS43MSA1NjEuNTAgMzQ2LjcyIDU0OS4wMyBDIDM0NC4yMCA1MzkuMDIgMzQwLjEwIDUyOS4xNiAzMzguMzEgNTE5LjAwIEMgMzM3LjcwIDUxNC40NSAzMzcuNDcgNTA4LjkxIDMzNS4yMiA1MDQuODQgQyAzMzAuNjAgNDk5LjY3IDMyNS4wNyA0OTQuNzkgMzIxLjE4IDQ4OC44MiBDIDMxOC44MSA0ODUuMzIgMzE2LjU3IDQ4MS4zOCAzMTYuOTYgNDc3LjAwIEMgMzE3LjcxIDQ2OC4xMCAzMjEuODYgNDYxLjQyIDMyMy44NiA0NTIuOTkgQyAzMjYuMDkgNDM4LjA0IDMxMi41MCA0MjMuOTUgMjk4LjAwIDQyMy4wMCBDIDI5MC40NiA0MjMuNjggMjgzLjQ1IDQyNi45NCAyNzkuODggNDMzLjkzIEMgMjc3LjEzIDQzOS4yNyAyNzcuNzkgNDQ0LjU5IDI3Ni41OCA0NTAuMTEgQyAyNzYuMTYgNDUyLjg5IDI3My42MCA0NTQuNDIgMjcyLjk4IDQ1Ni45NiBDIDI3My42NiA0NTguNTkgMjc2LjI1IDQ1OS4xMyAyNzYuODIgNDYxLjA0IEMgMjc3LjQ3IDQ2NS44MiAyNzYuMTggNDcwLjAzIDI3OC4zMSA0NzQuNjggQyAyODAuNzYgNDc4LjQwIDI4Ny44OSA0NzQuOTIgMjg5LjY0IDQ3OS4zNCBDIDI5MS45MyA0ODQuNzIgMjkzLjgyIDQ5MS4xNiAyOTEuODUgNDk2LjkyIEMgMjg4LjA3IDUwOC43OSAyNzkuMDUgNTE5LjM0IDI3NS40MSA1MzEuMjggQyAyNzEuOTQgNTQxLjU1IDI3MC40MSA1NTIuMTkgMjcwLjAwIDU2My4wMCBDIDI2OS44MSA1NzIuOTMgMjcxLjU5IDU4My4yMiAyNzMuMDcgNTkzLjAwIEMgMjc0LjI3IDYwMC41MyAyNzQuNzcgNjA4Ljc1IDI3Ni42NSA2MTYuMDkgQyAyNzguMzIgNjIyLjEyIDI4My43NSA2MjYuNDkgMjg1LjcxIDYzMi4wOSBDIDI4Ny44NyA2NDAuNTIgMjg4LjI3IDY0OS40NiAyODkuOTkgNjU4LjAwIEMgMjkyLjc0IDY3NC40OSAyOTcuMjAgNjkxLjQwIDI5OC4yNCA3MDcuOTkgQyAyOTguNzkgNzExLjQzIDI5OS42MyA3MTQuNzggMzAxLjIzIDcxNy45MCBDIDMwMy4yNiA3MjIuNDQgMzA2LjA3IDcyNS44MSAzMDYuMDIgNzMxLjAwIEMgMzA2Ljc3IDc0MS42NyAzMDguMTQgNzUyLjMzIDMwOS4wMSA3NjMuMDAgQyAzMDkuMjAgNzcwLjgyIDMxMi4yOCA3ODAuMjkgMzEwLjMzIDc4OC4wMiBDIDMxMC4wMSA3OTAuNjIgMzA3LjcyIDc5Mi4zNSAzMDUuNzIgNzkzLjc0IEMgMjk4LjI4IDc5OC4wOSAyOTAuNzYgODAyLjI4IDI4My4xMiA4MDYuMjYgQyAyODAuOTUgODA3LjQxIDI3OC43OSA4MDcuNjcgMjc3LjE5IDgwOS42NiBDIDI3OS4xOCA4MTEuNjQgMjgxLjQxIDgxMS4zMCAyODQuMDAgODExLjQ0IEMgMjkzLjMxIDgxMi4wNyAzMDIuNTQgODExLjYzIDMxMS45OSA4MTEuMTkgQyAzMTUuODcgODEwLjgwIDMyMS4yNSA4MTAuOTUgMzI0LjM4IDgwOC4zOCBDIDMyNi45NSA4MDUuODMgMzI2LjM4IDgwMS4yNSAzMjYuMDcgNzk3Ljk5IEMgMzI2LjA1IDc5MC45NCAzMjQuNTUgNzg0LjA5IDMyNC45OSA3NzcuMDAgQyAzMjQuNTYgNzcwLjUyIDMyNS44NiA3NjQuMzYgMzI2Ljg0IDc1OC4wMCBDIDMyNy44NyA3NDguOTIgMzI5LjU1IDczOS4wMSAzMjcuOTIgNzI5Ljk5IEMgMzI1LjI3IDcxNi4zNSAzMTkuNjQgNzA0LjMyIDMxOS45OSA2OTAuMDAgQyAzMTkuNzcgNjgxLjY2IDMyMC43NiA2NzMuMzMgMzIxLjMyIDY2NS4wMCBDIDMyMi4zNSA2NTguMzMgMzIxLjkwIDY1MS43MiAzMjEuOTggNjQ1LjAwIEMgMzIyLjEwIDY0Mi42NiAzMjEuMjkgNjM5LjYyIDMyMi41NSA2MzcuNTggQyAzMjUuNzQgNjMyLjMxIDMyOC4zNSA2MjYuMTggMzI4Ljg4IDYyMC4wMCBDIDMyNy44OCA2MDkuNDcgMzE4LjYzIDYwMC45NCAzMTcuMTkgNTkxLjAwIEMgMzE2LjMxIDU4NC4zMCAzMTcuOTkgNTc4LjczIDMxOC4wOSA1NzIuMDEgQyAzMTguNzcgNTY3LjY3IDMxOC45MCA1NjMuMjQgMzIwLjE2IDU1OS4wNiBDIDMyMS4xNyA1NTguNzAgMzIxLjg5IDU1OC45MSAzMjIuMzEgNTU5LjY5IEMgMzI1LjU3IDU2My45NiAzMjguNjkgNTY4LjA2IDMzMC43NCA1NzMuMDkgQyAzMzMuNDIgNTc5LjcxIDMzMy41NSA1ODYuOTEgMzM1LjQ0IDU5My45OCBDIDMzNy44NSA2MDUuMDUgMzQxLjYxIDYxNC42MyAzNDEuMjkgNjI2LjAwIEMgMzQxLjQzIDYzMy42OSAzMzguMjUgNjQxLjk5IDMzOS40NiA2NDkuNTUgQyAzNDAuNjMgNjQ5LjM5IDM0MS42NCA2NDkuNDggMzQyLjU1IDY0OC42MSBDIDM0My40NyA2NDYuMjUgMzQzLjc5IDY0My44MyAzNDQuNDIgNjQxLjM5IEMgMzQ2LjEzIDY0MC4zMCAzNDYuMzggNjQxLjczIDM0Ni44OCA2NDMuMDMgQyAzNDguNDIgNjQ4LjQ5IDM1MC4xOCA2NTQuNzYgMzUzLjc0IDY1OS4yNyBDIDM1NS4xNSA2NjAuOTkgMzU3LjU5IDY2My4xNSAzNTkuOTYgNjYyLjk0IEMgMzU4Ljk4IDY1OS45OSAzNTcuODkgNjU3LjA0IDM1Ni43MSA2NTQuMTYgQyAzNTYuMDkgNjUyLjcyIDM1NC43NiA2NTEuOTUgMzU0LjM0IDY1MC40MCBDIDM1NS45OSA2NTAuMjQgMzU2LjUyIDY1MS41MSAzNTcuMzEgNjUyLjcwIEMgMzU5LjA4IDY1NS44NiAzNjAuMTUgNjU5LjQ4IDM2MS4yOCA2NjIuOTAgQyAzNjIuODEgNjYzLjA4IDM2My44OSA2NjIuNDkgMzY1LjI0IDY2MS44OSBDIDM2My44MiA2NTcuODEgMzYxLjI3IDY1My45OCAzNTkuODQgNjQ5Ljg4IEwgMzYwLjg5IDY1MC4wMCBDIDM2My41MyA2NTIuOTIgMzYzLjk5IDY1OC4wOSAzNjYuOTQgNjYwLjY3IEMgMzY3LjgwIDY1OS43OCAzNjguNjMgNjU4Ljg3IDM2OS40MyA2NTcuOTQgQyAzNjcuODIgNjU1LjAxIDM2Ni4xMCA2NTIuMjEgMzY0Ljc2IDY0OS4xMyBDIDM2NC4xNyA2NDcuNjUgMzYyLjMyIDY0NS42NyAzNjIuOTkgNjQ0LjA3IEMgMzY2LjIwIDY0Ni45MSAzNjcuMTIgNjUyLjQ5IDM3MC4wMCA2NTUuOTIgQyAzNzIuMjEgNjUzLjY1IDM3MS41MSA2NDguNzEgMzY5LjczIDY0Ni4yNCBDIDM2NC4wOSA2MzcuMTYgMzU2LjMwIDYyOC4wMCAzNTUuMDEgNjE3LjAxIEMgMzU1LjA3IDYxMi43NCAzNTMuODMgNjA4LjI5IDM1NC40MSA2MDQuMTIgTCAzNTUuOTggNjA0LjQ1IEMgMzU1Ljk2IDYwOS42MiAzNTYuNjcgNjE0LjgxIDM1Ny41NSA2MTkuODkgQyAzNjAuMzEgNjI5LjQxIDM2Ny4yMSA2MzguMzggMzcyLjMxIDY0Ni43OSBDIDM3My45MCA2NDkuNjQgMzczLjk4IDY1Mi43MSAzNzIuNjUgNjU1LjY4IEMgMzcwLjk4IDY1OS4yNSAzNjkuMTAgNjYyLjg2IDM2NS4xMSA2NjQuMjEgQyAzNjAuOTggNjY2LjA2IDM1NS44NyA2NjQuMzAgMzUzLjAwIDY2MS4wMCBDIDM0OC42MiA2NTYuNDAgMzQ3LjY5IDY1MC41OSAzNDUuMzYgNjQ1LjE5IEMgMzQ0Ljk4IDY0Ny41NiAzNDQuNDQgNjUxLjIyIDM0Mi4xOCA2NTIuNTggQyAzNDAuNjggNjUzLjUzIDMzOS4zMSA2NTMuMDYgMzM3Ljk5IDY1Mi4xMCBDIDMzNS43NiA2NDIuNTUgMzM5LjAwIDYzNC42MCAzMzkuMzIgNjI1LjAwIEMgMzQwLjA5IDYxNC42OCAzMzUuNDggNjA2LjAzIDMzMy41OCA1OTYuMDIgQyAzMzEuNzYgNTg3LjYyIDMzMi40MSA1NzkuNTIgMzI4LjM1IDU3MS42NiBDIDMyNi4zNCA1NjguMzYgMzI0LjIzIDU2NC41MCAzMjEuMjAgNTYyLjA1IEMgMzIwLjI4IDU3MS40OSAzMTguNjQgNTgwLjQ2IDMxOS4wMSA1OTAuMDEgQyAzMTkuNjMgNjAwLjYyIDMzMC44MSA2MTAuMzIgMzMwLjA0IDYyMS4wMCBDIDMzMC43OSA2MjkuMTMgMzIzLjk1IDYzNS44OCAzMjMuNzUgNjQ0LjAwIEMgMzIzLjU5IDY0OS4zMyAzMjMuNTIgNjU0LjY3IDMyMy4xMCA2NjAuMDAgQyAzMjIuOTYgNjY5LjM1IDMyMS4yMyA2NzguNjUgMzIwLjk4IDY4OC4wMCBDIDMyMS4wMSA3MDEuNDMgMzI2LjIwIDcxMi4yMCAzMjkuMDMgNzI1LjAyIEMgMzMwLjkxIDczMi4yMCAzMzAuMTAgNzM5LjY4IDMyOS45MyA3NDYuOTkgQyAzMjguOTQgNzU0LjYxIDMyOC4wOCA3NjIuMzggMzI3LjEwIDc3MC4wMCBDIDMyNi45MyA3NzQuOTcgMzI2Ljk5IDc4MC4wMiAzMjcuMDYgNzg1LjAwIEMgMzI4LjAyIDc5MS43NCAzMjkuMDYgNzk4LjEyIDMyOC40MyA4MDUuMDEgQyAzMjguMDUgODA3LjUxIDMyNy42MCA4MDkuOTQgMzI0Ljk0IDgxMC44OSBDIDMxNS4wNyA4MTQuNzkgMzAyLjM4IDgxMy4zNSAyOTIuMDAgODEzLjI3IEMgMjg2Ljk5IDgxMy43NiAyODIuMDQgODEzLjUxIDI3Ny4wOCA4MTIuNzUgQyAyNzQuOTkgODEyLjQ2IDI3NC42NSA4MTAuNTMgMjczLjAwIDgxMC4wMCBDIDI2My4wOSA4MTIuMDYgMjUzLjA4IDgxMS45NiAyNDMuMDggODEwLjQ1IEMgMjQwLjY5IDgxMC4wNiAyMzcuNDIgODA4LjY3IDIzNi45MiA4MDYuMDMgQyAyMzcuNTQgODAzLjg1IDIzOS41MCA4MDMuMTIgMjQxLjQ5IDgwMi41NyBDIDI0Ni4yMCA4MDEuNDQgMjUxLjIwIDgwMS4wMyAyNTUuODggNzk5LjY5IEMgMjU5LjMzIDc5OC40NCAyNjIuNDMgNzk2LjU1IDI2NC4yOCA3OTMuMjggQyAyNjguNjMgNzg2LjA3IDI3MS43MiA3NzcuNDggMjcxLjY4IDc2OS4wMCBDIDI3MS4zOCA3NTcuMjAgMjY4LjgwIDc0NS45OSAyNjYuMjYgNzM0LjUzIEMgMjY0LjE4IDcyNC40NiAyNjAuODcgNzEzLjI5IDI2MS4xMyA3MDIuOTkgQyAyNjMuNTQgNjg4Ljk3IDI2MC43MSA2NzQuMTggMjYxLjcxIDY2MC4wMCBDIDI2MS45MyA2NDMuODkgMjYzLjkyIDYyOC4wNiAyNzQuMTUgNjE1LjAwIEMgMjcyLjAzIDYwMy4wNSAyNzEuMDUgNTkxLjAwIDI2OS4yMSA1NzkuMDEgQyAyNjguMTIgNTY2Ljg3IDI2Ny44NCA1NTUuMDIgMjcwLjI4IDU0My4wMCBDIDI3MC41NSA1NDEuNjkgMjcwLjk4IDU0MC40MiAyNzAuOTIgNTM5LjA4IEMgMjYxLjUwIDU0Mi4zOCAyNTEuODAgNTQ1LjA1IDI0Mi4wMSA1NDYuOTcgQyAyMzguOTkgNTQ3LjUzIDIzNi4xNyA1NDguMDAgMjMzLjIzIDU0Ni43NyBDIDIzMS4zOCA1NDUuMTAgMjMwLjY5IDU0Mi4zNyAyMzAuMjIgNTQwLjAxIEMgMjI5LjMyIDUzMS4zOCAyMzAuNjcgNTIzLjUyIDIzMS4wMiA1MTUuMDAgQyAyMzIuOTggNDk1LjA1IDIzNy43MiA0NzUuNzYgMjM5LjkxIDQ1Ni4wMCBDIDI0MC4wMSA0NTEuNjIgMjQwLjMzIDQ0NC45NiAyNDQuMDEgNDQxLjk2IEMgMjQ3LjMzIDQ0MS42NSAyNTIuNjYgNDQyLjM3IDI1My45NSA0NDYuMDIgQyAyNTQuNzMgNDUwLjE4IDI1My44OSA0NTMuODYgMjUzLjg5IDQ1OC4wMiBDIDI1NS41NiA0NTcuMjkgMjU3LjEzIDQ1Ni45MSAyNTguOTcgNDU3LjAwIEMgMjYwLjU4IDQ1Ny45MSAyNjAuMTUgNDYwLjA2IDI1OS44MCA0NjEuNTcgQyAyNTguMjEgNDY1Ljk4IDI1Ni40MSA0NzAuMzQgMjU0LjY1IDQ3NC42OSBDIDI1Mi43MyA0NzkuNjQgMjQ5LjgxIDQ4My42NyAyNDkuMTggNDg5LjA4IEMgMjQ4LjA0IDUwMC42NSAyNTIuMDIgNTEyLjcxIDI1Mi4wNCA1MjQuMzMgQyAyNTMuMzYgNTI0LjQ0IDI1NC42NiA1MjMuMzcgMjU1Ljc4IDUyMi43NiBDIDI2NS41OSA1MTYuNzEgMjc2Ljg5IDUxMS45MyAyODUuOTcgNTA0Ljg4IEMgMjg4LjIxIDUwMi42NiAyODkuMTkgNDk4Ljk3IDI5MC4zNyA0OTYuMTAgQyAyOTIuNDIgNDkwLjY1IDI4OS4zNyA0ODQuNzUgMjg3LjI5IDQ3OS43NSBDIDI4NC43NSA0NzYuNTcgMjgwLjExIDQ3OS40MCAyNzcuMDYgNDc2LjAxIEMgMjc0LjU2IDQ3Mi40MSAyNzUuMzggNDY2LjMzIDI3NC43MyA0NjIuMjMgQyAyNzMuMTQgNDU5LjU0IDI3MC4xMiA0NTcuODAgMjcyLjI4IDQ1NC4yOSBDIDI3NS40MCA0NTAuNjUgMjc1LjI2IDQ0Ni41MiAyNzUuODcgNDQyLjAwIEMgMjc2LjA4IDQzNi40MSAyNzcuOTYgNDMxLjAwIDI4Mi4xMCA0MjcuMTIgQyAyODcuMjUgNDIyLjg1IDI5NC4yNyA0MjAuNzcgMzAwLjkxIDQyMS41MCBaIiBmaWxsPSIjODE4MTgxIiAvPgo8cGF0aCBkPSJNIDE2MC4wNiA0MzUuMTcgQyAxNjMuNjkgNDM1LjM1IDE2NS45MyA0MzkuODMgMTY3LjM2IDQ0Mi42NyBDIDE3MC42MyA0NTAuMDcgMTcxLjMwIDQ1OS4wMCAxNzEuMDAgNDY3LjAwIEMgMTcwLjk5IDQ3OS42NiAxNzAuNjQgNDkxLjU4IDE3NC4yOCA1MDMuOTMgQyAxNzguMDMgNTE2LjE1IDE4MS43OSA1MjkuMTQgMTc5Ljk0IDU0Mi4wMSBDIDE3OS4wMSA1NDUuODkgMTc3LjYyIDU0OS44MiAxNzIuOTkgNTUwLjAzIEMgMTY0LjgxIDU0OC4xMyAxNTcuMTMgNTQ0Ljk5IDE0OS4wOSA1NDIuNjcgQyAxNDUuMzYgNTQxLjgxIDE0MS45MiA1NDAuNjUgMTM4LjAzIDU0MS4wNyBDIDEzOC45MyA1NDcuOTcgMTM5LjkxIDU1NS4wMiAxNDAuMDUgNTYyLjAwIEMgMTM5Ljc1IDU3NC45NSAxMzcuNzMgNTg3LjIyIDEzNi4xNiA2MDAuMDAgQyAxMzYuMDIgNjA0LjA0IDEzNC4yNCA2MDguMDEgMTM0LjA1IDYxMS45NyBDIDE0Mi4wNCA2MjIuMjkgMTQzLjcxIDYzNi40MSAxNDQuOTYgNjQ5LjAwIEMgMTQ1LjExIDY1OS43MyAxNDUuNzYgNjcwLjI4IDE0NC41MiA2ODAuOTkgQyAxNDMuOTAgNjg2LjgxIDE0NC40MiA2OTIuMjggMTQ1LjU5IDY5Ny45OSBDIDE0Ny4wNiA3MDUuMzAgMTQ0LjkwIDcxMS4zMCAxNDMuNDMgNzE4LjM0IEMgMTQxLjUwIDcyNy41NiAxMzkuNDEgNzM2LjcwIDEzNy44NCA3NDUuOTkgQyAxMzYuMjkgNzU0LjU1IDEzNC45OSA3NjMuMzAgMTM2LjAyIDc3Mi4wMSBDIDEzNi43OCA3NzkuNzEgMTM5Ljc0IDc4OC4wOSAxNDUuNDkgNzkzLjUwIEMgMTUwLjAzIDc5Ny40MSAxNTYuMzMgNzk3LjM4IDE2MS45MyA3OTguNDAgQyAxNjQuOTggNzk5LjIxIDE2Ny44NyA3OTkuNDUgMTcwLjA1IDgwMi4wMSBDIDE3MC4wNCA4MDIuOTggMTcwLjAzIDgwMy45NSAxNzAuMDMgODA0LjkxIEMgMTY4Ljc4IDgwNS44OCAxNjcuNjUgODA2LjgwIDE2Ni4wMyA4MDcuMDMgQyAxNTYuMDMgODA5LjEzIDE0NS4wNCA4MDcuNjEgMTM0Ljk1IDgwNi43MCBDIDEzMy42NiA4MDkuMTMgMTMyLjA2IDgwOS43NiAxMjkuMzIgODA5LjM0IEMgMTI5LjY3IDgwOC40OCAxMzAuMTkgODA3LjY4IDEzMC44NyA4MDYuOTYgQyAxMjkuODYgODA1LjU3IDEyOC40NCA4MDQuODggMTI2Ljk1IDgwNC4xMiBDIDEyMC41MSA4MDAuOTggMTE0LjM3IDc5Ny4yOSAxMDcuODggNzk0LjI0IEMgMTAzLjY2IDc5MS45NSA5OS43MyA3ODkuNzkgOTguMDggNzg0Ljk2IEMgOTcuMjQgNzY5LjIwIDk5Ljg0IDc1NC42NSAxMDEuMjAgNzM5LjAxIEMgMTAyLjI1IDczMy4yNSAxMDEuNzEgNzI3LjU2IDEwMy4yMCA3MjIuMDcgQyAxMDQuOTYgNzE2Ljc4IDEwOC40MiA3MTIuNDggMTA5LjgyIDcwNy4wMCBDIDExMC42NCA2OTYuMjUgMTEyLjY2IDY4NS41NSAxMTQuOTMgNjc1LjAxIEMgMTE3LjI1IDY2My41MSAxMTguODMgNjUxLjY5IDEyMC41NyA2NDAuMDggQyAxMjEuMjQgNjM1Ljc4IDEyMS42NCA2MzEuNzkgMTIzLjY4IDYyNy44NiBDIDEyNi4zMiA2MjIuMjUgMTMwLjY5IDYxNi45NyAxMzIuMjMgNjExLjAxIEMgMTM0LjMxIDYwMC43MSAxMzYuMjMgNTkwLjQ1IDEzNy41NSA1NzkuOTkgQyAxMzkuOTIgNTU4LjIwIDEzNy40NyA1MzUuNTQgMTI2Ljc5IDUxNi4wOSBDIDEyMy4wMCA1MDkuMDkgMTE3LjIzIDUwMS45MyAxMTUuOTUgNDk0LjAxIEMgMTE1LjA3IDQ4OC4zNSAxMTYuNzcgNDgzLjI0IDExOS4yNCA0NzguMjUgQyAxMjEuOTAgNDc0LjQ4IDEyNi43OSA0NzcuMjQgMTI5LjcwIDQ3My42OCBDIDEzMi42OSA0NjkuODUgMTI5LjQ1IDQ2My4yMyAxMzIuMDIgNDU5LjAwIEMgMTMyLjg3IDQ1Ny45NSAxMzQuNzcgNDU3Ljc2IDEzNi4wMSA0NTcuMjYgQyAxMzUuMzIgNDU4LjY0IDEzMy44MSA0NTkuNDggMTMzLjQwIDQ2MC45NSBDIDEzMy4yNyA0NjUuMzggMTMzLjc2IDQ3MS40NiAxMzEuMzYgNDc1LjM3IEMgMTI5Ljc1IDQ3Ny44NSAxMjYuNjQgNDc2Ljk1IDEyMy45NSA0NzcuOTQgQyAxMjAuNDkgNDc4LjczIDExOS4yMSA0ODIuOTEgMTE4LjQwIDQ4NS45MCBDIDExNi43NCA0OTMuNDkgMTE5LjI4IDUwMC4zOSAxMjMuMzAgNTA2LjczIEMgMTI0LjQxIDUwNy42NyAxMjUuNjUgNTA3LjY5IDEyNy4wMSA1MDguMDQgQyAxMzMuODggNTA5LjUxIDEzOS45NyA1MTIuNDIgMTQ2LjEwIDUxNS43NiBDIDE0OC4zOSA1MTcuMTggMTUwLjUyIDUxOC43NiAxNTMuMDMgNTE5LjgxIEMgMTU0LjQxIDUxOS4wOSAxNTUuMjMgNTE4LjQyIDE1NS43NiA1MTYuOTQgQyAxNTYuNjggNTEyLjE1IDE1Ni43NSA1MDcuMjggMTU3LjY2IDUwMi40OSBDIDE1OC4yMyA0OTcuMDIgMTU4LjkzIDQ5MS4zMyAxNTcuMTkgNDg2LjAwIEMgMTU1LjM5IDQ4MC40MiAxNTQuMzkgNDc0LjcxIDE1My4xOCA0NjkuMDAgQyAxNTIuMjEgNDY0LjQzIDE0OS4yNiA0NjAuNDAgMTQ3Ljg4IDQ1Ni4wNCBDIDE0Ny42MiA0NTQuMzIgMTQ2LjQzIDQ1MS44MiAxNDguOTQgNDUxLjI2IEMgMTUyLjA3IDQ1MS44NCAxNTQuMDQgNDU1LjUyIDE1NS43OCA0NTcuOTAgQyAxNTYuODQgNDUyLjA2IDE1My45MyA0NDcuMDcgMTU1LjU2IDQ0MS40OCBDIDE1Ni4xOSA0MzguODMgMTU2Ljk2IDQzNS43NyAxNjAuMDYgNDM1LjE3IFoiIGZpbGw9IiM4MTgxODEiIC8+CjxwYXRoIGQ9Ik0gMTYyLjMyIDQzOC4yMCBDIDE2NC4yMiA0NDAuMTYgMTY1LjIwIDQ0My4wNyAxNjYuMjIgNDQ1LjU2IEMgMTcwLjM5IDQ1OS45MiAxNjguOTQgNDczLjgzIDE2OS42OCA0ODguMDEgQyAxNjkuODQgNTAyLjUxIDE3Ny4wNyA1MTUuNjYgMTc4LjM4IDUzMC4wMCBDIDE3OS4wMSA1MzQuNDMgMTc5LjEyIDUzOS45NiAxNzcuMTUgNTQ0LjA0IEMgMTc1LjQxIDU0Ny4zNSAxNzIuNDEgNTQ3Ljg5IDE2OC45OCA1NDYuOTYgQyAxNjAuMjcgNTQ0LjQ5IDE1Mi4wNiA1NDEuNDcgMTQzLjA5IDUzOS43OCBDIDE0MS4wNCA1MzkuNTEgMTM5LjE1IDUzOS45NCAxMzcuMjQgNTM4Ljg4IEMgMTM0Ljk4IDUyOC42MCAxMzAuOTggNTE4LjgyIDEyNS4xNSA1MTAuMDMgQyAxMjYuNDQgNTA5Ljk4IDEyNy42OCA1MTAuMTcgMTI4Ljg4IDUxMC42MCBDIDEzNy4wMiA1MTMuMTggMTQ1LjE3IDUxNi45MCAxNTEuOTYgNTIyLjEzIEMgMTUyLjc5IDUyMi44OCAxNTMuNzcgNTIzLjE5IDE1NC45MCA1MjMuMDUgQyAxNTkuMTMgNTE2LjIyIDE1OC40NyA1MDkuMTkgMTU5LjMwIDUwMS40OSBDIDE1OS44MiA0OTguNjQgMTYwLjE0IDQ5NS45MCAxNjAuMDMgNDkzLjAwIEMgMTYwLjMyIDQ4OC4xMiAxNTguMjkgNDgzLjY1IDE1Ny4yMyA0NzguOTcgQyAxNTYuMzAgNDc0LjU2IDE1NS41NyA0NzAuMjIgMTU0LjMzIDQ2NS44NyBDIDE1My4yNiA0NjEuOTEgMTUwLjgwIDQ1OC4wNCAxNTAuMDIgNDU0LjAwIEMgMTUyLjc1IDQ1NS41MCAxNTQuMzQgNDU4LjgxIDE1NS40MyA0NjEuNTggQyAxNTcuMTQgNDYzLjczIDE1OC4wNiA0NjAuNDMgMTU4LjAyIDQ1OS4wNCBDIDE1OC41NSA0NTQuMjggMTU3LjE5IDQ0OS43OSAxNTcuMjUgNDQ1LjExIEMgMTU3Ljc4IDQ0NS43MSAxNTguMzEgNDQ2LjMyIDE1OC44NCA0NDYuOTMgQyAxNTguNjUgNDQ0LjMwIDE1Ny40NiA0NDEuNTAgMTU3LjkwIDQzOC45MSBMIDE1OC42OCA0MzkuMjEgQyAxNjAuNDIgNDQyLjU4IDE1OS44OCA0NDYuNDQgMTYxLjYxIDQ0OS43MyBDIDE2MS45NyA0NDYuNjYgMTYwLjcxIDQ0My44NyAxNjEuMDUgNDQwLjc5IEMgMTYzLjYxIDQ0Mi40MSAxNjIuNTIgNDQ3LjkxIDE2NC44MSA0NDkuOTUgQyAxNjQuMzUgNDQ1LjkxIDE2My41MCA0NDIuMDkgMTYyLjMyIDQzOC4yMCBaIiBmaWxsPSIjZmZmZmZmIiAvPgo8cGF0aCBkPSJNIDI0Ni4wOCA0NDQuMDYgQyAyNDYuODAgNDQ2LjUwIDI0NC40MiA0NDguOTUgMjQ1LjQ4IDQ1MS43NSBDIDI0NS44MCA0NDkuMTQgMjQ2LjQ1IDQ0Ni42MCAyNDYuOTYgNDQ0LjAyIEwgMjQ4LjA2IDQ0NC4xMyBDIDI0OS4wNiA0NDYuOTIgMjQ3Ljg5IDQ1MS4wMyAyNDcuMjMgNDUzLjkwIEMgMjQ5LjE5IDQ1MS41MiAyNDguMzggNDQ3Ljg0IDI0OC45NSA0NDUuMDkgTCAyNTAuMDYgNDQ1LjA3IEMgMjUxLjEyIDQ0Ny41NyAyNTAuMDkgNDUyLjAxIDI0OS4zOCA0NTQuNzIgQyAyNTEuMjQgNDUyLjg3IDI1MC41NyA0NDkuNDMgMjUwLjg5IDQ0Ni45OCBMIDI1MS44OSA0NDcuMTUgQyAyNTMuOTQgNDUxLjEyIDI1MS4yNSA0NTcuMTcgMjUxLjQzIDQ2MS41OCBDIDI1My41NiA0NjMuNjAgMjU1LjU1IDQ1Ny40NyAyNTcuOTQgNDU3LjcxIEMgMjU4LjAyIDQ1OS4zOCAyNTguMDcgNDYwLjkxIDI1Ny4zOSA0NjIuNDggQyAyNTQuOTEgNDY5LjQ1IDI1Mi4wMyA0NzYuMjUgMjQ4Ljc2IDQ4Mi44OCBDIDI0Ni44OCA0ODYuODYgMjQ3LjI5IDQ5MC43MyAyNDcuNDggNDk1LjAwIEMgMjQ4LjMyIDUwNS41OSAyNTAuNTkgNTE1Ljg2IDI1MC42MiA1MjYuNTUgQyAyNTEuNzIgNTI4LjIyIDI1Mi44NSA1MjYuNTkgMjUzLjk3IDUyNS45MyBDIDI2MC43OCA1MjAuODUgMjY4Ljg2IDUxNy43NCAyNzYuMDkgNTEzLjIyIEMgMjc4LjczIDUxMi4wNiAyODEuMjggNTA5LjUzIDI4NC4wNCA1MDkuMDEgQyAyODMuNTEgNTEwLjQ2IDI4Mi42MCA1MTEuNjIgMjgxLjc4IDUxMi45MiBDIDI3Ny4yNSA1MjAuMzYgMjc0LjIxIDUyOC4zOSAyNzEuNzUgNTM2LjcyIEMgMjYxLjI2IDU0MC44NCAyNTAuMTMgNTQzLjUwIDIzOS4wOSA1NDUuNjcgQyAyMzcuMjMgNTQ2LjA2IDIzNS4wMSA1NDUuNzMgMjMzLjc3IDU0NC4xNyBDIDIzMi4wMSA1NDEuNjYgMjMxLjg4IDUzOC45OCAyMzIuMDAgNTM2LjAwIEMgMjMxLjgxIDUxMC45NyAyMzcuNTcgNDg4LjYyIDI0MS4wMiA0NjQuMDAgQyAyNDEuMjQgNDU4LjMzIDI0MS45NiA0NTIuNTQgMjQzLjI2IDQ0Ny4wMiBDIDI0My44MSA0NDUuNTUgMjQzLjg3IDQ0My40MCAyNDYuMDggNDQ0LjA2IFoiIGZpbGw9IiNmZmZmZmYiIC8+CjxwYXRoIGQ9Ik0gMTI2Ljg2IDQ0OC43NyBDIDEyNS41NyA0NTAuOTkgMTI0LjExIDQ1MC41NSAxMjMuMjggNDQ4LjMzIEMgMTI0LjE4IDQ0Ni40NyAxMjcuMDcgNDQ2LjUzIDEyNi44NiA0NDguNzcgWiIgZmlsbD0iIzgxODE4MSIgLz4KPHBhdGggZD0iTSAyODcuNjEgNDUwLjM3IEMgMjg3LjA3IDQ1Mi45OSAyODMuODQgNDUyLjQ2IDI4My45OSA0NTAuMDYgQyAyODUuNDUgNDQ4LjE2IDI4Ni4zMCA0NDguNzUgMjg3LjYxIDQ1MC4zNyBaIiBmaWxsPSIjODE4MTgxIiAvPgo8cGF0aCBkPSJNIDEwOC42MyA0NTAuMzggQyAxMDkuMDcgNDUyLjcyIDEwNS43NyA0NTEuNDAgMTA0Ljc1IDQ1Mi43NSBDIDEwMi45OCA0NTQuNTAgMTA0LjQwIDQ1Ny42NyAxMDUuNTggNDU5LjQxIEMgMTA3LjExIDQ2MS43NCAxMDYuODYgNDY0LjExIDEwNy4zMCA0NjYuNzAgQyAxMDguNTggNDY4LjIwIDExMS41NSA0NjYuMjIgMTEzLjA5IDQ2NS44MSBDIDExNC4yNCA0NjkuMjQgMTA4LjE4IDQ2OS4yOCAxMDYuMjEgNDY4Ljg0IEMgMTA1LjQxIDQ2Ni42MSAxMDUuMDYgNDY0LjM5IDEwNC44MiA0NjIuMDQgQyAxMDMuOTIgNDU5Ljg0IDEwMS44NSA0NTcuNDQgMTAxLjk2IDQ1NS4wMCBDIDEwMS43NiA0NTEuODcgMTA1LjcwIDQ0OC44MSAxMDguNjMgNDUwLjM4IFoiIGZpbGw9IiM4MTgxODEiIC8+CjxwYXRoIGQ9Ik0gMzA1Ljk5IDQ1Mi4wMCBDIDMwNi43OSA0NTIuODggMzA3Ljk5IDQ1My42OSAzMDguMzEgNDU0Ljg1IEMgMzA5LjI5IDQ1Ny4yNSAzMDcuODIgNDU5LjY1IDMwNi43MSA0NjEuNzEgQyAzMDUuMTUgNDY0LjE1IDMwNS41OCA0NjcuMjYgMzA0LjY4IDQ2OS43MSBDIDMwMy40MCA0NzEuMzUgMjk3LjQzIDQ2OS45OCAyOTcuODkgNDY3Ljc3IEMgMjk5LjkzIDQ2OC41MyAzMDEuNTUgNDY5LjMwIDMwMy44MSA0NjguODggQyAzMDQuMDcgNDY2LjI3IDMwNC4xMCA0NjMuNzIgMzA1LjM4IDQ2MS4zOCBDIDMwNi4zNiA0NTkuMTkgMzA3Ljk3IDQ1Ny41NyAzMDYuOTYgNDU1LjAzIEMgMzA2LjMxIDQ1My4xNCAzMDMuNzggNDUzLjA2IDMwMi4xNSA0NTIuNzQgQyAzMDIuODIgNDUxLjQ2IDMwNC43OCA0NTIuMTQgMzA1Ljk5IDQ1Mi4wMCBaIiBmaWxsPSIjODE4MTgxIiAvPgo8cGF0aCBkPSJNIDg3Ljk0IDU1OC4wMiBDIDg4LjM0IDU1OC42NyA4OC42MSA1NTkuMzQgODguNzUgNTYwLjA0IEMgODkuODEgNTY2LjMzIDkwLjI3IDU3Mi42NiA5MC45NiA1NzkuMDAgQyA5MS4xMiA1ODIuNTMgOTEuOTEgNTg1LjY1IDkxLjQwIDU4OS4wMSBDIDkxLjA2IDU5My42MyA4OC40MCA1OTguMDggODYuMzAgNjAyLjEzIEMgODUuNjIgNjAzLjkxIDgzLjcxIDYwNC4yOSA4My4xMiA2MDYuMDYgQyA4MS40MSA2MTAuMDMgNzkuNzIgNjE0LjYzIDgwLjAxIDYxOS4wMCBDIDgwLjQ1IDYyNC4yMiA4Mi41MCA2MjguNTEgODQuODEgNjMzLjA4IEMgODcuODQgNjM5LjI4IDg2LjA2IDY0NS40MSA4Ni44NSA2NTIuMDEgQyA4Ny4xMiA2NjQuMzYgODguMzYgNjc2LjYwIDg4LjQ0IDY4OS4wMSBDIDg4LjEyIDcwMy40NCA4Mi4xMSA3MTUuNTUgODAuMzEgNzI5LjUwIEMgNzguMjcgNzQ2LjczIDg0Ljg5IDc2MS44NSA4My4xNyA3NzkuMDAgQyA4My4zOCA3ODYuNDcgODEuMzIgNzkzLjU3IDgxLjY5IDgwMS4wMSBDIDgxLjk1IDgwMi43NSA4MS44MCA4MDQuNTMgODMuMzEgODA1LjcwIEMgODUuODYgODA3LjI2IDg5LjAyIDgwOC4wNSA5Mi4wMCA4MDcuOTkgQyAxMDIuOTIgODA4LjYyIDExMy4wMyA4MDguOTcgMTIzLjk5IDgwOC42MiBDIDEyNS4zNyA4MDguNzAgMTI5LjMxIDgwNy40MSAxMjguNjMgODA5Ljk5IEMgMTIzLjA4IDgxMC4zNiAxMTcuNTUgODEwLjI5IDExMi4wMCA4MTAuMjcgQyAxMDMuMTAgODEwLjIzIDkzLjgzIDgxMC40MiA4NS4wNyA4MDguODAgQyA4MC40OSA4MDcuNjQgNzkuODYgODAzLjAyIDgwLjEwIDc5OS4wMSBDIDgwLjM4IDc5My42NyA4MC45MyA3ODguMzUgODEuMDkgNzgzLjAxIEMgODMuMzggNzY3LjI4IDc3LjQ4IDc1MC42OSA3Ny45NyA3MzUuMDAgQyA3Ny41NSA3MTkuNjIgODcuMDcgNzA1Ljk4IDg3LjAwIDY5MC4wMCBDIDg2Ljg1IDY4NC4zMyA4Ni42NSA2NzguNjQgODYuMDEgNjczLjAxIEMgODUuOTYgNjY3LjUwIDg1LjUxIDY2MS45MyA4NC44MiA2NTYuNDcgQyA4NS4zNiA2NTAuOTUgODQuMzggNjQ1LjUzIDg0LjY2IDY0MC4wMCBDIDg0LjgzIDYzMy44MSA3OS41MyA2MjguMjkgNzguNTUgNjIyLjI1IEMgNzcuMDQgNjE3LjIwIDc4Ljk1IDYxMS42MyA4MC43MyA2MDYuODcgQyA4MS4yNyA2MDUuMDcgODIuNDEgNjA0LjA1IDgzLjY0IDYwMi43MyBDIDg3LjAyIDU5Ni40MSA4OS44OSA1OTAuNDAgODkuMjEgNTgzLjAwIEMgODguODUgNTc1LjY5IDg4LjQxIDU2OC4xOSA4Ni43OCA1NjEuMDMgQyA4Mi40MCA1NjYuMjcgNzguMDcgNTcyLjU0IDc3LjMwIDU3OS40OCBDIDc2LjE4IDU4OC40MSA3NS41NiA1OTcuMDEgNzEuMTEgNjA1LjA4IEMgNjkuMDEgNjEwLjQ4IDY4LjQxIDYxNS40NSA2Ny40MiA2MjAuOTkgQyA2Ny4zMyA2MzIuNjcgNzIuNjIgNjQyLjEzIDcxLjAwIDY1My45NSBDIDY5LjA1IDY1NC44OCA2OC4wNiA2NTUuMDIgNjYuMTIgNjUzLjkzIEMgNjQuODEgNjUyLjc1IDY0LjM3IDY1MC42NyA2Mi45MiA2NDkuNzIgQyA2MC40NyA2NTYuNDUgNTYuNjMgNjYzLjY5IDUwLjAwIDY2Ny4xMiBDIDQ2LjEzIDY2OC45NiA0Mi4zMCA2NjYuODAgNDAuNjUgNjYzLjE1IEMgMzguMjcgNjU5LjQ3IDM4LjY3IDY1NS4yNCAzOS4wOSA2NTEuMDYgQyA0MS4zNSA2NDYuNDUgNDMuNjYgNjQyLjAxIDQ1LjA5IDYzNy4wNSBDIDQ3Ljc2IDYyOC42OCA1MC45NyA2MjAuOTYgNTEuNjggNjEyLjA4IEMgNTIuMDcgNjA1Ljg0IDUzLjA3IDU5OS43MSA1My4wMyA1OTMuNDUgTCA1NC41OSA1OTMuMTIgQyA1NS40OSA1OTcuMjMgNTQuNDEgNjAxLjgyIDU0LjI1IDYwNi4wMCBDIDUzLjk1IDYxMy41NyA1Mi4yMiA2MjAuNzggNTAuMTkgNjI4LjAzIEMgNDguMDcgNjM1LjI2IDQ1LjU0IDY0Mi4zMyA0Mi4yMiA2NDkuMDkgQyA0MS4wNSA2NTEuNDMgMzkuNjUgNjUzLjMwIDQwLjAzIDY1Ni4wOCBDIDQxLjY5IDY1NC45MyA0Mi41MSA2NTIuMzQgNDMuNjQgNjUwLjY2IEMgNDQuNjcgNjQ5LjMzIDQ0Ljc0IDY0Ni43MCA0Ni45MSA2NDcuMDYgQyA0NS4xNiA2NTAuODQgNDIuOTcgNjU0LjM2IDQxLjAxIDY1OC4wMiBDIDQwLjkwIDY1OS4yMyA0MS4yMyA2NjAuMDcgNDIuMDAgNjYwLjUxIEMgNDQuMzMgNjU3LjIxIDQ2LjQ1IDY1My43MSA0OC4wOCA2NTAuMDAgQyA0OC42NSA2NDkuNzYgNDkuMjMgNjQ5LjUyIDQ5LjgwIDY0OS4yOCBDIDQ4LjQwIDY1My44NyA0NC45MSA2NTcuODQgNDIuOTggNjYyLjAzIEMgNDIuOTggNjYzLjQ3IDQ0Ljk4IDY2NC4zNCA0Ni4wMSA2NjUuMDMgQyA0Ny40MyA2NjIuMDAgNDguODkgNjU5LjU3IDUxLjAwIDY1Ni45NyBDIDUyLjA4IDY1NS43MiA1My4xNyA2NTMuMDYgNTUuMDEgNjUzLjAzIEMgNTMuNjcgNjU3LjE5IDQ4LjUxIDY2MC42MSA0Ny45NyA2NjQuOTcgQyA0OS41MSA2NjUuMDYgNTAuNDQgNjY0LjU1IDUxLjYxIDY2My42NCBDIDU2LjQ5IDY2MC40MCA1OS42MiA2NTQuNTQgNjAuODAgNjQ4LjkxIEMgNjEuMzAgNjQ2LjgyIDYwLjU4IDY0NC4zMiA2MS4zNiA2NDIuMzQgQyA2My4zMiA2NDEuMDcgNjMuMjIgNjQzLjkwIDYzLjg2IDY0NS4wNSBDIDY0LjU0IDY0Ny41MiA2Ni41MiA2NTIuMDYgNjkuNjUgNjUxLjcyIEMgNjkuODUgNjQyLjU3IDY2LjA5IDYzMy4yNSA2NS43OSA2MjQuMDkgQyA2NS4zNCA2MTcuNjQgNjcuMTIgNjEyLjA3IDY4LjgzIDYwNS45OCBDIDcxLjk2IDU5OS4xNiA3My40NCA1OTEuMzcgNzQuODAgNTgzLjk5IEMgNzUuNjcgNTc1LjU0IDc5LjA4IDU2Ny45MCA4NC4xOSA1NjEuMTkgQyA4NS4wOSA1NjAuMjEgODYuMzUgNTU3LjQzIDg3Ljk0IDU1OC4wMiBaIiBmaWxsPSIjODE4MTgxIiAvPgo8cGF0aCBkPSJNIDM1NS4xNCA1ODcuOTQgQyAzNTQuNzAgNTkzLjIyIDM1Ni4yMSA1OTguNzEgMzU1LjU5IDYwMy44OCBMIDM1NC4wMSA2MDMuNTUgQyAzNTMuOTAgNTk4Ljg3IDM1My42NiA1OTQuMTYgMzUzLjAyIDU4OS41MSBDIDM1My4zOSA1ODguNTQgMzU0LjEwIDU4OC4wMiAzNTUuMTQgNTg3Ljk0IFoiIGZpbGw9IiM4MTgxODEiIC8+CjxwYXRoIGQ9Ik0gMTA2Ljg1IDU5NC4xMyBDIDEwOS4zMSA1OTYuMTEgMTExLjc0IDU5OC45NSAxMTMuMDQgNjAxLjc5IEwgMTExLjc4IDYwMy4wNyBDIDEwOS40NiA2MDEuNzAgMTA2LjIxIDYwMS4zNSAxMDUuNTcgNTk4LjM2IEMgMTA0LjY4IDU5Ni44OSAxMDMuMzkgNTk3LjIxIDEwMS45MSA1OTYuODggQyAxMDIuODQgNTk0Ljk0IDEwNC41MyA1OTMuMzAgMTA2Ljg1IDU5NC4xMyBaIiBmaWxsPSIjODE4MTgxIiAvPgo8cGF0aCBkPSJNIDMwNS4wMCA1OTYuODIgQyAzMDUuMDIgNTk3LjgxIDMwNS4wMyA1OTguODEgMzA1LjAzIDU5OS44MSBDIDMwMy4wMiA2MDEuODggMzAxLjI1IDYwMy42OSAyOTguMzkgNjA0LjUyIEMgMjk4LjIwIDYwNS4zNyAyOTguMDAgNjA2LjIxIDI5Ny44MSA2MDcuMDUgQyAyOTUuNzEgNjA2LjQzIDI5NC45NyA2MDMuNzMgMjk1LjY5IDYwMS45OSBDIDI5Ny42OCA1OTkuNDYgMzAwLjEwIDU5OC45OCAzMDIuOTIgNTk3LjkzIEMgMzAzLjYwIDU5Ny41MyAzMDQuMjkgNTk3LjE1IDMwNS4wMCA1OTYuODIgWiIgZmlsbD0iIzgxODE4MSIgLz4KPHBhdGggZD0iTSAxMzMuNjYgNjE1LjI3IEMgMTQxLjMxIDYyNi44OCAxNDIuNjggNjQwLjQ3IDE0My42MSA2NTQuMDAgQyAxNDQuNTUgNjY0LjM3IDE0My41MCA2NzUuMTQgMTQyLjcyIDY4NS41MiBDIDE0MS43NSA2OTAuOTQgMTQzLjE0IDY5NS43MiAxNDMuOTMgNzAxLjAwIEMgMTQ0Ljg4IDcxMi43MyAxNDAuMDcgNzI1LjQ4IDEzNy43MSA3MzYuOTYgQyAxMzYuMTUgNzQ1LjAzIDEzNC4xMCA3NTMuMjkgMTMzLjcyIDc2MS41MSBDIDEzMi45NSA3NzEuODUgMTM1Ljc0IDc4Mi4xNiAxNDEuMTAgNzkwLjk5IEMgMTQyLjQ5IDc5My4yNyAxNDQuMDUgNzk1LjMyIDE0Ni40NyA3OTYuNTggQyAxNTMuMDYgODAwLjE4IDE2MC44MSA3OTguNzggMTY3LjY0IDgwMi4zNSBDIDE2OC43MyA4MDMuNjYgMTY3LjAxIDgwNC41NSAxNjUuOTMgODA0Ljg1IEMgMTYwLjkwIDgwNi4yMCAxNTUuMTYgODA1Ljk5IDE1MC4wMCA4MDUuOTkgQyAxNDQuNjUgODA2LjA4IDEzOS4yNCA4MDUuNzAgMTMzLjk5IDgwNC42NyBDIDEyOS43MiA4MDMuNTIgMTI1LjY1IDgwMS4yOCAxMjEuNjggNzk5LjMzIEMgMTE5LjI0IDc5Ny45OCAxMTYuODcgNzk3LjI3IDExNS43NSA3OTQuNDQgQyAxMTUuMjcgNzk0LjY4IDExNC43OSA3OTQuOTIgMTE0LjMxIDc5NS4xNyBDIDExMS43MSA3OTQuMTAgMTA5LjQ1IDc5Mi40MCAxMDYuOTEgNzkxLjE2IEMgMTA0LjU1IDc4OS44NCAxMDEuNTggNzg4LjI0IDEwMC4yMyA3ODUuODAgQyA5OS4xNiA3ODEuMTAgMTAwLjE1IDc3Ni43NSAxMDAuMTAgNzcyLjAwIEMgMTAxLjMxIDc1Ni4zNCAxMDMuMjggNzQwLjYyIDEwNC40MyA3MjUuMDggQyAxMDUuNTAgNzIxLjUzIDEwNy45NCA3MTguNTUgMTA5LjE2IDcxNS4wMiBDIDExMi40MSA3MDcuNDAgMTEyLjgwIDY5OS42NiAxMTMuODMgNjkxLjUzIEwgMTE0LjU1IDY5MS4xOCBDIDExNi4yMSA2OTQuMzkgMTE3Ljg4IDY5Ny41NyAxMTkuMjAgNzAwLjkzIEMgMTIwLjQ3IDcwMy42MSAxMTkuOTMgNzA3LjEwIDExOS45NCA3MTAuMDEgQyAxMTguNTIgNzE4LjU2IDExMy4yNSA3MjYuODggMTExLjI5IDczNS4wNiBDIDExMC40MiA3NDAuMTYgMTExLjM1IDc0Ni4wNSAxMTEuODAgNzUxLjA5IEMgMTEyLjMyIDc2Mi4yMiAxMTUuNTQgNzczLjA3IDExNC45OCA3ODQuMDAgQyAxMTUuMTAgNzg3LjMwIDExNC4wMSA3OTAuNTggMTE0LjM3IDc5My44NCBMIDExNS45NCA3OTMuNTIgQyAxMTYuMTkgNzg4Ljk3IDExNy4xMiA3ODQuNTggMTE3LjAyIDc4MC4wMSBDIDExNy40OCA3NzAuNzkgMTE0LjU2IDc2MS4yMSAxMTMuODIgNzUyLjAxIEMgMTEyLjk1IDc0Ni42OCAxMTIuODcgNzQxLjM3IDExMy4wNiA3MzUuOTggQyAxMTMuNzcgNzMwLjQxIDExNi42MCA3MjYuMDQgMTE4LjQ2IDcyMC4zOSBDIDEyMC43OSA3MTQuMjIgMTIzLjA5IDcwOC44NiAxMjEuNzAgNzAyLjA4IEMgMTIwLjAwIDY5Ni44MSAxMTcuMDggNjkyLjM0IDExNC42NSA2ODcuNDcgQyAxMTQuNTggNjgzLjg4IDExNS45MCA2ODAuMDggMTE2LjQ4IDY3Ni41MyBDIDExOS42MiA2NjIuNjQgMTIxLjI0IDY0OC41NSAxMjMuMzggNjM0LjUwIEMgMTI0LjczIDYyNy44NCAxMjguOTYgNjIzLjYyIDEzMS42MiA2MTcuNjggQyAxMzIuMTIgNjE2Ljk1IDEzMi4zOCA2MTQuNzIgMTMzLjY2IDYxNS4yNyBaIiBmaWxsPSIjZmZmZmZmIiAvPgo8cGF0aCBkPSJNIDI3NC4xNCA2MTcuOTMgTCAyNzUuMDcgNjE3LjY0IEMgMjc3LjA1IDYyMi4wMiAyNzkuNTQgNjI1Ljg1IDI4Mi4yMyA2MjkuODAgQyAyODQuNzggNjMzLjU0IDI4NC44OSA2MzcuNjQgMjg1Ljc3IDY0Mi4wMSBDIDI4Ny41NSA2NTcuMTUgMjkwLjM1IDY3Mi4xMSAyOTMuNTQgNjg3LjAxIEMgMjkzLjgyIDY4Ny45NiAyOTMuNzYgNjg4Ljc3IDI5My4zNSA2ODkuNDQgQyAyOTAuNTYgNjk0LjA5IDI4Ny45OSA2OTguOTQgMjg1Ljg5IDcwMy45NCBDIDI4NC42MSA3MDYuODAgMjg0LjQ3IDcwOS45NCAyODQuOTIgNzEzLjAwIEMgMjg2LjIxIDcyMi40MSAyOTEuOTkgNzMwLjMzIDI5My42MCA3MzkuMjYgQyAyOTQuMzkgNzQzLjM5IDI5My44NCA3NDcuODAgMjkzLjk5IDc1Mi4wMCBDIDI5Mi45NCA3NjUuNTIgMjg4LjMyIDc4MC42MCAyOTAuNjEgNzkzLjk5IEMgMjkwLjgxIDc5Ni4yOCAyOTAuNjMgNzk5LjEzIDI4OC43MiA4MDAuNzIgQyAyODUuODUgODAzLjI3IDI4MS41MiA4MDQuNTAgMjc4LjA3IDgwNi4xMyBDIDI3MS44MCA4MDkuMjIgMjYzLjg5IDgwOS42MCAyNTcuMDAgODA5Ljk1IEMgMjUxLjQ2IDgwOS4zNCAyNDUuNTIgODA5LjQ2IDI0MC4yMCA4MDcuNzMgQyAyMzkuMjcgODA1Ljc4IDI0MS42MSA4MDQuNzMgMjQzLjA2IDgwNC4xOCBDIDI0OS42MSA4MDIuMTAgMjU3Ljc5IDgwMy4xOCAyNjMuMDEgNzk3LjkyIEMgMjY5LjA4IDc5MS44MSAyNzIuMTQgNzgyLjM5IDI3My4xOCA3NzQuMDAgQyAyNzQuNjAgNzU1LjEyIDI2OC4xMiA3MzQuMzQgMjY0LjE5IDcxNS45MSBDIDI2Mi44NiA3MTEuNjUgMjYyLjY3IDcwNy40MiAyNjMuMzUgNzAzLjAxIEMgMjY1LjYzIDY5MS45MCAyNjIuOTEgNjc5LjM0IDI2My4yOCA2NjguMDAgQyAyNjMuNTkgNjYxLjM1IDI2My43NCA2NTQuNjQgMjY0LjMzIDY0OC4wMSBDIDI2NS41NyA2MzcuOTIgMjY4LjAwIDYyNi4yNCAyNzQuMTQgNjE3LjkzIFoiIGZpbGw9IiNmZmZmZmYiIC8+CjxwYXRoIGQ9Ik0gMjk0LjM0IDY5MS41NCBMIDI5NC40NyA2OTEuNDAgQyAyOTYuMDUgNzAxLjI3IDI5NS43MyA3MDkuODUgMjk5LjU1IDcxOC4zOSBDIDMwMC44MCA3MjEuOTYgMzAzLjMwIDcyNS4yOSAzMDQuMDEgNzI4Ljk4IEMgMzA1LjQzIDc0Ny45MCAzMDcuODYgNzY1Ljk1IDMwOC43MyA3ODUuMDAgQyAzMDguNTEgNzg3LjQ5IDMwNy42NyA3OTAuMDAgMzA1LjQ3IDc5MS40MSBDIDMwMS41NCA3OTQuMTEgMjk3LjI3IDc5Ni4zMiAyOTMuMjAgNzk4Ljc5IEMgMjkyLjEzIDc5MS41MiAyOTEuNjEgNzg0LjM0IDI5Mi4yMyA3NzcuMDEgQyAyOTMuNDggNzcwLjcxIDI5NC4xNSA3NjQuMzYgMjk0LjkxIDc1Ny45OSBDIDI5NS4xMyA3NTAuMjYgMjk3LjAyIDc0My42MiAyOTUuMTAgNzM1Ljk5IEMgMjkyLjUwIDcyNy40OCAyODcuMjUgNzE4LjkyIDI4Ni4zNCA3MTAuMDAgQyAyODYuMjMgNzAzLjYyIDI5MS4zMyA2OTYuOTggMjk0LjM0IDY5MS41NCBaIiBmaWxsPSIjZmZmZmZmIiAvPgo8L3N2Zz4=', - - }, - ], + [ + { + imageSrc: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4w', + name: 'epaule-3aa10b33-78f8-4b5f-9cba-7dacff042e3c.svg', + originalName: 'Epaule.svg', + size: 41978, + storage: 'base64', + type: 'image/svg+xml', + url: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCA0MjggODMxIiB3aWR0aD0iNDI4LjBwdCIgaGVpZ2h0PSI4MzEuMHB0Ij4KPHBhdGggZD0iTSAwLjAwIDAuMDAgTCA0MjguMDAgMC4wMCBMIDQyOC4wMCA4MzEuMDAgTCAwLjAwIDgzMS4wMCBMIDAuMDAgMC4wMCBaIiBmaWxsPSIjZmZmZmZmIiAvPgo8cGF0aCBkPSJNIDEyNC4xMSAxMy44MyBDIDEyOC4wMCAxNy4xMSAxMjkuODUgMjEuNTUgMTMwLjc2IDI2LjQ1IEMgMTMyLjE5IDI5LjA5IDEzMy44NSAzMC42NiAxMzMuNTAgMzQuMDAgQyAxMzIuODggNDEuNDAgMTI3LjE1IDQ4LjI5IDEyNC4wOCA1NS4wMCBDIDEyMy45MyA1OS4wNSAxMjMuNjcgNjIuNzcgMTI1LjUxIDY2LjUyIEMgMTI3LjEzIDcwLjQ1IDEzMS4yMSA3Mi42MyAxMzUuMTIgNzMuNjcgQyAxNDIuNzEgNzYuMDMgMTUwLjA5IDc3LjQ0IDE1Ni4zNCA4Mi42NiBDIDE2My41NSA4OC43MyAxNjYuMTYgOTcuODggMTY2LjQ2IDEwNy4wMCBDIDE2Ni43NyAxMTcuMzIgMTY2LjQ2IDEyNy42OSAxNjYuNjAgMTM4LjAyIEMgMTY2LjgyIDE0MC45NyAxNjcuMDQgMTQzLjc2IDE2OC42NSAxNDYuMzQgQyAxNzQuNjIgMTU2Ljk1IDE4MC41OSAxNjguNjQgMTgxLjQ0IDE4MC45OSBDIDE4Mi4xNCAxODUuNzMgMTgxLjY4IDE5MC4zNSAxODMuMjAgMTk0Ljk2IEMgMTg0LjMwIDE5OS42MCAxODcuNDIgMjAyLjMwIDE5MC4xNSAyMDUuOTcgQyAxOTQuNjIgMjExLjcxIDIwMC4xMyAyMTYuODUgMjAzLjkwIDIyMy4wNyBDIDIwNC4xOCAyMjQuOTEgMjA0Ljc4IDIyOC4xMCAyMDIuMDAgMjI4LjEyIEMgMTk5LjA4IDIyNy4yMiAxOTcuNDkgMjI1LjEzIDE5NS4yNiAyMjMuMjIgQyAxOTQuODQgMjI1LjU5IDE5NS42MiAyMjcuNjEgMTk1LjgxIDIyOS45OCBDIDE5Ni4yMCAyMzMuMjcgMTk1LjcwIDIzNi42MyAxOTQuNzEgMjM5Ljc3IEMgMTkzLjU1IDI0Mi45MyAxOTAuNjUgMjQ2LjIzIDE4Ny4wMSAyNDYuMDYgQyAxODIuMjggMjQ2LjU1IDE3OC4zOCAyNDIuMTAgMTc2Ljc5IDIzOC4xMSBDIDE3My45OCAyMzAuODQgMTc0LjY4IDIyMi4xNSAxNzMuNjkgMjE0LjQ5IEMgMTcyLjYwIDIwOC42MiAxNjkuNDkgMjAzLjE0IDE2Ny4xOSAxOTcuNjAgQyAxNjIuMzUgMTg3LjA2IDE1Ny43NCAxNzYuMzMgMTUzLjYzIDE2NS40OCBDIDE1MC4wOSAxNTUuMDcgMTQ5LjI1IDE0My44OSAxNDguNTIgMTMyLjk5IEMgMTQ4LjUyIDEzMS42OCAxNDguMTIgMTMxLjAxIDE0Ny4zMCAxMzAuOTcgQyAxNDYuMDEgMTM4LjM4IDE0My4zNiAxNDUuNTYgMTQyLjIwIDE1My4wMCBDIDE0MC42MCAxNjguNjMgMTQ2LjM4IDE4Mi44NCAxNDguMjkgMTk4Ljk5IEMgMTQ5LjY4IDIwNi45NCAxNTAuMzggMjE0Ljk0IDE1MC45MCAyMjMuMDAgQyAxNTEuMTYgMjM1Ljg1IDE0OC41MSAyNDkuMTkgMTQ1LjU5IDI2MS42OCBDIDE0NC4wMSAyNjcuNjYgMTQyLjA2IDI3NC4zMiAxMzguMzcgMjc5LjM3IEMgMTM2LjQxIDI4MS40OSAxMzcuNzYgMjg1LjM5IDEzNy44NSAyODguMDQgQyAxMzguMDQgMjk0Ljc1IDEzNS43NiAzMDIuMzEgMTM3LjkzIDMwOS4wMSBDIDEzOS45MSAzMTUuMzkgMTQyLjUxIDMyMS4xNiAxNDIuNTkgMzI4LjAwIEMgMTQzLjEzIDM0MS40MSAxMzUuMzQgMzU0LjQyIDEzMS4zOCAzNjcuMDkgQyAxMzAuMjYgMzcyLjIyIDEyOS42NCAzNzcuNTIgMTMxLjQ0IDM4Mi41OCBDIDEzMy4zMSAzODYuNTAgMTM3LjM3IDM4OC42NSAxMzkuNDQgMzkyLjU2IEMgMTQwLjIwIDM5NC4xMyAxNDIuMDYgMzk2LjU5IDE0MC4wMCAzOTcuOTMgQyAxMzUuNzEgNDAwLjY1IDEyOS44NSAzOTkuOTggMTI0Ljk5IDM5OS45OCBDIDEyMi41OCAzOTkuNjAgMTE5LjM2IDM5OS4yMCAxMTcuNTIgMzk3LjQ4IEMgMTE0Ljk2IDM5NC4zNyAxMTMuNjUgMzkwLjAxIDExMy45NyAzODYuMDAgQyAxMTMuMzEgMzc2LjUwIDExOC4xMiAzNjcuODcgMTE3LjIwIDM1OC4wMCBDIDExNy4wMyAzNTEuMjQgMTE1LjEzIDM0NC43NCAxMTQuODUgMzM4LjAwIEMgMTEzLjY5IDMyNy44NiAxMTcuODkgMzE3LjQ5IDExNy4zMyAzMDcuMDAgQyAxMTYuOTggMzAyLjk4IDExNi45MCAyOTguOTggMTE2LjIwIDI5NS4wMCBDIDExNC45MSAyODEuMjkgMTE1LjIyIDI2Ny42NCAxMTQuMTIgMjUzLjk5IEMgMTEzLjgyIDI0My44NiAxMTIuODcgMjMzLjgwIDExMi41MCAyMjMuNjMgQyAxMTEuOTIgMjM1LjM2IDExMS4xMiAyNDcuMzAgMTEwLjEwIDI1OS4wMCBDIDEwOS43OCAyNzAuMjEgMTA5LjE3IDI4MS43OSAxMDkuMTYgMjkzLjAwIEMgMTA4LjI0IDI5Mi45MyAxMDcuNDcgMjkyLjQ1IDEwNi44NSAyOTEuNTQgQyAxMDcuOTIgMjgzLjgyIDEwNy45NCAyNzUuNzkgMTA3Ljk3IDI2OC4wMCBDIDEwOC40MyAyNTAuNzkgMTEwLjQ0IDIzMy4zNSAxMTAuODkgMjE2LjI1IEMgMTA5LjAxIDIxNS4xOCAxMDcuMjAgMjE0LjIxIDEwNS4wNiAyMTMuNzggQyAxMDQuMjkgMjEzLjcyIDEwMy45MCAyMTMuMTAgMTAzLjg4IDIxMS45MCBDIDEwNi45OCAyMTIuMTcgMTA5LjkxIDIxMy45OCAxMTIuOTIgMjE0Ljk2IEMgMTEzLjU2IDIxNC4zOSAxMTQuMjQgMjE0LjA2IDExNC45NiAyMTMuOTkgQyAxMTcuMzMgMjEzLjk3IDExOS4zMSAyMTIuNTcgMTIxLjQxIDIxMS40NiBDIDEyMS43OSAyMTAuNDMgMTIyLjM2IDIwOS45MCAxMjMuMTEgMjA5Ljg3IEMgMTIzLjg0IDIxMy43NCAxMTYuOTIgMjE1LjU4IDExNC4xNCAyMTYuMzMgQyAxMTMuNjIgMjIzLjI5IDExNC40MCAyMzAuMDggMTE0LjkzIDIzNy4wMCBDIDExNS4wMyAyNDEuNjkgMTE1LjU3IDI0Ni4zMyAxMTUuOTggMjUxLjAwIEMgMTE1Ljk3IDI1Ny4wMyAxMTYuNzEgMjYzLjAwIDExNi45MiAyNjkuMDIgQyAxMTcuMTggMjgyLjM1IDExOC4zNSAyOTUuNzIgMTE5LjIyIDMwOS4wMCBDIDExOS42OSAzMTYuNjYgMTE2Ljg5IDMyMy40NCAxMTYuNjcgMzMxLjAwIEMgMTE2LjQyIDMzOS4yMiAxMTcuNjUgMzQ2Ljk1IDExOC45MiAzNTUuMDAgQyAxMTkuNzYgMzYyLjkwIDExNy4xNCAzNzEuMTUgMTE2LjA2IDM3OS4wMCBDIDExNi4xMSAzODQuOTEgMTE1LjA1IDM5MC4yNCAxMTguNjQgMzk1LjM4IEMgMTIyLjI0IDM5OS4zNSAxMjguMjMgMzk4LjMzIDEzMy4wMSAzOTguMDAgQyAxMzQuODAgMzk4LjA3IDEzNy41NiAzOTYuODcgMTM3Ljk5IDM5NC45OSBDIDEzNi40MiAzOTAuMzkgMTMxLjQ4IDM4Ny41NiAxMjkuOTUgMzgyLjk5IEMgMTI4LjUzIDM3OS4zMSAxMjguOTkgMzc0Ljk2IDEyOS4wOCAzNzEuMDAgQyAxMzAuOTQgMzU3Ljc2IDEzOC41MSAzNDYuNjQgMTQwLjI5IDMzMy4wMSBDIDE0MS40NCAzMjYuMjkgMTM5LjYyIDMxOS45NSAxMzcuMzcgMzEzLjY5IEMgMTM1LjUwIDMwOC41NyAxMzUuNjAgMzAzLjM2IDEzNS43OCAyOTguMDAgQyAxMzYuMjMgMjkyLjAyIDEzNS44NCAyODUuOTggMTM2LjAzIDI4MC4wMSBDIDEzNi40OSAyNzguNTggMTM3LjMyIDI3Ny4yNyAxMzguMTEgMjc2LjAwIEMgMTQxLjA2IDI3MS41OSAxNDIuNDUgMjY2LjAyIDE0NC4wNSAyNjEuMDAgQyAxNTEuNjAgMjMzLjY1IDE0OC41NSAyMDUuMjggMTQyLjM3IDE3OC4wMCBDIDE0MC43NiAxNjguOTUgMTM4LjMzIDE1OS4wMSAxNDAuODEgMTQ5Ljk2IEMgMTQzLjI4IDEzOS42MyAxNDYuMzcgMTI5LjU1IDE0Ny44OCAxMTkuMDAgQyAxNDcuNzcgMTE3Ljg5IDE0OC40NSAxMTcuNTQgMTQ5LjkzIDExNy45NSBDIDE0OS4yMiAxMjYuNTMgMTUwLjIzIDEzNS4zNyAxNTEuMjIgMTQzLjkxIEMgMTUyLjM4IDE1MC40MiAxNTMuMDkgMTU2LjY0IDE1NC45MCAxNjMuMDAgQyAxNTcuNDggMTcyLjA4IDE2MS45NyAxODAuODUgMTY1LjY0IDE4OS40NiBDIDE2OS45OCAxOTkuMTkgMTc1LjE2IDIwOC4xMyAxNzYuMDIgMjE5LjAwIEMgMTc1LjgzIDIyNC41NSAxNzYuMzIgMjMwLjA1IDE3Ny40OCAyMzUuNDggQyAxNzguMDggMjM3LjI0IDE3OS4wNCAyNDAuNTggMTgxLjA1IDI0MS4xMyBDIDE4MS42MCAyMzYuOTYgMTc5Ljg1IDIzMi4zMCAxODAuMjIgMjI4LjM3IEwgMTgwLjc5IDIyOC4xMyBDIDE4MS4yMyAyMzIuNzYgMTgxLjg0IDIzNy4zNCAxODEuOTIgMjQyLjAwIEMgMTgyLjE0IDI0My4yOSAxODMuODAgMjQ0LjIwIDE4NS4wNSAyNDMuOTcgQyAxODYuMzAgMjM5LjMxIDE4Mi41NiAyMzMuMTcgMTg0LjY4IDIyOS4yMCBDIDE4NC45OCAyMzQuMTUgMTg1Ljc5IDIzOS4wNCAxODUuODAgMjQ0LjAxIEMgMTg1Ljk2IDI0NC44NyAxODYuNjcgMjQ1LjE2IDE4Ny45NCAyNDQuOTAgQyAxODkuNTIgMjQwLjI2IDE4OC4wNyAyMzUuOTkgMTg4LjE2IDIzMS4yNiBMIDE4OC43NyAyMzEuMjkgQyAxODguNzggMjMyLjg0IDE4OC44MiAyMzQuMzYgMTg5LjE0IDIzNS44OCBDIDE4OS43MiAyMzguNDYgMTg4LjkzIDI0MS4wNSAxODkuNzQgMjQzLjUyIEMgMTkxLjc5IDI0My4wNyAxOTIuNDUgMjQwLjc3IDE5My4wMSAyMzkuMDIgQyAxOTQuNzUgMjMzLjU0IDE5My42MiAyMjcuNzAgMTkyLjY0IDIyMi4xMyBDIDE5Mi41NCAyMjAuOTEgMTkwLjk4IDIxOC43NyAxOTMuMDQgMjE4LjYxIEMgMTk1LjY1IDIyMC4xNCAxOTcuNzQgMjI0LjE0IDIwMC45NSAyMjUuMDcgQyAyMDMuMDQgMjI1LjA4IDIwMS41NCAyMjIuODQgMjAwLjg4IDIyMi4wNCBDIDE5Ni42MCAyMTYuMzggMTkxLjgyIDIxMS4yNSAxODcuMDkgMjA1Ljk4IEMgMTgwLjQ2IDE5OC45MiAxODAuNjcgMTg4LjAxIDE3OS40MyAxNzguOTkgQyAxNzguNjAgMTY5Ljg3IDE3NC44NiAxNjIuMjQgMTcwLjcwIDE1NC4yNSBDIDE2OC44MCAxNTAuNTAgMTY2LjcxIDE0Ny4xOSAxNjUuODEgMTQzLjAxIEMgMTYyLjI2IDEyOC4wMCAxNjYuNzkgMTEzLjc0IDE2My42OCA5OS4xMiBDIDE2Mi4xOCA5Mi44MCAxNTkuNDAgODYuOTggMTUzLjk4IDgzLjE0IEMgMTQ2LjQ1IDc3Ljk1IDEzNi43NSA3Ny4xNSAxMjguOTMgNzMuMjEgQyAxMjMuNDggNjkuODcgMTIxLjQ1IDY0LjExIDEyMS45MCA1Ny45MSBDIDExOS43NSA1OC42NCAxMTguMzAgNjAuMjQgMTE1LjkzIDYwLjQ5IEMgMTExLjEzIDYxLjMyIDEwNy40MiA1OS40NyAxMDMuMTcgNTcuNzEgQyAxMDIuNzQgNjEuMDggMTAyLjQ3IDY0LjY1IDEwMC41OSA2Ny41OSBDIDk3LjkzIDcyLjIyIDkyLjg2IDc0LjQzIDg4LjAzIDc2LjA4IEMgODEuNDIgNzguMTAgNzMuMTkgODAuMTIgNjguMzAgODUuMjkgQyA2Mi45MSA5MC4yNiA2MC42NiA5Ny45MCA2MC4wNSAxMDQuOTkgQyA1OS44OCAxMTQuNjkgNjAuMTggMTI0LjQwIDU5LjkzIDEzNC4wOSBDIDU5LjQ5IDEzOC42NyA1OS40MiAxNDMuOTQgNTcuNDIgMTQ4LjE0IEMgNTMuNTQgMTU1LjY2IDQ5LjAzIDE2My4zOCA0Ni41OSAxNzEuNTEgQyA0NC42NSAxNzkuOTEgNDUuMTEgMTg5LjkzIDQxLjg0IDE5Ny45NCBDIDM5LjgzIDIwMy41OCAzNC44NSAyMDguNDAgMzEuMTIgMjEzLjAyIEMgMjguMTIgMjE3LjA0IDI0LjY0IDIxOS45MiAyMy4xNCAyMjQuOTIgQyAyNi45NSAyMjUuMjQgMzAuNTMgMjE5LjY5IDMzLjczIDIxOC4yNiBMIDM0LjEyIDIxOS45NyBDIDMyLjUzIDIyNS4wOSAzMS4yMCAyMzAuNjIgMzEuMzYgMjM2LjAxIEMgMzEuNTUgMjM3LjAxIDMxLjU3IDI0MC4yNSAzMy4wNCAyNDAuMDYgQyAzMy4yNiAyMzYuNTEgMzMuMTYgMjMyLjk4IDMzLjY0IDIyOS40NCBDIDMzLjk0IDIyOS4wOCAzNC4yNCAyMjguNzEgMzQuNTQgMjI4LjM0IEMgMzQuMTUgMjMyLjYxIDMzLjQwIDIzNi42NyAzMy44NCAyNDEuMDAgQyAzNC43MCAyNDIuNTQgMzcuMzEgMjQyLjQ4IDM3LjQzIDI0MC4zMCBDIDM3LjkzIDI0MC42OCAzOC40MiAyNDEuMDUgMzguOTIgMjQxLjQzIEMgNDEuOTMgMjQwLjQzIDQxLjAyIDIzNS40MiA0MS4yMCAyMzMuMDAgQyA0MS40NyAyMzAuOTAgNDAuODkgMjI3LjY3IDQyLjY1IDIyNi4xOSBDIDQyLjA1IDIzMC40OSA0MS4zNyAyMzQuNTcgNDEuOTkgMjM4Ljk0IEwgNDIuNjcgMjM4LjYwIEMgNDYuMDAgMjMzLjU3IDQ1LjAyIDIyNS41NyA0Ni43MyAyMTkuODkgQyA0OS43NiAyMDguNjcgNTUuMDEgMTk5LjA4IDU5LjQ5IDE4OC40NyBDIDYzLjA4IDE4MC42NCA2Ni45NyAxNzIuNzEgNjkuNDkgMTY0LjQ2IEMgNzAuOTYgMTU5LjgyIDcxLjQ4IDE1NS4xMCA3Mi40NyAxNTAuMzUgQyA3NC4xMyAxMzkuOTMgNzUuNDEgMTI5LjU0IDc0Ljk3IDExOC45NiBDIDc0LjQ5IDExNy42OSA3Ni42MyAxMTYuNjkgNzYuODMgMTE4LjA1IEMgNzguMTggMTMyLjEyIDg0LjUyIDE0NS45NSA4NC45OCAxNjAuMDAgQyA4My4wMyAxODQuOTAgNzMuOTEgMjA3LjUyIDc2LjM2IDIzMi45OSBDIDc3LjI0IDI0My44MyA3OS4xOSAyNTQuNTEgODEuNzEgMjY1LjA4IEMgODMuMDAgMjY5LjMzIDg0LjE3IDI3My40MiA4Ni45NSAyNzYuOTYgQyA4Ny44MCAyNzguMTkgODkuMDggMjc5LjQxIDg5LjE3IDI4MC45NiBDIDg5Ljc0IDI4Ni4zNyA4OC4xMSAyOTEuNTkgODguOTcgMjk3LjAwIEMgODguOTggMzAxLjgyIDg5Ljg5IDMwNy40OCA4Ny45MCAzMTEuOTUgQyA4NS4yOCAzMTguOTcgODMuMDQgMzI2LjM0IDg0LjI5IDMzMy44OSBDIDg2LjU0IDM0NC4wNyA5MC41NiAzNTQuMDIgOTQuNDAgMzYzLjcwIEMgOTYuMjYgMzY4Ljk1IDk2LjIwIDM3NS4wNiA5NS45MSAzODAuNTcgQyA5NS4yMiAzODYuNzEgODguMzAgMzg5LjQyIDg2LjY5IDM5NC45OSBDIDg2LjQ5IDM5Ny43MCA5MC4wNCAzOTguMDcgOTIuMDAgMzk3Ljk4IEMgOTYuNzggMzk4LjAxIDEwMi44OCAzOTkuNzMgMTA2LjI5IDM5NS4yNyBDIDEwOC40NiAzOTAuOTggMTA5Ljg2IDM4NS44MiAxMDkuMTAgMzgxLjAwIEMgMTA4LjQ0IDM3NC45MyAxMDUuNzggMzY5LjI0IDEwNS43NiAzNjMuMDAgQyAxMDUuMDUgMzUyLjM5IDEwOC43OCAzNDIuNTkgMTA4LjA5IDMzMi4wMCBDIDEwNy44NSAzMjQuOTQgMTA1Ljk4IDMxOC4wMyAxMDUuNzMgMzExLjAxIEMgMTA1LjAxIDMwNS4xMSAxMDYuNjggMjk5LjcwIDEwNi44NiAyOTMuOTYgQyAxMDcuNzcgMjk0LjAzIDEwOC41MSAyOTQuNTMgMTA5LjA4IDI5NS40NiBDIDEwOC4xNiAyOTguOTMgMTA4LjI0IDMwMi40OSAxMDcuNTQgMzA2LjAxIEMgMTA2LjQ4IDMxMi41OSAxMDguMzEgMzE4LjQ1IDEwOS4yNSAzMjQuOTAgQyAxMTEuNTIgMzM1LjY2IDEwOC4xNCAzNDguMTkgMTA3LjAyIDM1OS4wMCBDIDEwNy42MyAzNjYuMDQgMTA4LjgxIDM3My4yNCAxMTAuNjkgMzgwLjA3IEMgMTExLjk0IDM4Ni4xOSAxMTAuMjkgMzkyLjAyIDEwNy4zNSAzOTcuMzkgQyAxMDUuNTQgNDAwLjExIDEwMS45MiA0MDAuMTEgOTkuMDAgNDAwLjAyIEMgOTUuMDYgMzk5LjgyIDkwLjU5IDQwMC41NiA4Ni44OCAzOTkuMjUgQyA4NS4zMCAzOTguNjUgODMuNzQgMzk3Ljk4IDgzLjkxIDM5NS45NyBDIDg0Ljc1IDM5MC4yMCA5MS4wNSAzODcuOTAgOTMuMTUgMzgyLjA4IEMgOTYuMDggMzc0LjQyIDkzLjA0IDM2NS4xOSA5MC4xMCAzNTcuOTQgQyA4Ny4xNSAzNDkuNTcgODMuMTkgMzQwLjc5IDgyLjA3IDMzMS45OSBDIDgxLjY1IDMyOC40OCA4Mi4yNiAzMjUuMTQgODMuMDMgMzIxLjc0IEMgODIuNTkgMzIxLjMyIDgyLjE1IDMyMC45MSA4MS43MSAzMjAuNTAgQyA4Mi4zMSAzMjAuMjUgODIuOTAgMzIwLjAxIDgzLjQ5IDMxOS43NyBDIDg0LjQ5IDMxNC42MiA4Ny4wMSAzMTAuNDkgODcuMDAgMzA1LjAwIEMgODYuOTUgMjk3LjY3IDg3LjA0IDI5MC4zMyA4Ni45NSAyODMuMDAgQyA4Ni45NiAyNzkuODMgODQuNzUgMjc3LjM0IDgzLjQ2IDI3NC41NCBDIDc3Ljk3IDI2My41MSA3Ni43NyAyNTEuMDIgNzUuMDcgMjM5LjAwIEMgNzQuODAgMjMyLjM4IDczLjY4IDIyNS41NSA3NC4zOSAyMTkuMDAgQyA3NC45OCAyMDEuNzEgNzkuNTcgMTg2LjAxIDgyLjIxIDE2OS4wMSBDIDgzLjM2IDE2NC4xNyA4My4wMiAxNTguOTUgODIuOTAgMTU0LjAwIEMgODEuMDQgMTQ1LjE1IDc4LjM4IDEzNi40OCA3Ni40OCAxMjcuNjUgQyA3NS43NyAxMzkuMzMgNzQuNzkgMTUwLjY1IDcxLjcwIDE2Mi4wMCBDIDY2Ljc5IDE3OS40NyA1OC4wNyAxOTQuMDIgNTEuNTUgMjEwLjU1IEMgNDguMTMgMjE5LjEzIDQ3LjA1IDIyNy44OCA0NS44OCAyMzYuOTYgQyA0NC42MyAyNDIuMzcgMzguOTkgMjQ0LjA1IDM0LjA4IDI0My42MSBDIDMxLjQ0IDI0My4wNyAzMC4wMCAyNDAuNjMgMjkuNzQgMjM4LjEwIEMgMjguOTkgMjMyLjgyIDMwLjM3IDIyOC4xMCAzMC45OCAyMjIuOTcgQyAyOC4xNSAyMjQuNjggMjYuMjMgMjI2LjkyIDIyLjk5IDIyNy45OSBDIDIxLjA1IDIyNy42NyAxOS4yNSAyMjUuMzkgMjAuNTIgMjIzLjQ4IEMgMjIuMTcgMjIwLjE5IDI1LjE2IDIxNy4zNCAyNy40OSAyMTQuNTAgQyAzMC44NyAyMTAuMjYgMzQuNTMgMjA2LjIwIDM3Ljc4IDIwMS44NiBDIDQyLjk0IDE5NC41NiA0Mi40NCAxODQuOTIgNDMuNzEgMTc2LjUxIEMgNDUuMDQgMTY1LjE3IDUyLjc5IDE1Ni4wNCA1Ni4xNSAxNDUuMDQgQyA1OC4zMSAxMzguNzYgNTguMDQgMTMyLjU0IDU3Ljk1IDEyNi4wMCBDIDU3LjUzIDExOS4zNiA1Ny42OSAxMTIuNjQgNTcuOTggMTA2LjAwIEMgNTguNjkgOTguMTggNjAuODkgOTAuMjggNjYuNTggODQuNTggQyA2OC43MSA4Mi4yNiA3MS4zNCA4MC44NCA3My44MiA3OC45MiBDIDc2LjAyIDc4LjA0IDc4LjI5IDc3LjMwIDgwLjUxIDc2LjQ3IEMgODUuOTQgNzQuNjggOTIuNTAgNzMuNzkgOTcuMDAgNzAuMDEgQyAxMDAuODYgNjYuNTMgMTAxLjI1IDYwLjg2IDEwMC45NCA1Ni4wMSBDIDk5LjY0IDU0LjAyIDk3LjU0IDUyLjc0IDk2LjUxIDUwLjQ2IEMgOTMuODIgNDUuMjEgODkuMTUgNDAuMjggODkuNzcgMzMuOTkgQyA4OS44NCAzMC4xNiA5Mi43NSAyOC4yMSA5My40OSAyNC40NiBDIDk0LjUyIDE5LjgxIDk2LjIwIDE1LjA3IDEwMC4yNSAxMi4yMSBDIDEwNy40MSA3LjA5IDExNy41OCA4LjIzIDEyNC4xMSAxMy44MyBaIiBmaWxsPSIjODE4MTgxIiAvPgo8cGF0aCBkPSJNIDMwNC45OCA5LjYyIEMgMzEwLjIyIDkuNjAgMzE1LjUyIDEwLjEyIDMyMC4xOCAxMi43MiBDIDMyNC45OSAxNS41OSAzMjcuMjMgMjEuNjEgMzI3LjExIDI3LjAwIEMgMzI3LjE5IDMyLjU5IDMyOC40MCAzOS40MiAzMjYuNzEgNDQuNzcgQyAzMjQuODUgNDguNTkgMzIyLjUyIDUxLjQ5IDMyMi4xMyA1NS45OCBDIDMyMi4zNyA1OC41NiAzMjEuMzggNjAuNzAgMzIyLjg3IDYzLjA0IEMgMzI0LjQ4IDY2LjQ3IDMyNy41MCA2Ny45NyAzMzAuODcgNjkuMjIgQyAzNDAuMDMgNzIuOTMgMzQ5LjM5IDc2LjQ0IDM1Ni4xNyA4My45NSBDIDM2Mi4yOCA5MC44NiAzNjMuNTYgMTAwLjEyIDM2Mi41NSAxMDkuMDEgQyAzNjEuOTEgMTE1LjAzIDM2MS43NiAxMjAuOTkgMzYyLjU2IDEyNy4wMSBDIDM2My44NiAxNDQuMjMgMzcyLjMwIDE1OS42NCAzNzcuMTggMTc1Ljk0IEMgMzc4LjY4IDE4MC42MyAzNzkuMjIgMTg1LjAwIDM4MS42NyAxODkuMzIgQyAzODYuMzEgMTk3LjY1IDM5My41MCAyMDMuODEgMzk4LjU1IDIxMS41MCBDIDM5OS41MCAyMTMuMDkgNDAwLjU4IDIxNS45NyAzOTguMTAgMjE2Ljc1IEMgMzk1Ljc0IDIxNy4yNSAzOTIuOTIgMjE1LjcyIDM5MC43MyAyMTQuOTAgQyAzOTAuNjggMjIxLjAxIDM5Mi42NiAyMjYuNzIgMzkwLjk3IDIzMi45MCBDIDM4OC41MiAyMzYuMTkgMzgzLjc3IDIzNi40NCAzODAuMDcgMjM1Ljc2IEMgMzc0LjA5IDIzMy44MCAzNzAuOTcgMjI2LjczIDM2OS45MiAyMjEuMDAgQyAzNjkuMTQgMjE0LjY5IDM3MC40OCAyMDYuMjEgMzY3LjY5IDIwMC4yOCBDIDM2My4zNCAxOTMuNTkgMzU4Ljk1IDE4Ni45NCAzNTQuOTcgMTgwLjAyIEMgMzQ5Ljc2IDE3MS4wMiAzNDUuMzkgMTYxLjY3IDM0NC42MSAxNTEuMTUgQyAzNDMuNjggMTQzLjcyIDM0Mi4yNSAxMzYuMDQgMzQyLjU1IDEyOC41NCBDIDM0Mi4yNCAxMjkuMzMgMzQxLjU4IDEyOS43NiAzNDAuNTcgMTI5LjgyIEMgMzQwLjY3IDEyNS4yMCAzNDIuNzcgMTIwLjcwIDM0Mi45NCAxMTUuOTkgQyAzNDMuMDYgMTE0Ljk1IDM0My4yMyAxMTEuNzUgMzQ0LjkxIDExMi4xMiBDIDM0NS4yNSAxMTIuOTkgMzQ1LjMxIDExMy45NSAzNDUuMDggMTE1LjAxIEMgMzQzLjc0IDEyMi42OSAzNDQuODcgMTMxLjIxIDM0NS4wOSAxMzkuMDAgQyAzNDUuMjMgMTQzLjM1IDM0NS45MSAxNDcuNjQgMzQ2LjIyIDE1MS45OCBDIDM0OC45MiAxNjguNDYgMzU4LjQwIDE4Mi45MCAzNjcuNDMgMTk2LjU3IEMgMzcyLjA4IDIwNC40NSAzNzAuNDkgMjEyLjQ1IDM3MS43MyAyMjEuMDEgQyAzNzIuMTMgMjI0LjM2IDM3NC4wMiAyMjYuNTYgMzc1LjU3IDIyOS40NCBDIDM3Ni41MSAyMzAuODQgMzc3LjI4IDIzMi45NCAzNzkuMjIgMjMzLjA4IEMgMzc5LjMxIDIyOS45OCAzNzcuNjEgMjI2Ljg0IDM3Ny40NiAyMjMuNjUgQyAzNzkuMTAgMjI2LjcxIDM3OS4yOCAyMzAuNzggMzgxLjE2IDIzMy44MyBMIDM4My4zMSAyMzQuMDMgQyAzODMuNjkgMjMwLjUyIDM4MS45NiAyMjcuNDggMzgyLjI1IDIyNC4wMCBDIDM4Mi4zNiAyMjIuNTEgMzgyLjE1IDIyMS41NSAzODEuNTEgMjIwLjIzIEwgMzgyLjQ1IDIyMC41MyBDIDM4Mi43MSAyMjQuOTkgMzgzLjEyIDIyOS4yNSAzODQuNDYgMjMzLjUzIEMgMzg1LjIzIDIzMy45MSAzODYuMTMgMjM0LjA2IDM4Ny4xNiAyMzMuOTggQyAzODcuOTkgMjMyLjcxIDM4Ni45NSAyMzAuODMgMzg2LjcxIDIyOS40NCBDIDM4NS44NCAyMjYuMzcgMzg2LjczIDIyMy4zMyAzODUuMzggMjIwLjMwIEMgMzg3LjEyIDIyMS44NSAzODYuNTQgMjIzLjg3IDM4Ni43NiAyMjYuMDAgQyAzODYuNzQgMjI4LjQwIDM4Ny42MiAyMzAuNTUgMzg4LjAzIDIzMi45NSBDIDM4OC43NyAyMzIuODggMzg5LjI4IDIzMi4yNCAzODkuNTUgMjMxLjAzIEMgMzkwLjE4IDIyNC41NSAzODguMTcgMjE3Ljc4IDM4OC40OSAyMTEuMzkgQyAzOTEuNjUgMjExLjQ5IDM5NC4yMiAyMTQuNDAgMzk3LjYzIDIxNC41MyBDIDM5Ny41NCAyMTMuNzEgMzk3LjI1IDIxMi45NCAzOTYuNzUgMjEyLjI1IEMgMzk0LjM2IDIwOC42OSAzOTEuNjEgMjA1LjQ0IDM4OC42NCAyMDIuMzcgQyAzODQuNzEgMTk3LjgwIDM4MS4xMSAxOTIuNjYgMzc4Ljk0IDE4Ny4wMSBDIDM3Ni40NiAxODEuNDMgMzc1LjQzIDE3NS41MSAzNzMuMzIgMTY5LjgxIEMgMzY5LjY3IDE1OS43OSAzNjUuMzMgMTUwLjQ2IDM2Mi45MiAxNDAuMDAgQyAzNjAuMTIgMTI4LjczIDM2MC4wOCAxMTcuNTIgMzYwLjcxIDEwNi4wMCBDIDM2MC4zMyA5OS4zNiAzNjAuMTIgOTMuMjcgMzU2LjUyIDg3LjQ0IEMgMzUyLjUyIDgwLjIxIDM0My41OSA3Ni45NSAzMzYuNDggNzMuNTggQyAzMzAuMTIgNzAuNjEgMzIyLjc1IDY5LjM4IDMyMC4wNyA2MS45NyBDIDMxNy41NyA1My4yNSAzMjYuMjggNDUuNDggMzI1Ljk5IDM3LjAwIEMgMzI2LjA1IDMxLjYwIDMyNi4xNiAyNS4xMiAzMjQuMDIgMjAuMDUgQyAzMjEuNTAgMTQuNTQgMzE1LjYzIDEyLjQxIDMxMC4wMSAxMS43MCBDIDMwNC43NiAxMC44NCAyOTcuNzYgMTEuNzMgMjk0LjIzIDE2LjE5IEMgMjkxLjQzIDE5LjA1IDI5MS4xOCAyNC4yMCAyOTAuODIgMjguMDAgQyAyOTAuNzMgMzEuMjcgMjg4LjY5IDMzLjcxIDI4OC45OCAzNy4wMCBDIDI4OC45OCA0Mi4xNiAyOTMuNTQgNDYuOTcgMjk0LjkyIDUyLjA1IEMgMjk1Ljk4IDU1Ljk5IDI5Ny4yMiA2MC43MSAyOTMuOTYgNjMuOTkgQyAyODUuOTcgNzMuMDMgMjcwLjE5IDc0LjY4IDI2MC40MCA4MS4zOSBDIDI1Ny44MCA4My41OCAyNTUuNDggODUuODUgMjU0LjI1IDg5LjEwIEMgMjUyLjMwIDk0LjQ0IDI1MS43NCAxMDAuMzQgMjUyLjA4IDEwNi4wMCBDIDI1Mi44MCAxMTcuNDIgMjUxLjMzIDEyOC44NiAyNDguOTAgMTQwLjAwIEMgMjQ3LjM0IDE0Ny40NCAyNDIuNzQgMTUyLjgyIDIzOS41MSAxNTkuNTIgQyAyMzYuNjAgMTY0Ljg1IDIzNi4zMiAxNzAuMzIgMjM0LjY4IDE3NS45MiBDIDIzMi4yMiAxODMuNzEgMjI3LjQxIDE5MS4xMSAyMjIuODAgMTk3LjgxIEMgMjIwLjEyIDIwMS42NSAyMTYuNzIgMjA0LjYyIDIxMy4yMyAyMDcuNjcgQyAyMTUuOTUgMjA4LjI2IDIxOC40MyAyMDcuODQgMjIxLjI0IDIwNy44MiBMIDIyMS41MCAyMDguOTUgQyAyMjAuMjAgMjE0LjM4IDIxOC4zMSAyMjAuMzcgMjE4LjE3IDIyNi4wMSBDIDIxOS41NyAyMjguMTUgMjE4LjMxIDIzMC44MSAyMjAuMDcgMjMyLjUwIEMgMjIyLjI0IDIzMC4yNCAyMjMuNjEgMjI3LjAwIDIyNS4zMyAyMjQuMzQgQyAyMjYuMzAgMjIzLjAyIDIyNi42OCAyMjAuMDcgMjI4LjY4IDIyMC4yNSBDIDIyNy4wNiAyMjQuOTEgMjIzLjkxIDIyOC42NCAyMjEuOTMgMjMzLjA2IEwgMjIzLjA1IDIzMy40OCBMIDIyMy45NyAyMzMuMDMgQyAyMjYuMjkgMjI5LjMyIDIyOC43OSAyMjUuMDcgMjMxLjk1IDIyMi4wNyBMIDIzMi4xOSAyMjMuMDQgQyAyMzAuODMgMjI2LjM3IDIyNi45OCAyMjkuMTkgMjI2LjI2IDIzMi42OCBDIDIyOC44NCAyMzAuNzcgMjMxLjExIDIyOC41MiAyMzIuNzIgMjI1LjczIEMgMjM4LjQ4IDIxNi43NCAyMzcuMDEgMjA0LjkwIDI0MS43OCAxOTQuODggQyAyNDguMDggMTgyLjY4IDI1Ni44NiAxNzAuNzggMjYyLjE5IDE1OC4xMiBDIDI2Ni42NSAxNDguMjQgMjY4LjEwIDEzNi43NiAyNjguMDIgMTI2LjAwIEMgMjY4Ljc4IDEyMS4zMiAyNjcuNTAgMTE2Ljc4IDI2OC4wOCAxMTIuMDcgTCAyNjkuOTIgMTEyLjA4IEMgMjY5LjQ5IDEyNS40MCAyNzUuMzMgMTM4LjM4IDI3NS4wMiAxNTIuMDAgQyAyNzQuOTcgMTU0LjY3IDI3NS4wNiAxNTcuMzMgMjc1LjAyIDE2MC4wMCBDIDI3My44NCAxNzYuNDEgMjcyLjUwIDE5Mi42MSAyNzAuODQgMjA4Ljk5IEMgMjY5Ljk4IDIxNC42MCAyNjkuMzYgMjIwLjMzIDI2OS4wMCAyMjYuMDAgQyAyNjkuNzYgMjM4Ljg1IDI3My4wNCAyNTIuMjMgMjc1LjA5IDI2NC45NyBDIDI3Ni45NSAyNzYuMzkgMjc4LjczIDI4OC40NiAyNzcuMjcgMzAwLjAxIEMgMjc2Ljk3IDMwNi44OCAyNzQuNTUgMzEzLjE4IDI3My44NyAzMjAuMDEgQyAyNzIuOTAgMzI1Ljk5IDI3NC41MSAzMzIuNTkgMjc1Ljg2IDMzOC40MyBDIDI3OS43NCAzNTMuMDUgMjg2LjU1IDM2NS45MyAyODQuNzkgMzgxLjY2IEMgMjgyLjY1IDM4Mi42OCAyODAuMzUgMzgzLjI1IDI3OS45OSAzODUuOTkgQyAyODAuNTkgMzg4LjQ3IDI4My4wNiAzOTAuNTYgMjg1LjAyIDM5Mi4wNSBDIDI4OC40NiAzOTQuMjYgMjkyLjU2IDM5Ni4yNCAyOTYuNjggMzk0LjY4IEMgMjk3LjA1IDM5NC4wOSAyOTcuNDAgMzkzLjQ5IDI5Ny43NCAzOTIuODggQyAyOTkuMTMgMzg2LjE4IDI5OC4xNiAzNzguOTIgMjk3LjUwIDM3Mi4wMSBDIDI5NS4wNCAzNTcuNTcgMjk3LjkwIDM0NC4zOCAyOTguNTkgMzMwLjAwIEMgMjk4LjMwIDMyMy4zMiAyOTcuMzUgMzE2LjY2IDI5Ni45NiAzMTAuMDAgQyAyOTcuMzcgMjk5LjQ4IDI5OC40NSAyODkuMzcgMjk5LjQ3IDI3OC45OSBDIDMwMC4zMCAyNzAuOTQgMjk5LjkwIDI2My4xMSAyOTkuOTcgMjU1LjAwIEMgMzAwLjkzIDI0MC4wMyAzMDIuMzggMjI1LjIwIDMwMy44NSAyMTAuMjggQyAzMDMuMjUgMjEwLjY3IDMwMi42NSAyMTEuMDQgMzAyLjAzIDIxMS40MCBDIDI5Ny4xOCAyMTIuMDEgMjkzLjY4IDIxNC43MiAyODguNDQgMjEzLjc4IEMgMjg3LjIyIDIxMy40MCAyODYuNDcgMjEzLjY1IDI4NS4yOSAyMTMuOTQgQyAyODMuNjUgMjEyLjg3IDI4MS45MiAyMTMuMDcgMjgwLjA2IDIxMi45MyBDIDI3OS42MCAyMTIuMDcgMjc5LjU4IDIxMS4wNyAyNzkuOTkgMjA5Ljk0IEMgMjg0Ljc0IDIwOS45NyAyODkuMTkgMjExLjIzIDI5My45OSAyMTAuNjMgQyAyOTYuODYgMjA5LjY3IDI5OS42NyAyMDguOTAgMzAxLjkyIDIwNi43NSBDIDMwMy40NiAyMDUuMjUgMzA1LjAwIDIwNS44OSAzMDYuOTAgMjA2LjAwIEMgMzA3LjMwIDIwOC40OSAzMDkuMzEgMjA5LjUyIDMxMS4yNSAyMTAuNzUgQyAzMTUuMzMgMjEzLjA3IDMxOS43NSAyMTMuMDggMzI0LjE5IDIxMS44NCBDIDMyNC4yNyAyMTIuNjggMzIzLjc3IDIxMy4zMCAzMjIuNjkgMjEzLjcxIEMgMzE5Ljk0IDIxNC4yOSAzMTcuMTEgMjE0LjE4IDMxNC4zNiAyMTQuODkgQyAzMTMuNDAgMjE0LjM1IDMxMi4zNSAyMTMuOTggMzExLjIzIDIxMy44MCBDIDMwOS4xMiAyMTEuODMgMzA2Ljk5IDIxMi40MyAzMDUuNDMgMjA5LjQ3IEMgMzA2LjE3IDIxNi4yOSAzMDYuMDUgMjIzLjE3IDMwNi45NyAyMzAuMDAgQyAzMDYuODkgMjM3LjAwIDMwNy41NSAyNDQuMjIgMzA2LjIxIDI1MS4wOSBDIDMwNS41NCAyNTkuMDEgMzA1LjM1IDI2Ny4wNyAzMDUuOTYgMjc1LjAwIEMgMzA1Ljk0IDI4My43OSAzMDguMDUgMjkzLjE4IDMwNy4wNSAzMDIuMDAgQyAzMDcuMDQgMzA5LjM1IDMwNS40NyAzMTYuNjcgMzA0Ljk4IDMyNC4wMCBDIDMwNC4yNSAzMzEuMDQgMzA1LjMzIDMzOC4zOSAzMDYuNTUgMzQ1LjMzIEMgMzA5LjAzIDM1OS40NyAzMDUuNzEgMzcyLjA0IDMwNC42OSAzODYuMDAgQyAzMDQuNjIgMzg5LjEzIDMwNC45OSAzOTMuMjQgMzA4LjI0IDM5NC43MiBDIDMxMy41MSAzOTYuMjMgMzE5Ljc5IDM5Mi44MyAzMjIuMDUgMzg3Ljk4IEMgMzIyLjkxIDM4NS42OSAzMjAuNDMgMzg1LjE0IDMxOS4xNCAzODMuODUgQyAzMTUuNTAgMzgwLjEyIDMxNS4yNiAzNzQuNzEgMzE2LjkzIDM2OS45OSBDIDMyMC4wNiAzNTguNDQgMzIyLjYwIDM0Ni43MiAzMjUuNDIgMzM1LjA5IEMgMzI2LjcwIDMyOC43MSAzMjkuNTQgMzIxLjQ5IDMyOC4zNCAzMTQuOTkgQyAzMjcuNzggMzA4LjU2IDMyNi4zMiAzMDIuNTEgMzI2LjQwIDI5Ni4wMCBDIDMyNS45NSAyNjguMTAgMzM3LjM0IDI0My40OCAzMzguMDAgMjE1LjAwIEMgMzM4LjEwIDIwMS4wMyAzMzYuMzYgMTg3LjAzIDMzNS44MiAxNzMuMDkgQyAzMzUuNTQgMTY4LjA3IDMzNC45NiAxNjMuMDMgMzM0Ljk3IDE1OC4wMCBDIDMzNS41OCAxNDguNjAgMzM3LjQ4IDEzOS40MCAzNDAuMDUgMTMwLjM0IEwgMzQxLjQxIDEzMC4yNCBDIDM0MC4wMiAxNDAuMDcgMzM1Ljg3IDE0OC42MyAzMzcuMTQgMTU4Ljk0IEMgMzM2LjY1IDE2Ni4zNyAzMzguMDggMTczLjU1IDMzOC4xNCAxODAuOTkgQyAzMzkuMDIgMTg4LjY0IDMzOS4yMyAxOTYuMzIgMzM5LjgxIDIwMy45OSBDIDM0MC4yOSAyMTUuNjYgMzM5LjQ5IDIyNy41NSAzMzcuMjcgMjM5LjAyIEMgMzMzLjQ1IDI2MS4wMiAzMjYuNTIgMjgyLjM5IDMyOS4xNiAzMDQuOTkgQyAzMzAuODkgMzE0LjM4IDMzMC4wMyAzMjMuNzMgMzI3LjgwIDMzMi45NiBDIDMyNC43OCAzNDYuMzggMzIxLjQ3IDM1OS43MiAzMTguMjMgMzczLjA5IEMgMzE3LjkxIDM3NS40NiAzMTcuNDkgMzc4Ljg2IDMxOS4yNCAzODAuNzggQyAzMjAuNTcgMzgyLjg4IDMyMy41MyAzODMuMjQgMzI0LjY1IDM4NS4zNiBDIDMyNC45MiAzODkuOTggMzIwLjUzIDM5My44NSAzMTYuNzAgMzk1LjY2IEMgMzEzLjEwIDM5Ny42NCAzMDcuNDEgMzk3Ljg1IDMwNC41MSAzOTQuNTMgQyAzMDIuMjEgMzkxLjgxIDMwMy4xNiAzODcuMzcgMzAzLjAwIDM4NC4wMCBDIDMwMy43NiAzNzUuNjYgMzA1LjAwIDM2Ny4zNyAzMDUuNjUgMzU5LjAwIEMgMzA1LjQ1IDM0OC42OCAzMDIuNDggMzM4LjI4IDMwMi45OCAzMjguMDAgQyAzMDIuNzEgMzIzLjU1IDMwMy43NSAzMTkuMzcgMzA0LjM2IDMxNC45OSBDIDMwNy43NiAyOTYuMjYgMzAyLjU0IDI3Ni43NSAzMDQuMzYgMjU4LjAwIEMgMzA0LjY0IDI1MS42NCAzMDUuMjEgMjQ1LjM4IDMwNC45OCAyMzkuMDAgQyAzMDQuODMgMjMzLjQ2IDMwNS40MCAyMjcuNzQgMzA0LjUxIDIyMi4yNyBDIDMwMy4zNyAyMzMuNDQgMzAxLjY1IDI0NC40NSAzMDEuNDggMjU1LjcxIEMgMzAxLjY5IDI2Ny40MiAzMDIuNDQgMjc4LjM4IDMwMC4wNyAyOTAuMDAgQyAyOTguMzYgMjk4LjAxIDI5OS4xOSAzMDUuODcgMjk5LjAxIDMxNC4wMCBDIDI5OS4zNSAzMjAuMDQgMzAwLjQ0IDMyNS45MSAyOTkuOTMgMzMyLjAwIEMgMjk5LjM1IDMzOC42NiAyOTkuMjAgMzQ1LjM2IDI5OC40NSAzNTIuMDAgQyAyOTcuMTEgMzY1LjY5IDMwMi44OSAzNzguNjUgMjk5LjYzIDM5Mi45MiBDIDI5OS4yNiAzOTQuODAgMjk4LjM0IDM5NS43NSAyOTYuOTggMzk2Ljk4IEMgMjkyLjk0IDM5Ny4zNCAyODkuNjQgMzk2Ljg1IDI4Ni4wNiAzOTQuOTAgQyAyODMuMDEgMzkzLjI1IDI3OC42OSAzOTAuNjkgMjc3LjkyIDM4Ny4wMyBDIDI3Ny43MiAzODMuNTYgMjgyLjAzIDM4Mi4yMSAyODIuOTQgMzc5Ljk5IEMgMjgzLjQ2IDM3Mi45NSAyODEuODAgMzY2LjcxIDI4MC40NSAzNTkuOTEgQyAyNzguMTMgMzQ5LjkzIDI3My4wNyAzNDAuMTUgMjcxLjk1IDMzMC4wNCBDIDI3MS4xOSAzMjIuNjUgMjcyLjkzIDMxNi4yMyAyNzQuMDQgMzA5LjAxIEMgMjc1Ljc3IDI5OS43MCAyNzYuODcgMjkwLjUyIDI3NS4xNSAyODEuMTAgQyAyNzMuNjcgMjY1LjkzIDI3MC4zNyAyNTEuMDcgMjY4LjI4IDIzNi4wMCBDIDI2Ny42MSAyMjkuMjggMjY3LjU4IDIyMi43MSAyNjguMjggMjE1Ljk4IEMgMjY5LjE2IDIxMS4yMCAyNjguOTQgMjA2LjM0IDI2OS43NyAyMDEuNTMgQyAyNzAuOTUgMTg0LjA2IDI3My41OSAxNjYuNTIgMjczLjM1IDE0OS4wMCBDIDI3My4zNiAxNDMuMTEgMjcxLjYwIDEzNy43NSAyNzAuNzIgMTMyLjAxIEwgMjcwLjE5IDEzMi4yNSBDIDI2OS4xMSAxMzYuMDcgMjY5LjQwIDE0MC4yNCAyNjguNDMgMTQ0LjE1IEMgMjY3LjEwIDE1My40MyAyNjIuOTMgMTYwLjcwIDI1OS4yMiAxNjkuMTAgQyAyNTMuNzIgMTc5Ljg4IDI0NS4xNSAxODkuMjQgMjQxLjU2IDIwMS4xMSBDIDIzOS40MyAyMDguNTggMjM5LjkwIDIxOC43NiAyMzUuMjggMjI1LjI3IEMgMjMyLjM5IDIyOC44OSAyMjkuMjAgMjM0LjA0IDIyNC43MCAyMzUuNjggQyAyMjEuMjkgMjM2LjM0IDIxNy44MSAyMzMuNzIgMjE2LjAyIDIzMS4wMCBDIDIxNC42OSAyMjguMzYgMjE2LjQwIDIyNC43OSAyMTYuODIgMjIxLjk5IEMgMjE3LjQxIDIxOC4wMiAyMTguOTggMjE0LjA0IDIxOC45NyAyMTAuMDMgQyAyMTYuNzQgMjEwLjAwIDIxNC4xOSAyMTAuNzAgMjEyLjA4IDIwOS45NyBDIDIxMC41NCAyMDguOTAgMjEwLjU3IDIwNy44NCAyMTAuOTYgMjA2LjE0IEMgMjE2LjkyIDIwMy4yMyAyMjAuNjIgMTk4LjQ1IDIyNC4yNCAxOTMuMDkgQyAyMjcuMzMgMTg4LjM0IDIyOS45MiAxODMuMjggMjMxLjkzIDE3Ny45OCBDIDIzMy41NiAxNzMuNTUgMjMzLjk2IDE2OC44OCAyMzUuNDEgMTY0LjQxIEMgMjM2Ljg2IDE1OS41NyAyNDAuMDkgMTU1LjAxIDI0Mi41MSAxNTAuNTQgQyAyNDguNjkgMTQwLjA4IDI1MC42MiAxMjYuOTYgMjUxLjAwIDExNS4wMCBDIDI1MC42NyAxMDUuMTggMjQ4LjQ5IDkzLjE4IDI1NC42NyA4NC42MyBDIDI2MS4wNCA3NS44MyAyNzIuNDcgNzQuMzAgMjgxLjQ3IDY5LjQxIEMgMjg2LjU1IDY2Ljg5IDI5Mi4zOCA2NC4wMCAyOTMuOTMgNTguMDIgQyAyOTQuNzUgNTAuOTMgMjg5LjI3IDQ3LjA0IDI4Ny40NCA0MC45MSBDIDI4NS45NyAzNi44OSAyODguNjEgMzIuMjAgMjg4LjkxIDI4LjAyIEMgMjg4Ljk4IDIzLjQ5IDI4OS43OCAxOC40MyAyOTIuODMgMTQuODkgQyAyOTUuNzIgMTEuMjQgMzAwLjQ3IDkuNzMgMzA0Ljk4IDkuNjIgWiIgZmlsbD0iIzgxODE4MSIgLz4KPHBhdGggZD0iTSAxMjUuNjMgMTguNDAgQyAxMjguMzUgMjIuMDcgMTI4LjEzIDI1Ljk4IDEyOS45OSAyOS45OCBDIDEzMS41OSAzMy41NSAxMzIuMTQgMzcuMzAgMTI5LjU1IDQwLjU3IEMgMTI1LjU1IDQ1LjUwIDEyNS40NiA1MS4yOSAxMjAuNzIgNTUuNzIgQyAxMTcuNzUgNTkuMTAgMTEzLjAxIDU5LjgzIDEwOC45MCA1OC4zMiBDIDEwMy4wMSA1Ni4xMCA5OS4wNCA1MS40MCA5Ni4zNSA0NS44NiBDIDk0Ljg2IDQyLjQxIDkyLjAxIDQwLjU4IDkxLjM5IDM2Ljc1IEMgOTAuOTMgMzMuNzIgOTMuMDggMzAuMTggOTQuODAgMjcuODEgQyA5NS45MCAyMi40NyA5Ni40NyAxNy43NiAxMDEuMDkgMTQuMTcgQyAxMDguNjkgOC4xNyAxMjAuMjEgMTAuNzQgMTI1LjYzIDE4LjQwIFoiIGZpbGw9IiNmZmZmZmYiIC8+CjxwYXRoIGQ9Ik0gMTA1LjkxIDI5Ljk1IEMgMTA2Ljg1IDMxLjA5IDEwNy43NSAzMi4xMSAxMDYuNDUgMzMuNDUgQyAxMDQuOTQgMzUuMzAgMTAxLjc4IDMzLjg3IDEwMi40NyAzMS40NyBDIDEwMy4xNiAyOS42NCAxMDQuMjUgMjkuOTggMTA1LjkxIDI5Ljk1IFoiIGZpbGw9IiM4MTgxODEiIC8+CjxwYXRoIGQ9Ik0gMTE2LjQ1IDMwLjQ2IEMgMTE5LjE1IDI4LjMyIDEyMS42MCAzMi40NiAxMTkuNDUgMzQuNDUgQyAxMTcuMDYgMzYuNzAgMTEzLjU3IDMyLjQzIDExNi40NSAzMC40NiBaIiBmaWxsPSIjODE4MTgxIiAvPgo8cGF0aCBkPSJNIDEwOS4xOCA0Mi4wMiBDIDExMS40NiA0MS45MCAxMTMuNzUgNDIuMDAgMTE2LjAzIDQxLjk5IEwgMTE2LjAzIDQ0LjAyIEMgMTE0LjM0IDQ0LjE4IDExMS44OSA0My41OSAxMTAuNDIgNDQuNDEgQyAxMDkuNjEgNDUuMDcgMTA4Ljc5IDQ1LjI3IDEwNy45OCA0NS4wMSBDIDEwNy42NSA0My43OCAxMDguMDUgNDIuNzggMTA5LjE4IDQyLjAyIFoiIGZpbGw9IiM4MTgxODEiIC8+CjxwYXRoIGQ9Ik0gMTA5Ljk4IDQ2Ljk4IEMgMTExLjY2IDQ2Ljk3IDExMy4zNCA0Ni45NyAxMTUuMDEgNDYuOTggQyAxMTUuMDIgNDguMzMgMTE1LjAzIDQ5LjY4IDExNS4wMyA1MS4wNCBDIDExMy4zNSA1MS4wNSAxMTEuNjYgNTEuMDUgMTA5Ljk4IDUxLjAyIEMgMTA5Ljk4IDQ5LjY3IDEwOS45OCA0OC4zMyAxMDkuOTggNDYuOTggWiIgZmlsbD0iIzgxODE4MSIgLz4KPHBhdGggZD0iTSAzMDUuNTMgNzguMzcgTCAzMDYuNzIgNzguMzIgQyAzMDcuMzUgODAuNTkgMzA2LjQ5IDgyLjcwIDMwNi44NCA4NC45OSBDIDMwNy4zNiA4OC44MSAzMDYuNDMgOTMuNDQgMzA4LjA5IDk2Ljk2IEMgMzA3LjAzIDEwMS44MiAzMDguMTkgMTA2LjE3IDMwOC4wMiAxMTEuMDAgQyAzMDcuODggMTE2LjY3IDMwOC4yNiAxMjIuMzggMzA3Ljc5IDEyOC4wMyBDIDMwNy44NSAxMzAuNDYgMzA4LjM0IDEzMi42MiAzMDcuNDYgMTM1LjAwIEMgMzA4LjI0IDEzOC42NiAzMDcuODcgMTQyLjMwIDMwOC4wOCAxNDYuMDIgQyAzMDcuOTYgMTQ2LjkxIDMwOC4yNyAxNDcuNjggMzA5LjAzIDE0OC4zMyBDIDMwNy45NCAxNTEuNjggMzA3Ljk3IDE1NS41MiAzMDguMTMgMTU5LjAwIEMgMzA4LjYxIDE2MS43NyAzMDcuNTAgMTY0LjA3IDMwOS4wOCAxNjYuNjggQyAzMDcuMTUgMTY4LjEzIDMwOC4yMSAxNjkuOTMgMzA3Ljk2IDE3Mi4wNCBMIDMwNi44NyAxNzIuMDQgQyAzMDcuMTMgMTYxLjg3IDMwNy4xMyAxNTEuNjIgMzA2LjczIDE0MS40NiBMIDMwNS45OCAxNDAuNzUgQyAzMDYuNDAgMTM4LjM4IDMwNi41NSAxMzYuMzAgMzA2LjAxIDEzMy45MyBDIDMwNi4xMyAxMzMuMjggMzA2LjQyIDEzMi42NSAzMDYuODkgMTMyLjA0IEMgMzA2LjUyIDEyOC4zNiAzMDYuMTkgMTI0LjQ2IDMwNi41NyAxMjAuNzcgQyAzMDYuOTEgMTEzLjgzIDMwNi41MSAxMDYuOTQgMzA2LjMzIDEwMC4wMCBDIDMwNS43MSA5Mi43MyAzMDYuMzYgODUuNjIgMzA1LjUzIDc4LjM3IFoiIGZpbGw9IiM4MTgxODEiIC8+CjxwYXRoIGQ9Ik0gMTM5LjAwIDgxLjA1IEwgMTM5LjAxIDgyLjAxIEMgMTM0LjYzIDgyLjA0IDEzMC4yNCA4Mi4wMCAxMjUuODUgODIuMDYgTCAxMjYuMDIgODEuMDIgQyAxMjkuOTIgNzguOTcgMTM1LjAxIDgwLjI0IDEzOS4wMCA4MS4wNSBaIiBmaWxsPSIjODE4MTgxIiAvPgo8cGF0aCBkPSJNIDk0LjA2IDgwLjkyIEMgOTYuMjUgODIuMDYgOTcuNTQgODIuMTAgMTAwLjAyIDgyLjAwIEwgMTAwLjAyIDgyLjk5IEMgOTcuODcgODIuODkgOTYuNzkgODMuMDQgOTUuMDAgODQuMjMgQyA5Mi43NSA4Mi41MCA5MC41MyA4My4wOSA4Ny44OSA4My4wNyBDIDg3LjUzIDgwLjgzIDkyLjcxIDgxLjQzIDk0LjA2IDgwLjkyIFoiIGZpbGw9IiM4MTgxODEiIC8+CjxwYXRoIGQ9Ik0gMzE1Ljk5IDExNS45MyBDIDMxOC40NiAxMTUuMDUgMzE4LjY5IDExOC4xOCAzMjAuODAgMTE5LjIzIEMgMzIxLjIyIDEyMi4wNiAzMjQuODQgMTIwLjkxIDMyNS4xMSAxMjMuMDMgQyAzMjEuNDcgMTIzLjY0IDMxOS4xOCAxMjEuNDggMzE3LjM2IDExOC42MyBDIDMxNi4wOCAxMTguMTUgMzE1LjYyIDExNy4yNiAzMTUuOTkgMTE1LjkzIFoiIGZpbGw9IiM4MTgxODEiIC8+CjxwYXRoIGQ9Ik0gMjgzLjg3IDExOC45OSBDIDI4Ni4zNyAxMTguNzIgMjg4LjI3IDEyMi4xNCAyOTEuMDAgMTIxLjk5IEMgMjkzLjMxIDEyMi4xMSAyOTUuMzQgMTE5LjQ3IDI5Ny4wNSAxMTguODUgQyAyOTcuNjYgMTIyLjEzIDI5NC43MyAxMjEuMzkgMjkzLjg1IDEyNC4wMCBDIDI5MC4xNyAxMjQuNzcgMjg4LjQ0IDEyMi41OCAyODUuMTQgMTIxLjg5IEMgMjg0LjYyIDEyMC45NCAyODQuMjAgMTE5Ljk4IDI4My44NyAxMTguOTkgWiIgZmlsbD0iIzgxODE4MSIgLz4KPHBhdGggZD0iTSAxNDEuMDQgMTI2LjgyIEwgMTQwLjk4IDEyOC44NiBDIDEzOC45MSAxMjkuMzAgMTM3LjcwIDEzMS4xMyAxMzUuNzQgMTMxLjk4IEMgMTMzLjI3IDEzMS45NSAxMzAuNjQgMTMyLjI2IDEyOC4yMCAxMzEuODcgQyAxMjYuMTkgMTMwLjk5IDEyNC4yNyAxMzAuMzEgMTIyLjE2IDEyOS43MyBDIDEyMS4yNCAxMjkuMzggMTIwLjgyIDEyOC43NiAxMjAuOTAgMTI3Ljg3IEMgMTIyLjg2IDEyOC4yNSAxMjQuNzQgMTI4LjgwIDEyNi42MyAxMjkuNDMgQyAxMjkuMTIgMTMwLjMxIDEzMS41MiAxMjkuODUgMTM0LjAyIDEzMC4zOCBDIDEzNS43NSAxMzAuMDEgMTM3LjUwIDEyOS4wNiAxMzguOTcgMTI4LjA3IEMgMTM5LjYwIDEyNy41MyAxNDAuMjkgMTI3LjExIDE0MS4wNCAxMjYuODIgWiIgZmlsbD0iIzgxODE4MSIgLz4KPHBhdGggZD0iTSA4NC44OCAxMjcuOTQgQyA4OC40OCAxMjcuODIgOTAuOTMgMTMwLjU2IDk0LjczIDEzMC4wNSBDIDk2LjYzIDEzMS45OCAxMDEuNDcgMTMxLjAwIDEwMy4yNyAxMjkuMTEgQyAxMDQuNzUgMTI4LjM5IDEwNS4zNSAxMjkuMDEgMTA1LjA2IDEzMC45NiBDIDEwMi4wNiAxMzAuOTAgOTkuMDYgMTMzLjU0IDk1Ljk3IDEzMy4wNSBDIDkzLjE3IDEzMy4wNiA5MS4yNCAxMzAuODMgODguMjYgMTMwLjkxIEMgODcuMzggMTI5Ljc5IDg0Ljg3IDEyOS40NCA4NC44OCAxMjcuOTQgWiIgZmlsbD0iIzgxODE4MSIgLz4KPHBhdGggZD0iTSAyODAuODYgMTY2LjAwIEMgMjgyLjUwIDE2NS45NiAyODQuMTQgMTY1Ljk2IDI4NS43NyAxNjYuMDEgQyAyODcuNDcgMTY4LjA3IDI5MC43MiAxNjcuODkgMjkwLjA0IDE3MS4xOCBDIDI4Ny40MCAxNzAuMjcgMjg0LjkxIDE2Ny43OSAyODIuMTAgMTY3Ljk1IEMgMjgxLjM1IDE2Ny40MyAyODAuOTQgMTY2Ljc4IDI4MC44NiAxNjYuMDAgWiIgZmlsbD0iIzgxODE4MSIgLz4KPHBhdGggZD0iTSAzMjkuNzkgMTY3LjkzIEMgMzMwLjUwIDE2OC40OCAzMzAuOTMgMTY5LjExIDMzMS4wOSAxNjkuODEgQyAzMjkuNjYgMTcxLjY5IDMyNy45NyAxNzAuODkgMzI1LjkxIDE3MS4wNSBDIDMyNS41MCAxNjcuNzYgMzI3LjgxIDE2OS42NyAzMjkuNzkgMTY3LjkzIFoiIGZpbGw9IiM4MTgxODEiIC8+CjxwYXRoIGQ9Ik0gMTM5LjA0IDE3Ni45NiBMIDEzOS4wMiAxNzcuOTYgQyAxMzcuMzggMTc4LjA2IDEzNi40NiAxNzguMDAgMTM1LjM3IDE3OS4zNiBDIDEzMy41NyAxODEuNTIgMTMwLjc3IDE4MS44MyAxMjguOTIgMTgzLjk3IEMgMTI3LjkxIDE4NS4yOSAxMjcuOTYgMTg3LjExIDEyNS45NCAxODcuMTMgQyAxMjUuODggMTgzLjk1IDEyOC4zOSAxODIuMzYgMTMwLjIwIDE4MC4xNSBDIDEzMy41MiAxNzguOTkgMTM1LjI3IDE3Ni40MiAxMzkuMDQgMTc2Ljk2IFoiIGZpbGw9IiM4MTgxODEiIC8+CjxwYXRoIGQ9Ik0gOTMuNzQgMTgxLjA5IEMgOTUuMjAgMTgxLjk0IDk4LjIyIDE4My4xMiA5OC4wOCAxODUuMTcgQyA5NC4xNSAxODMuODQgOTAuMDYgMTgxLjUxIDg1Ljg4IDE4Mi4wMCBDIDg2LjI3IDE3OC42MiA5MS42MiAxODEuMjUgOTMuNzQgMTgxLjA5IFoiIGZpbGw9IiM4MTgxODEiIC8+CjxwYXRoIGQ9Ik0gMjIzLjg2IDIxOC4xMCBDIDIyNC40OSAyMjAuNDQgMjIxLjcwIDIyMy42NCAyMjAuNzIgMjI1LjgxIEwgMjE5LjY4IDIyNi4xOSBDIDIxOS40OCAyMjQuNTcgMjIyLjQ4IDIxOS4wOCAyMjMuODYgMjE4LjEwIFoiIGZpbGw9IiM4MTgxODEiIC8+CjxwYXRoIGQ9Ik0gMTI1LjMzIDQyNS42OCBDIDEyOC4zNiA0MjguMjcgMTMwLjk5IDQzMS4wMSAxMzEuNzkgNDM1LjA3IEMgMTMyLjQ1IDQzOS4zOCAxMzIuMDYgNDQzLjY2IDEzMy4wOSA0NDguMDAgQyAxMzMuNzEgNDUxLjQzIDEzNy42NSA0NTIuNzMgMTM3LjA5IDQ1Ni4zNSBMIDEzNi4wNSA0NTYuMzYgQyAxMzQuNjAgNDU0LjM2IDEzMi45NiA0NTIuMjggMTMyLjA1IDQ0OS45NyBDIDEzMC4yMCA0NDMuODYgMTMxLjY3IDQzNi42MyAxMjcuOTkgNDMxLjAwIEMgMTIzLjY2IDQyNS4yOSAxMTYuMTQgNDIyLjE1IDEwOS4wOSA0MjIuMTcgQyAxMDEuODAgNDIyLjkwIDk0Ljk1IDQyNi42NCA5MC42MSA0MzIuNTggQyA4Ny4zNCA0MzcuMDQgODQuNjYgNDQyLjM5IDg0LjMzIDQ0Ny45OSBDIDg0LjIyIDQ1Ny41OSA5MS4zNSA0NjUuNjkgOTEuMjggNDc1LjAwIEMgOTEuNjQgNDgxLjQzIDg4LjkzIDQ4Ny4zMyA4NS43MyA0OTIuNzQgQyA4Mi42MiA0OTcuOTkgNzguMzcgNTAxLjQyIDc2LjM3IDUwNy4xMCBDIDc1LjE4IDUxMS4wNCA3NC4yOSA1MTUuMDYgNzMuMTYgNTE5LjAyIEMgNzAuMjQgNTMxLjIxIDY0LjYwIDU0Mi45NyA2MC44NyA1NTQuOTMgQyA1Ny4wNSA1NjcuMTUgNTUuMzAgNTc5Ljk2IDU1LjA0IDU5Mi43MyBMIDUzLjQzIDU5Mi44NCBMIDUzLjA3IDU5MS4xOSBDIDU0LjAzIDU4My4wNCA1My43NyA1NzUuMDEgNTUuODAgNTY2Ljk4IEMgNTkuMTkgNTUxLjAwIDY1LjQ0IDUzNi43MyA3MC40NCA1MjEuNDUgQyA3Mi4wNiA1MTYuNzQgNzIuODcgNTExLjg0IDc0LjI4IDUwNy4wOCBDIDc1Ljg3IDUwMS45MSA3OS43NCA0OTguODcgODIuNzkgNDkzLjkzIEMgODUuNjcgNDg5LjM2IDg5LjI1IDQ4My41NSA4OS4wMiA0NzguMDAgQyA4OS44OSA0NjcuOTMgODIuMzIgNDU4LjA4IDgyLjYwIDQ0OC4wMCBDIDgyLjQyIDQ0NC4xNyA4My41NSA0NDAuNzAgODUuNDAgNDM3LjQwIEMgODkuMTkgNDMwLjE0IDk0LjkxIDQyNC4xNiAxMDIuODUgNDIxLjYxIEMgMTEwLjQwIDQxOC45MSAxMTkuMDUgNDIwLjkzIDEyNS4zMyA0MjUuNjggWiIgZmlsbD0iIzgxODE4MSIgLz4KPHBhdGggZD0iTSAzMDAuOTEgNDIxLjUwIEMgMzA3LjM4IDQyMi4yNiAzMTMuMjcgNDI1LjY1IDMxNy42NiA0MzAuMzYgQyAzMjIuMTQgNDM2LjAxIDMyNy4wNSA0NDMuNDYgMzI2LjAwIDQ1MS4wMCBDIDMyNS4xNSA0NTkuNDUgMzE4LjE5IDQ2OC42OCAzMTkuMDQgNDc4LjAwIEMgMzE5Ljk4IDQ4NS42OCAzMjUuNDMgNDkyLjA0IDMzMC44MSA0OTcuMjAgQyAzMzMuNDIgNTAwLjAzIDMzNi40NiA1MDEuODggMzM3LjQyIDUwNS43OSBDIDMzOS4yNCA1MTIuNDAgMzM5Ljk1IDUxOS4zOSAzNDEuNzkgNTI2LjAwIEMgMzQyLjczIDUyOS40MiAzNDQuMjggNTMyLjU5IDM0NS4yOCA1MzUuOTkgQyAzNDguOTkgNTQ4LjkzIDM1Mi4zNyA1NjEuNTggMzUzLjk3IDU3NS4wMCBDIDM1NC4yMCA1NzguNTYgMzU0LjM3IDU4Mi4wMyAzNTUuMTQgNTg1LjUzIEMgMzU0LjUzIDU4Ni40NCAzNTMuNzcgNTg2LjkzIDM1Mi44NiA1ODcuMDAgQyAzNTIuNzQgNTc0LjEwIDM0OS43MSA1NjEuNTAgMzQ2LjcyIDU0OS4wMyBDIDM0NC4yMCA1MzkuMDIgMzQwLjEwIDUyOS4xNiAzMzguMzEgNTE5LjAwIEMgMzM3LjcwIDUxNC40NSAzMzcuNDcgNTA4LjkxIDMzNS4yMiA1MDQuODQgQyAzMzAuNjAgNDk5LjY3IDMyNS4wNyA0OTQuNzkgMzIxLjE4IDQ4OC44MiBDIDMxOC44MSA0ODUuMzIgMzE2LjU3IDQ4MS4zOCAzMTYuOTYgNDc3LjAwIEMgMzE3LjcxIDQ2OC4xMCAzMjEuODYgNDYxLjQyIDMyMy44NiA0NTIuOTkgQyAzMjYuMDkgNDM4LjA0IDMxMi41MCA0MjMuOTUgMjk4LjAwIDQyMy4wMCBDIDI5MC40NiA0MjMuNjggMjgzLjQ1IDQyNi45NCAyNzkuODggNDMzLjkzIEMgMjc3LjEzIDQzOS4yNyAyNzcuNzkgNDQ0LjU5IDI3Ni41OCA0NTAuMTEgQyAyNzYuMTYgNDUyLjg5IDI3My42MCA0NTQuNDIgMjcyLjk4IDQ1Ni45NiBDIDI3My42NiA0NTguNTkgMjc2LjI1IDQ1OS4xMyAyNzYuODIgNDYxLjA0IEMgMjc3LjQ3IDQ2NS44MiAyNzYuMTggNDcwLjAzIDI3OC4zMSA0NzQuNjggQyAyODAuNzYgNDc4LjQwIDI4Ny44OSA0NzQuOTIgMjg5LjY0IDQ3OS4zNCBDIDI5MS45MyA0ODQuNzIgMjkzLjgyIDQ5MS4xNiAyOTEuODUgNDk2LjkyIEMgMjg4LjA3IDUwOC43OSAyNzkuMDUgNTE5LjM0IDI3NS40MSA1MzEuMjggQyAyNzEuOTQgNTQxLjU1IDI3MC40MSA1NTIuMTkgMjcwLjAwIDU2My4wMCBDIDI2OS44MSA1NzIuOTMgMjcxLjU5IDU4My4yMiAyNzMuMDcgNTkzLjAwIEMgMjc0LjI3IDYwMC41MyAyNzQuNzcgNjA4Ljc1IDI3Ni42NSA2MTYuMDkgQyAyNzguMzIgNjIyLjEyIDI4My43NSA2MjYuNDkgMjg1LjcxIDYzMi4wOSBDIDI4Ny44NyA2NDAuNTIgMjg4LjI3IDY0OS40NiAyODkuOTkgNjU4LjAwIEMgMjkyLjc0IDY3NC40OSAyOTcuMjAgNjkxLjQwIDI5OC4yNCA3MDcuOTkgQyAyOTguNzkgNzExLjQzIDI5OS42MyA3MTQuNzggMzAxLjIzIDcxNy45MCBDIDMwMy4yNiA3MjIuNDQgMzA2LjA3IDcyNS44MSAzMDYuMDIgNzMxLjAwIEMgMzA2Ljc3IDc0MS42NyAzMDguMTQgNzUyLjMzIDMwOS4wMSA3NjMuMDAgQyAzMDkuMjAgNzcwLjgyIDMxMi4yOCA3ODAuMjkgMzEwLjMzIDc4OC4wMiBDIDMxMC4wMSA3OTAuNjIgMzA3LjcyIDc5Mi4zNSAzMDUuNzIgNzkzLjc0IEMgMjk4LjI4IDc5OC4wOSAyOTAuNzYgODAyLjI4IDI4My4xMiA4MDYuMjYgQyAyODAuOTUgODA3LjQxIDI3OC43OSA4MDcuNjcgMjc3LjE5IDgwOS42NiBDIDI3OS4xOCA4MTEuNjQgMjgxLjQxIDgxMS4zMCAyODQuMDAgODExLjQ0IEMgMjkzLjMxIDgxMi4wNyAzMDIuNTQgODExLjYzIDMxMS45OSA4MTEuMTkgQyAzMTUuODcgODEwLjgwIDMyMS4yNSA4MTAuOTUgMzI0LjM4IDgwOC4zOCBDIDMyNi45NSA4MDUuODMgMzI2LjM4IDgwMS4yNSAzMjYuMDcgNzk3Ljk5IEMgMzI2LjA1IDc5MC45NCAzMjQuNTUgNzg0LjA5IDMyNC45OSA3NzcuMDAgQyAzMjQuNTYgNzcwLjUyIDMyNS44NiA3NjQuMzYgMzI2Ljg0IDc1OC4wMCBDIDMyNy44NyA3NDguOTIgMzI5LjU1IDczOS4wMSAzMjcuOTIgNzI5Ljk5IEMgMzI1LjI3IDcxNi4zNSAzMTkuNjQgNzA0LjMyIDMxOS45OSA2OTAuMDAgQyAzMTkuNzcgNjgxLjY2IDMyMC43NiA2NzMuMzMgMzIxLjMyIDY2NS4wMCBDIDMyMi4zNSA2NTguMzMgMzIxLjkwIDY1MS43MiAzMjEuOTggNjQ1LjAwIEMgMzIyLjEwIDY0Mi42NiAzMjEuMjkgNjM5LjYyIDMyMi41NSA2MzcuNTggQyAzMjUuNzQgNjMyLjMxIDMyOC4zNSA2MjYuMTggMzI4Ljg4IDYyMC4wMCBDIDMyNy44OCA2MDkuNDcgMzE4LjYzIDYwMC45NCAzMTcuMTkgNTkxLjAwIEMgMzE2LjMxIDU4NC4zMCAzMTcuOTkgNTc4LjczIDMxOC4wOSA1NzIuMDEgQyAzMTguNzcgNTY3LjY3IDMxOC45MCA1NjMuMjQgMzIwLjE2IDU1OS4wNiBDIDMyMS4xNyA1NTguNzAgMzIxLjg5IDU1OC45MSAzMjIuMzEgNTU5LjY5IEMgMzI1LjU3IDU2My45NiAzMjguNjkgNTY4LjA2IDMzMC43NCA1NzMuMDkgQyAzMzMuNDIgNTc5LjcxIDMzMy41NSA1ODYuOTEgMzM1LjQ0IDU5My45OCBDIDMzNy44NSA2MDUuMDUgMzQxLjYxIDYxNC42MyAzNDEuMjkgNjI2LjAwIEMgMzQxLjQzIDYzMy42OSAzMzguMjUgNjQxLjk5IDMzOS40NiA2NDkuNTUgQyAzNDAuNjMgNjQ5LjM5IDM0MS42NCA2NDkuNDggMzQyLjU1IDY0OC42MSBDIDM0My40NyA2NDYuMjUgMzQzLjc5IDY0My44MyAzNDQuNDIgNjQxLjM5IEMgMzQ2LjEzIDY0MC4zMCAzNDYuMzggNjQxLjczIDM0Ni44OCA2NDMuMDMgQyAzNDguNDIgNjQ4LjQ5IDM1MC4xOCA2NTQuNzYgMzUzLjc0IDY1OS4yNyBDIDM1NS4xNSA2NjAuOTkgMzU3LjU5IDY2My4xNSAzNTkuOTYgNjYyLjk0IEMgMzU4Ljk4IDY1OS45OSAzNTcuODkgNjU3LjA0IDM1Ni43MSA2NTQuMTYgQyAzNTYuMDkgNjUyLjcyIDM1NC43NiA2NTEuOTUgMzU0LjM0IDY1MC40MCBDIDM1NS45OSA2NTAuMjQgMzU2LjUyIDY1MS41MSAzNTcuMzEgNjUyLjcwIEMgMzU5LjA4IDY1NS44NiAzNjAuMTUgNjU5LjQ4IDM2MS4yOCA2NjIuOTAgQyAzNjIuODEgNjYzLjA4IDM2My44OSA2NjIuNDkgMzY1LjI0IDY2MS44OSBDIDM2My44MiA2NTcuODEgMzYxLjI3IDY1My45OCAzNTkuODQgNjQ5Ljg4IEwgMzYwLjg5IDY1MC4wMCBDIDM2My41MyA2NTIuOTIgMzYzLjk5IDY1OC4wOSAzNjYuOTQgNjYwLjY3IEMgMzY3LjgwIDY1OS43OCAzNjguNjMgNjU4Ljg3IDM2OS40MyA2NTcuOTQgQyAzNjcuODIgNjU1LjAxIDM2Ni4xMCA2NTIuMjEgMzY0Ljc2IDY0OS4xMyBDIDM2NC4xNyA2NDcuNjUgMzYyLjMyIDY0NS42NyAzNjIuOTkgNjQ0LjA3IEMgMzY2LjIwIDY0Ni45MSAzNjcuMTIgNjUyLjQ5IDM3MC4wMCA2NTUuOTIgQyAzNzIuMjEgNjUzLjY1IDM3MS41MSA2NDguNzEgMzY5LjczIDY0Ni4yNCBDIDM2NC4wOSA2MzcuMTYgMzU2LjMwIDYyOC4wMCAzNTUuMDEgNjE3LjAxIEMgMzU1LjA3IDYxMi43NCAzNTMuODMgNjA4LjI5IDM1NC40MSA2MDQuMTIgTCAzNTUuOTggNjA0LjQ1IEMgMzU1Ljk2IDYwOS42MiAzNTYuNjcgNjE0LjgxIDM1Ny41NSA2MTkuODkgQyAzNjAuMzEgNjI5LjQxIDM2Ny4yMSA2MzguMzggMzcyLjMxIDY0Ni43OSBDIDM3My45MCA2NDkuNjQgMzczLjk4IDY1Mi43MSAzNzIuNjUgNjU1LjY4IEMgMzcwLjk4IDY1OS4yNSAzNjkuMTAgNjYyLjg2IDM2NS4xMSA2NjQuMjEgQyAzNjAuOTggNjY2LjA2IDM1NS44NyA2NjQuMzAgMzUzLjAwIDY2MS4wMCBDIDM0OC42MiA2NTYuNDAgMzQ3LjY5IDY1MC41OSAzNDUuMzYgNjQ1LjE5IEMgMzQ0Ljk4IDY0Ny41NiAzNDQuNDQgNjUxLjIyIDM0Mi4xOCA2NTIuNTggQyAzNDAuNjggNjUzLjUzIDMzOS4zMSA2NTMuMDYgMzM3Ljk5IDY1Mi4xMCBDIDMzNS43NiA2NDIuNTUgMzM5LjAwIDYzNC42MCAzMzkuMzIgNjI1LjAwIEMgMzQwLjA5IDYxNC42OCAzMzUuNDggNjA2LjAzIDMzMy41OCA1OTYuMDIgQyAzMzEuNzYgNTg3LjYyIDMzMi40MSA1NzkuNTIgMzI4LjM1IDU3MS42NiBDIDMyNi4zNCA1NjguMzYgMzI0LjIzIDU2NC41MCAzMjEuMjAgNTYyLjA1IEMgMzIwLjI4IDU3MS40OSAzMTguNjQgNTgwLjQ2IDMxOS4wMSA1OTAuMDEgQyAzMTkuNjMgNjAwLjYyIDMzMC44MSA2MTAuMzIgMzMwLjA0IDYyMS4wMCBDIDMzMC43OSA2MjkuMTMgMzIzLjk1IDYzNS44OCAzMjMuNzUgNjQ0LjAwIEMgMzIzLjU5IDY0OS4zMyAzMjMuNTIgNjU0LjY3IDMyMy4xMCA2NjAuMDAgQyAzMjIuOTYgNjY5LjM1IDMyMS4yMyA2NzguNjUgMzIwLjk4IDY4OC4wMCBDIDMyMS4wMSA3MDEuNDMgMzI2LjIwIDcxMi4yMCAzMjkuMDMgNzI1LjAyIEMgMzMwLjkxIDczMi4yMCAzMzAuMTAgNzM5LjY4IDMyOS45MyA3NDYuOTkgQyAzMjguOTQgNzU0LjYxIDMyOC4wOCA3NjIuMzggMzI3LjEwIDc3MC4wMCBDIDMyNi45MyA3NzQuOTcgMzI2Ljk5IDc4MC4wMiAzMjcuMDYgNzg1LjAwIEMgMzI4LjAyIDc5MS43NCAzMjkuMDYgNzk4LjEyIDMyOC40MyA4MDUuMDEgQyAzMjguMDUgODA3LjUxIDMyNy42MCA4MDkuOTQgMzI0Ljk0IDgxMC44OSBDIDMxNS4wNyA4MTQuNzkgMzAyLjM4IDgxMy4zNSAyOTIuMDAgODEzLjI3IEMgMjg2Ljk5IDgxMy43NiAyODIuMDQgODEzLjUxIDI3Ny4wOCA4MTIuNzUgQyAyNzQuOTkgODEyLjQ2IDI3NC42NSA4MTAuNTMgMjczLjAwIDgxMC4wMCBDIDI2My4wOSA4MTIuMDYgMjUzLjA4IDgxMS45NiAyNDMuMDggODEwLjQ1IEMgMjQwLjY5IDgxMC4wNiAyMzcuNDIgODA4LjY3IDIzNi45MiA4MDYuMDMgQyAyMzcuNTQgODAzLjg1IDIzOS41MCA4MDMuMTIgMjQxLjQ5IDgwMi41NyBDIDI0Ni4yMCA4MDEuNDQgMjUxLjIwIDgwMS4wMyAyNTUuODggNzk5LjY5IEMgMjU5LjMzIDc5OC40NCAyNjIuNDMgNzk2LjU1IDI2NC4yOCA3OTMuMjggQyAyNjguNjMgNzg2LjA3IDI3MS43MiA3NzcuNDggMjcxLjY4IDc2OS4wMCBDIDI3MS4zOCA3NTcuMjAgMjY4LjgwIDc0NS45OSAyNjYuMjYgNzM0LjUzIEMgMjY0LjE4IDcyNC40NiAyNjAuODcgNzEzLjI5IDI2MS4xMyA3MDIuOTkgQyAyNjMuNTQgNjg4Ljk3IDI2MC43MSA2NzQuMTggMjYxLjcxIDY2MC4wMCBDIDI2MS45MyA2NDMuODkgMjYzLjkyIDYyOC4wNiAyNzQuMTUgNjE1LjAwIEMgMjcyLjAzIDYwMy4wNSAyNzEuMDUgNTkxLjAwIDI2OS4yMSA1NzkuMDEgQyAyNjguMTIgNTY2Ljg3IDI2Ny44NCA1NTUuMDIgMjcwLjI4IDU0My4wMCBDIDI3MC41NSA1NDEuNjkgMjcwLjk4IDU0MC40MiAyNzAuOTIgNTM5LjA4IEMgMjYxLjUwIDU0Mi4zOCAyNTEuODAgNTQ1LjA1IDI0Mi4wMSA1NDYuOTcgQyAyMzguOTkgNTQ3LjUzIDIzNi4xNyA1NDguMDAgMjMzLjIzIDU0Ni43NyBDIDIzMS4zOCA1NDUuMTAgMjMwLjY5IDU0Mi4zNyAyMzAuMjIgNTQwLjAxIEMgMjI5LjMyIDUzMS4zOCAyMzAuNjcgNTIzLjUyIDIzMS4wMiA1MTUuMDAgQyAyMzIuOTggNDk1LjA1IDIzNy43MiA0NzUuNzYgMjM5LjkxIDQ1Ni4wMCBDIDI0MC4wMSA0NTEuNjIgMjQwLjMzIDQ0NC45NiAyNDQuMDEgNDQxLjk2IEMgMjQ3LjMzIDQ0MS42NSAyNTIuNjYgNDQyLjM3IDI1My45NSA0NDYuMDIgQyAyNTQuNzMgNDUwLjE4IDI1My44OSA0NTMuODYgMjUzLjg5IDQ1OC4wMiBDIDI1NS41NiA0NTcuMjkgMjU3LjEzIDQ1Ni45MSAyNTguOTcgNDU3LjAwIEMgMjYwLjU4IDQ1Ny45MSAyNjAuMTUgNDYwLjA2IDI1OS44MCA0NjEuNTcgQyAyNTguMjEgNDY1Ljk4IDI1Ni40MSA0NzAuMzQgMjU0LjY1IDQ3NC42OSBDIDI1Mi43MyA0NzkuNjQgMjQ5LjgxIDQ4My42NyAyNDkuMTggNDg5LjA4IEMgMjQ4LjA0IDUwMC42NSAyNTIuMDIgNTEyLjcxIDI1Mi4wNCA1MjQuMzMgQyAyNTMuMzYgNTI0LjQ0IDI1NC42NiA1MjMuMzcgMjU1Ljc4IDUyMi43NiBDIDI2NS41OSA1MTYuNzEgMjc2Ljg5IDUxMS45MyAyODUuOTcgNTA0Ljg4IEMgMjg4LjIxIDUwMi42NiAyODkuMTkgNDk4Ljk3IDI5MC4zNyA0OTYuMTAgQyAyOTIuNDIgNDkwLjY1IDI4OS4zNyA0ODQuNzUgMjg3LjI5IDQ3OS43NSBDIDI4NC43NSA0NzYuNTcgMjgwLjExIDQ3OS40MCAyNzcuMDYgNDc2LjAxIEMgMjc0LjU2IDQ3Mi40MSAyNzUuMzggNDY2LjMzIDI3NC43MyA0NjIuMjMgQyAyNzMuMTQgNDU5LjU0IDI3MC4xMiA0NTcuODAgMjcyLjI4IDQ1NC4yOSBDIDI3NS40MCA0NTAuNjUgMjc1LjI2IDQ0Ni41MiAyNzUuODcgNDQyLjAwIEMgMjc2LjA4IDQzNi40MSAyNzcuOTYgNDMxLjAwIDI4Mi4xMCA0MjcuMTIgQyAyODcuMjUgNDIyLjg1IDI5NC4yNyA0MjAuNzcgMzAwLjkxIDQyMS41MCBaIiBmaWxsPSIjODE4MTgxIiAvPgo8cGF0aCBkPSJNIDE2MC4wNiA0MzUuMTcgQyAxNjMuNjkgNDM1LjM1IDE2NS45MyA0MzkuODMgMTY3LjM2IDQ0Mi42NyBDIDE3MC42MyA0NTAuMDcgMTcxLjMwIDQ1OS4wMCAxNzEuMDAgNDY3LjAwIEMgMTcwLjk5IDQ3OS42NiAxNzAuNjQgNDkxLjU4IDE3NC4yOCA1MDMuOTMgQyAxNzguMDMgNTE2LjE1IDE4MS43OSA1MjkuMTQgMTc5Ljk0IDU0Mi4wMSBDIDE3OS4wMSA1NDUuODkgMTc3LjYyIDU0OS44MiAxNzIuOTkgNTUwLjAzIEMgMTY0LjgxIDU0OC4xMyAxNTcuMTMgNTQ0Ljk5IDE0OS4wOSA1NDIuNjcgQyAxNDUuMzYgNTQxLjgxIDE0MS45MiA1NDAuNjUgMTM4LjAzIDU0MS4wNyBDIDEzOC45MyA1NDcuOTcgMTM5LjkxIDU1NS4wMiAxNDAuMDUgNTYyLjAwIEMgMTM5Ljc1IDU3NC45NSAxMzcuNzMgNTg3LjIyIDEzNi4xNiA2MDAuMDAgQyAxMzYuMDIgNjA0LjA0IDEzNC4yNCA2MDguMDEgMTM0LjA1IDYxMS45NyBDIDE0Mi4wNCA2MjIuMjkgMTQzLjcxIDYzNi40MSAxNDQuOTYgNjQ5LjAwIEMgMTQ1LjExIDY1OS43MyAxNDUuNzYgNjcwLjI4IDE0NC41MiA2ODAuOTkgQyAxNDMuOTAgNjg2LjgxIDE0NC40MiA2OTIuMjggMTQ1LjU5IDY5Ny45OSBDIDE0Ny4wNiA3MDUuMzAgMTQ0LjkwIDcxMS4zMCAxNDMuNDMgNzE4LjM0IEMgMTQxLjUwIDcyNy41NiAxMzkuNDEgNzM2LjcwIDEzNy44NCA3NDUuOTkgQyAxMzYuMjkgNzU0LjU1IDEzNC45OSA3NjMuMzAgMTM2LjAyIDc3Mi4wMSBDIDEzNi43OCA3NzkuNzEgMTM5Ljc0IDc4OC4wOSAxNDUuNDkgNzkzLjUwIEMgMTUwLjAzIDc5Ny40MSAxNTYuMzMgNzk3LjM4IDE2MS45MyA3OTguNDAgQyAxNjQuOTggNzk5LjIxIDE2Ny44NyA3OTkuNDUgMTcwLjA1IDgwMi4wMSBDIDE3MC4wNCA4MDIuOTggMTcwLjAzIDgwMy45NSAxNzAuMDMgODA0LjkxIEMgMTY4Ljc4IDgwNS44OCAxNjcuNjUgODA2LjgwIDE2Ni4wMyA4MDcuMDMgQyAxNTYuMDMgODA5LjEzIDE0NS4wNCA4MDcuNjEgMTM0Ljk1IDgwNi43MCBDIDEzMy42NiA4MDkuMTMgMTMyLjA2IDgwOS43NiAxMjkuMzIgODA5LjM0IEMgMTI5LjY3IDgwOC40OCAxMzAuMTkgODA3LjY4IDEzMC44NyA4MDYuOTYgQyAxMjkuODYgODA1LjU3IDEyOC40NCA4MDQuODggMTI2Ljk1IDgwNC4xMiBDIDEyMC41MSA4MDAuOTggMTE0LjM3IDc5Ny4yOSAxMDcuODggNzk0LjI0IEMgMTAzLjY2IDc5MS45NSA5OS43MyA3ODkuNzkgOTguMDggNzg0Ljk2IEMgOTcuMjQgNzY5LjIwIDk5Ljg0IDc1NC42NSAxMDEuMjAgNzM5LjAxIEMgMTAyLjI1IDczMy4yNSAxMDEuNzEgNzI3LjU2IDEwMy4yMCA3MjIuMDcgQyAxMDQuOTYgNzE2Ljc4IDEwOC40MiA3MTIuNDggMTA5LjgyIDcwNy4wMCBDIDExMC42NCA2OTYuMjUgMTEyLjY2IDY4NS41NSAxMTQuOTMgNjc1LjAxIEMgMTE3LjI1IDY2My41MSAxMTguODMgNjUxLjY5IDEyMC41NyA2NDAuMDggQyAxMjEuMjQgNjM1Ljc4IDEyMS42NCA2MzEuNzkgMTIzLjY4IDYyNy44NiBDIDEyNi4zMiA2MjIuMjUgMTMwLjY5IDYxNi45NyAxMzIuMjMgNjExLjAxIEMgMTM0LjMxIDYwMC43MSAxMzYuMjMgNTkwLjQ1IDEzNy41NSA1NzkuOTkgQyAxMzkuOTIgNTU4LjIwIDEzNy40NyA1MzUuNTQgMTI2Ljc5IDUxNi4wOSBDIDEyMy4wMCA1MDkuMDkgMTE3LjIzIDUwMS45MyAxMTUuOTUgNDk0LjAxIEMgMTE1LjA3IDQ4OC4zNSAxMTYuNzcgNDgzLjI0IDExOS4yNCA0NzguMjUgQyAxMjEuOTAgNDc0LjQ4IDEyNi43OSA0NzcuMjQgMTI5LjcwIDQ3My42OCBDIDEzMi42OSA0NjkuODUgMTI5LjQ1IDQ2My4yMyAxMzIuMDIgNDU5LjAwIEMgMTMyLjg3IDQ1Ny45NSAxMzQuNzcgNDU3Ljc2IDEzNi4wMSA0NTcuMjYgQyAxMzUuMzIgNDU4LjY0IDEzMy44MSA0NTkuNDggMTMzLjQwIDQ2MC45NSBDIDEzMy4yNyA0NjUuMzggMTMzLjc2IDQ3MS40NiAxMzEuMzYgNDc1LjM3IEMgMTI5Ljc1IDQ3Ny44NSAxMjYuNjQgNDc2Ljk1IDEyMy45NSA0NzcuOTQgQyAxMjAuNDkgNDc4LjczIDExOS4yMSA0ODIuOTEgMTE4LjQwIDQ4NS45MCBDIDExNi43NCA0OTMuNDkgMTE5LjI4IDUwMC4zOSAxMjMuMzAgNTA2LjczIEMgMTI0LjQxIDUwNy42NyAxMjUuNjUgNTA3LjY5IDEyNy4wMSA1MDguMDQgQyAxMzMuODggNTA5LjUxIDEzOS45NyA1MTIuNDIgMTQ2LjEwIDUxNS43NiBDIDE0OC4zOSA1MTcuMTggMTUwLjUyIDUxOC43NiAxNTMuMDMgNTE5LjgxIEMgMTU0LjQxIDUxOS4wOSAxNTUuMjMgNTE4LjQyIDE1NS43NiA1MTYuOTQgQyAxNTYuNjggNTEyLjE1IDE1Ni43NSA1MDcuMjggMTU3LjY2IDUwMi40OSBDIDE1OC4yMyA0OTcuMDIgMTU4LjkzIDQ5MS4zMyAxNTcuMTkgNDg2LjAwIEMgMTU1LjM5IDQ4MC40MiAxNTQuMzkgNDc0LjcxIDE1My4xOCA0NjkuMDAgQyAxNTIuMjEgNDY0LjQzIDE0OS4yNiA0NjAuNDAgMTQ3Ljg4IDQ1Ni4wNCBDIDE0Ny42MiA0NTQuMzIgMTQ2LjQzIDQ1MS44MiAxNDguOTQgNDUxLjI2IEMgMTUyLjA3IDQ1MS44NCAxNTQuMDQgNDU1LjUyIDE1NS43OCA0NTcuOTAgQyAxNTYuODQgNDUyLjA2IDE1My45MyA0NDcuMDcgMTU1LjU2IDQ0MS40OCBDIDE1Ni4xOSA0MzguODMgMTU2Ljk2IDQzNS43NyAxNjAuMDYgNDM1LjE3IFoiIGZpbGw9IiM4MTgxODEiIC8+CjxwYXRoIGQ9Ik0gMTYyLjMyIDQzOC4yMCBDIDE2NC4yMiA0NDAuMTYgMTY1LjIwIDQ0My4wNyAxNjYuMjIgNDQ1LjU2IEMgMTcwLjM5IDQ1OS45MiAxNjguOTQgNDczLjgzIDE2OS42OCA0ODguMDEgQyAxNjkuODQgNTAyLjUxIDE3Ny4wNyA1MTUuNjYgMTc4LjM4IDUzMC4wMCBDIDE3OS4wMSA1MzQuNDMgMTc5LjEyIDUzOS45NiAxNzcuMTUgNTQ0LjA0IEMgMTc1LjQxIDU0Ny4zNSAxNzIuNDEgNTQ3Ljg5IDE2OC45OCA1NDYuOTYgQyAxNjAuMjcgNTQ0LjQ5IDE1Mi4wNiA1NDEuNDcgMTQzLjA5IDUzOS43OCBDIDE0MS4wNCA1MzkuNTEgMTM5LjE1IDUzOS45NCAxMzcuMjQgNTM4Ljg4IEMgMTM0Ljk4IDUyOC42MCAxMzAuOTggNTE4LjgyIDEyNS4xNSA1MTAuMDMgQyAxMjYuNDQgNTA5Ljk4IDEyNy42OCA1MTAuMTcgMTI4Ljg4IDUxMC42MCBDIDEzNy4wMiA1MTMuMTggMTQ1LjE3IDUxNi45MCAxNTEuOTYgNTIyLjEzIEMgMTUyLjc5IDUyMi44OCAxNTMuNzcgNTIzLjE5IDE1NC45MCA1MjMuMDUgQyAxNTkuMTMgNTE2LjIyIDE1OC40NyA1MDkuMTkgMTU5LjMwIDUwMS40OSBDIDE1OS44MiA0OTguNjQgMTYwLjE0IDQ5NS45MCAxNjAuMDMgNDkzLjAwIEMgMTYwLjMyIDQ4OC4xMiAxNTguMjkgNDgzLjY1IDE1Ny4yMyA0NzguOTcgQyAxNTYuMzAgNDc0LjU2IDE1NS41NyA0NzAuMjIgMTU0LjMzIDQ2NS44NyBDIDE1My4yNiA0NjEuOTEgMTUwLjgwIDQ1OC4wNCAxNTAuMDIgNDU0LjAwIEMgMTUyLjc1IDQ1NS41MCAxNTQuMzQgNDU4LjgxIDE1NS40MyA0NjEuNTggQyAxNTcuMTQgNDYzLjczIDE1OC4wNiA0NjAuNDMgMTU4LjAyIDQ1OS4wNCBDIDE1OC41NSA0NTQuMjggMTU3LjE5IDQ0OS43OSAxNTcuMjUgNDQ1LjExIEMgMTU3Ljc4IDQ0NS43MSAxNTguMzEgNDQ2LjMyIDE1OC44NCA0NDYuOTMgQyAxNTguNjUgNDQ0LjMwIDE1Ny40NiA0NDEuNTAgMTU3LjkwIDQzOC45MSBMIDE1OC42OCA0MzkuMjEgQyAxNjAuNDIgNDQyLjU4IDE1OS44OCA0NDYuNDQgMTYxLjYxIDQ0OS43MyBDIDE2MS45NyA0NDYuNjYgMTYwLjcxIDQ0My44NyAxNjEuMDUgNDQwLjc5IEMgMTYzLjYxIDQ0Mi40MSAxNjIuNTIgNDQ3LjkxIDE2NC44MSA0NDkuOTUgQyAxNjQuMzUgNDQ1LjkxIDE2My41MCA0NDIuMDkgMTYyLjMyIDQzOC4yMCBaIiBmaWxsPSIjZmZmZmZmIiAvPgo8cGF0aCBkPSJNIDI0Ni4wOCA0NDQuMDYgQyAyNDYuODAgNDQ2LjUwIDI0NC40MiA0NDguOTUgMjQ1LjQ4IDQ1MS43NSBDIDI0NS44MCA0NDkuMTQgMjQ2LjQ1IDQ0Ni42MCAyNDYuOTYgNDQ0LjAyIEwgMjQ4LjA2IDQ0NC4xMyBDIDI0OS4wNiA0NDYuOTIgMjQ3Ljg5IDQ1MS4wMyAyNDcuMjMgNDUzLjkwIEMgMjQ5LjE5IDQ1MS41MiAyNDguMzggNDQ3Ljg0IDI0OC45NSA0NDUuMDkgTCAyNTAuMDYgNDQ1LjA3IEMgMjUxLjEyIDQ0Ny41NyAyNTAuMDkgNDUyLjAxIDI0OS4zOCA0NTQuNzIgQyAyNTEuMjQgNDUyLjg3IDI1MC41NyA0NDkuNDMgMjUwLjg5IDQ0Ni45OCBMIDI1MS44OSA0NDcuMTUgQyAyNTMuOTQgNDUxLjEyIDI1MS4yNSA0NTcuMTcgMjUxLjQzIDQ2MS41OCBDIDI1My41NiA0NjMuNjAgMjU1LjU1IDQ1Ny40NyAyNTcuOTQgNDU3LjcxIEMgMjU4LjAyIDQ1OS4zOCAyNTguMDcgNDYwLjkxIDI1Ny4zOSA0NjIuNDggQyAyNTQuOTEgNDY5LjQ1IDI1Mi4wMyA0NzYuMjUgMjQ4Ljc2IDQ4Mi44OCBDIDI0Ni44OCA0ODYuODYgMjQ3LjI5IDQ5MC43MyAyNDcuNDggNDk1LjAwIEMgMjQ4LjMyIDUwNS41OSAyNTAuNTkgNTE1Ljg2IDI1MC42MiA1MjYuNTUgQyAyNTEuNzIgNTI4LjIyIDI1Mi44NSA1MjYuNTkgMjUzLjk3IDUyNS45MyBDIDI2MC43OCA1MjAuODUgMjY4Ljg2IDUxNy43NCAyNzYuMDkgNTEzLjIyIEMgMjc4LjczIDUxMi4wNiAyODEuMjggNTA5LjUzIDI4NC4wNCA1MDkuMDEgQyAyODMuNTEgNTEwLjQ2IDI4Mi42MCA1MTEuNjIgMjgxLjc4IDUxMi45MiBDIDI3Ny4yNSA1MjAuMzYgMjc0LjIxIDUyOC4zOSAyNzEuNzUgNTM2LjcyIEMgMjYxLjI2IDU0MC44NCAyNTAuMTMgNTQzLjUwIDIzOS4wOSA1NDUuNjcgQyAyMzcuMjMgNTQ2LjA2IDIzNS4wMSA1NDUuNzMgMjMzLjc3IDU0NC4xNyBDIDIzMi4wMSA1NDEuNjYgMjMxLjg4IDUzOC45OCAyMzIuMDAgNTM2LjAwIEMgMjMxLjgxIDUxMC45NyAyMzcuNTcgNDg4LjYyIDI0MS4wMiA0NjQuMDAgQyAyNDEuMjQgNDU4LjMzIDI0MS45NiA0NTIuNTQgMjQzLjI2IDQ0Ny4wMiBDIDI0My44MSA0NDUuNTUgMjQzLjg3IDQ0My40MCAyNDYuMDggNDQ0LjA2IFoiIGZpbGw9IiNmZmZmZmYiIC8+CjxwYXRoIGQ9Ik0gMTI2Ljg2IDQ0OC43NyBDIDEyNS41NyA0NTAuOTkgMTI0LjExIDQ1MC41NSAxMjMuMjggNDQ4LjMzIEMgMTI0LjE4IDQ0Ni40NyAxMjcuMDcgNDQ2LjUzIDEyNi44NiA0NDguNzcgWiIgZmlsbD0iIzgxODE4MSIgLz4KPHBhdGggZD0iTSAyODcuNjEgNDUwLjM3IEMgMjg3LjA3IDQ1Mi45OSAyODMuODQgNDUyLjQ2IDI4My45OSA0NTAuMDYgQyAyODUuNDUgNDQ4LjE2IDI4Ni4zMCA0NDguNzUgMjg3LjYxIDQ1MC4zNyBaIiBmaWxsPSIjODE4MTgxIiAvPgo8cGF0aCBkPSJNIDEwOC42MyA0NTAuMzggQyAxMDkuMDcgNDUyLjcyIDEwNS43NyA0NTEuNDAgMTA0Ljc1IDQ1Mi43NSBDIDEwMi45OCA0NTQuNTAgMTA0LjQwIDQ1Ny42NyAxMDUuNTggNDU5LjQxIEMgMTA3LjExIDQ2MS43NCAxMDYuODYgNDY0LjExIDEwNy4zMCA0NjYuNzAgQyAxMDguNTggNDY4LjIwIDExMS41NSA0NjYuMjIgMTEzLjA5IDQ2NS44MSBDIDExNC4yNCA0NjkuMjQgMTA4LjE4IDQ2OS4yOCAxMDYuMjEgNDY4Ljg0IEMgMTA1LjQxIDQ2Ni42MSAxMDUuMDYgNDY0LjM5IDEwNC44MiA0NjIuMDQgQyAxMDMuOTIgNDU5Ljg0IDEwMS44NSA0NTcuNDQgMTAxLjk2IDQ1NS4wMCBDIDEwMS43NiA0NTEuODcgMTA1LjcwIDQ0OC44MSAxMDguNjMgNDUwLjM4IFoiIGZpbGw9IiM4MTgxODEiIC8+CjxwYXRoIGQ9Ik0gMzA1Ljk5IDQ1Mi4wMCBDIDMwNi43OSA0NTIuODggMzA3Ljk5IDQ1My42OSAzMDguMzEgNDU0Ljg1IEMgMzA5LjI5IDQ1Ny4yNSAzMDcuODIgNDU5LjY1IDMwNi43MSA0NjEuNzEgQyAzMDUuMTUgNDY0LjE1IDMwNS41OCA0NjcuMjYgMzA0LjY4IDQ2OS43MSBDIDMwMy40MCA0NzEuMzUgMjk3LjQzIDQ2OS45OCAyOTcuODkgNDY3Ljc3IEMgMjk5LjkzIDQ2OC41MyAzMDEuNTUgNDY5LjMwIDMwMy44MSA0NjguODggQyAzMDQuMDcgNDY2LjI3IDMwNC4xMCA0NjMuNzIgMzA1LjM4IDQ2MS4zOCBDIDMwNi4zNiA0NTkuMTkgMzA3Ljk3IDQ1Ny41NyAzMDYuOTYgNDU1LjAzIEMgMzA2LjMxIDQ1My4xNCAzMDMuNzggNDUzLjA2IDMwMi4xNSA0NTIuNzQgQyAzMDIuODIgNDUxLjQ2IDMwNC43OCA0NTIuMTQgMzA1Ljk5IDQ1Mi4wMCBaIiBmaWxsPSIjODE4MTgxIiAvPgo8cGF0aCBkPSJNIDg3Ljk0IDU1OC4wMiBDIDg4LjM0IDU1OC42NyA4OC42MSA1NTkuMzQgODguNzUgNTYwLjA0IEMgODkuODEgNTY2LjMzIDkwLjI3IDU3Mi42NiA5MC45NiA1NzkuMDAgQyA5MS4xMiA1ODIuNTMgOTEuOTEgNTg1LjY1IDkxLjQwIDU4OS4wMSBDIDkxLjA2IDU5My42MyA4OC40MCA1OTguMDggODYuMzAgNjAyLjEzIEMgODUuNjIgNjAzLjkxIDgzLjcxIDYwNC4yOSA4My4xMiA2MDYuMDYgQyA4MS40MSA2MTAuMDMgNzkuNzIgNjE0LjYzIDgwLjAxIDYxOS4wMCBDIDgwLjQ1IDYyNC4yMiA4Mi41MCA2MjguNTEgODQuODEgNjMzLjA4IEMgODcuODQgNjM5LjI4IDg2LjA2IDY0NS40MSA4Ni44NSA2NTIuMDEgQyA4Ny4xMiA2NjQuMzYgODguMzYgNjc2LjYwIDg4LjQ0IDY4OS4wMSBDIDg4LjEyIDcwMy40NCA4Mi4xMSA3MTUuNTUgODAuMzEgNzI5LjUwIEMgNzguMjcgNzQ2LjczIDg0Ljg5IDc2MS44NSA4My4xNyA3NzkuMDAgQyA4My4zOCA3ODYuNDcgODEuMzIgNzkzLjU3IDgxLjY5IDgwMS4wMSBDIDgxLjk1IDgwMi43NSA4MS44MCA4MDQuNTMgODMuMzEgODA1LjcwIEMgODUuODYgODA3LjI2IDg5LjAyIDgwOC4wNSA5Mi4wMCA4MDcuOTkgQyAxMDIuOTIgODA4LjYyIDExMy4wMyA4MDguOTcgMTIzLjk5IDgwOC42MiBDIDEyNS4zNyA4MDguNzAgMTI5LjMxIDgwNy40MSAxMjguNjMgODA5Ljk5IEMgMTIzLjA4IDgxMC4zNiAxMTcuNTUgODEwLjI5IDExMi4wMCA4MTAuMjcgQyAxMDMuMTAgODEwLjIzIDkzLjgzIDgxMC40MiA4NS4wNyA4MDguODAgQyA4MC40OSA4MDcuNjQgNzkuODYgODAzLjAyIDgwLjEwIDc5OS4wMSBDIDgwLjM4IDc5My42NyA4MC45MyA3ODguMzUgODEuMDkgNzgzLjAxIEMgODMuMzggNzY3LjI4IDc3LjQ4IDc1MC42OSA3Ny45NyA3MzUuMDAgQyA3Ny41NSA3MTkuNjIgODcuMDcgNzA1Ljk4IDg3LjAwIDY5MC4wMCBDIDg2Ljg1IDY4NC4zMyA4Ni42NSA2NzguNjQgODYuMDEgNjczLjAxIEMgODUuOTYgNjY3LjUwIDg1LjUxIDY2MS45MyA4NC44MiA2NTYuNDcgQyA4NS4zNiA2NTAuOTUgODQuMzggNjQ1LjUzIDg0LjY2IDY0MC4wMCBDIDg0LjgzIDYzMy44MSA3OS41MyA2MjguMjkgNzguNTUgNjIyLjI1IEMgNzcuMDQgNjE3LjIwIDc4Ljk1IDYxMS42MyA4MC43MyA2MDYuODcgQyA4MS4yNyA2MDUuMDcgODIuNDEgNjA0LjA1IDgzLjY0IDYwMi43MyBDIDg3LjAyIDU5Ni40MSA4OS44OSA1OTAuNDAgODkuMjEgNTgzLjAwIEMgODguODUgNTc1LjY5IDg4LjQxIDU2OC4xOSA4Ni43OCA1NjEuMDMgQyA4Mi40MCA1NjYuMjcgNzguMDcgNTcyLjU0IDc3LjMwIDU3OS40OCBDIDc2LjE4IDU4OC40MSA3NS41NiA1OTcuMDEgNzEuMTEgNjA1LjA4IEMgNjkuMDEgNjEwLjQ4IDY4LjQxIDYxNS40NSA2Ny40MiA2MjAuOTkgQyA2Ny4zMyA2MzIuNjcgNzIuNjIgNjQyLjEzIDcxLjAwIDY1My45NSBDIDY5LjA1IDY1NC44OCA2OC4wNiA2NTUuMDIgNjYuMTIgNjUzLjkzIEMgNjQuODEgNjUyLjc1IDY0LjM3IDY1MC42NyA2Mi45MiA2NDkuNzIgQyA2MC40NyA2NTYuNDUgNTYuNjMgNjYzLjY5IDUwLjAwIDY2Ny4xMiBDIDQ2LjEzIDY2OC45NiA0Mi4zMCA2NjYuODAgNDAuNjUgNjYzLjE1IEMgMzguMjcgNjU5LjQ3IDM4LjY3IDY1NS4yNCAzOS4wOSA2NTEuMDYgQyA0MS4zNSA2NDYuNDUgNDMuNjYgNjQyLjAxIDQ1LjA5IDYzNy4wNSBDIDQ3Ljc2IDYyOC42OCA1MC45NyA2MjAuOTYgNTEuNjggNjEyLjA4IEMgNTIuMDcgNjA1Ljg0IDUzLjA3IDU5OS43MSA1My4wMyA1OTMuNDUgTCA1NC41OSA1OTMuMTIgQyA1NS40OSA1OTcuMjMgNTQuNDEgNjAxLjgyIDU0LjI1IDYwNi4wMCBDIDUzLjk1IDYxMy41NyA1Mi4yMiA2MjAuNzggNTAuMTkgNjI4LjAzIEMgNDguMDcgNjM1LjI2IDQ1LjU0IDY0Mi4zMyA0Mi4yMiA2NDkuMDkgQyA0MS4wNSA2NTEuNDMgMzkuNjUgNjUzLjMwIDQwLjAzIDY1Ni4wOCBDIDQxLjY5IDY1NC45MyA0Mi41MSA2NTIuMzQgNDMuNjQgNjUwLjY2IEMgNDQuNjcgNjQ5LjMzIDQ0Ljc0IDY0Ni43MCA0Ni45MSA2NDcuMDYgQyA0NS4xNiA2NTAuODQgNDIuOTcgNjU0LjM2IDQxLjAxIDY1OC4wMiBDIDQwLjkwIDY1OS4yMyA0MS4yMyA2NjAuMDcgNDIuMDAgNjYwLjUxIEMgNDQuMzMgNjU3LjIxIDQ2LjQ1IDY1My43MSA0OC4wOCA2NTAuMDAgQyA0OC42NSA2NDkuNzYgNDkuMjMgNjQ5LjUyIDQ5LjgwIDY0OS4yOCBDIDQ4LjQwIDY1My44NyA0NC45MSA2NTcuODQgNDIuOTggNjYyLjAzIEMgNDIuOTggNjYzLjQ3IDQ0Ljk4IDY2NC4zNCA0Ni4wMSA2NjUuMDMgQyA0Ny40MyA2NjIuMDAgNDguODkgNjU5LjU3IDUxLjAwIDY1Ni45NyBDIDUyLjA4IDY1NS43MiA1My4xNyA2NTMuMDYgNTUuMDEgNjUzLjAzIEMgNTMuNjcgNjU3LjE5IDQ4LjUxIDY2MC42MSA0Ny45NyA2NjQuOTcgQyA0OS41MSA2NjUuMDYgNTAuNDQgNjY0LjU1IDUxLjYxIDY2My42NCBDIDU2LjQ5IDY2MC40MCA1OS42MiA2NTQuNTQgNjAuODAgNjQ4LjkxIEMgNjEuMzAgNjQ2LjgyIDYwLjU4IDY0NC4zMiA2MS4zNiA2NDIuMzQgQyA2My4zMiA2NDEuMDcgNjMuMjIgNjQzLjkwIDYzLjg2IDY0NS4wNSBDIDY0LjU0IDY0Ny41MiA2Ni41MiA2NTIuMDYgNjkuNjUgNjUxLjcyIEMgNjkuODUgNjQyLjU3IDY2LjA5IDYzMy4yNSA2NS43OSA2MjQuMDkgQyA2NS4zNCA2MTcuNjQgNjcuMTIgNjEyLjA3IDY4LjgzIDYwNS45OCBDIDcxLjk2IDU5OS4xNiA3My40NCA1OTEuMzcgNzQuODAgNTgzLjk5IEMgNzUuNjcgNTc1LjU0IDc5LjA4IDU2Ny45MCA4NC4xOSA1NjEuMTkgQyA4NS4wOSA1NjAuMjEgODYuMzUgNTU3LjQzIDg3Ljk0IDU1OC4wMiBaIiBmaWxsPSIjODE4MTgxIiAvPgo8cGF0aCBkPSJNIDM1NS4xNCA1ODcuOTQgQyAzNTQuNzAgNTkzLjIyIDM1Ni4yMSA1OTguNzEgMzU1LjU5IDYwMy44OCBMIDM1NC4wMSA2MDMuNTUgQyAzNTMuOTAgNTk4Ljg3IDM1My42NiA1OTQuMTYgMzUzLjAyIDU4OS41MSBDIDM1My4zOSA1ODguNTQgMzU0LjEwIDU4OC4wMiAzNTUuMTQgNTg3Ljk0IFoiIGZpbGw9IiM4MTgxODEiIC8+CjxwYXRoIGQ9Ik0gMTA2Ljg1IDU5NC4xMyBDIDEwOS4zMSA1OTYuMTEgMTExLjc0IDU5OC45NSAxMTMuMDQgNjAxLjc5IEwgMTExLjc4IDYwMy4wNyBDIDEwOS40NiA2MDEuNzAgMTA2LjIxIDYwMS4zNSAxMDUuNTcgNTk4LjM2IEMgMTA0LjY4IDU5Ni44OSAxMDMuMzkgNTk3LjIxIDEwMS45MSA1OTYuODggQyAxMDIuODQgNTk0Ljk0IDEwNC41MyA1OTMuMzAgMTA2Ljg1IDU5NC4xMyBaIiBmaWxsPSIjODE4MTgxIiAvPgo8cGF0aCBkPSJNIDMwNS4wMCA1OTYuODIgQyAzMDUuMDIgNTk3LjgxIDMwNS4wMyA1OTguODEgMzA1LjAzIDU5OS44MSBDIDMwMy4wMiA2MDEuODggMzAxLjI1IDYwMy42OSAyOTguMzkgNjA0LjUyIEMgMjk4LjIwIDYwNS4zNyAyOTguMDAgNjA2LjIxIDI5Ny44MSA2MDcuMDUgQyAyOTUuNzEgNjA2LjQzIDI5NC45NyA2MDMuNzMgMjk1LjY5IDYwMS45OSBDIDI5Ny42OCA1OTkuNDYgMzAwLjEwIDU5OC45OCAzMDIuOTIgNTk3LjkzIEMgMzAzLjYwIDU5Ny41MyAzMDQuMjkgNTk3LjE1IDMwNS4wMCA1OTYuODIgWiIgZmlsbD0iIzgxODE4MSIgLz4KPHBhdGggZD0iTSAxMzMuNjYgNjE1LjI3IEMgMTQxLjMxIDYyNi44OCAxNDIuNjggNjQwLjQ3IDE0My42MSA2NTQuMDAgQyAxNDQuNTUgNjY0LjM3IDE0My41MCA2NzUuMTQgMTQyLjcyIDY4NS41MiBDIDE0MS43NSA2OTAuOTQgMTQzLjE0IDY5NS43MiAxNDMuOTMgNzAxLjAwIEMgMTQ0Ljg4IDcxMi43MyAxNDAuMDcgNzI1LjQ4IDEzNy43MSA3MzYuOTYgQyAxMzYuMTUgNzQ1LjAzIDEzNC4xMCA3NTMuMjkgMTMzLjcyIDc2MS41MSBDIDEzMi45NSA3NzEuODUgMTM1Ljc0IDc4Mi4xNiAxNDEuMTAgNzkwLjk5IEMgMTQyLjQ5IDc5My4yNyAxNDQuMDUgNzk1LjMyIDE0Ni40NyA3OTYuNTggQyAxNTMuMDYgODAwLjE4IDE2MC44MSA3OTguNzggMTY3LjY0IDgwMi4zNSBDIDE2OC43MyA4MDMuNjYgMTY3LjAxIDgwNC41NSAxNjUuOTMgODA0Ljg1IEMgMTYwLjkwIDgwNi4yMCAxNTUuMTYgODA1Ljk5IDE1MC4wMCA4MDUuOTkgQyAxNDQuNjUgODA2LjA4IDEzOS4yNCA4MDUuNzAgMTMzLjk5IDgwNC42NyBDIDEyOS43MiA4MDMuNTIgMTI1LjY1IDgwMS4yOCAxMjEuNjggNzk5LjMzIEMgMTE5LjI0IDc5Ny45OCAxMTYuODcgNzk3LjI3IDExNS43NSA3OTQuNDQgQyAxMTUuMjcgNzk0LjY4IDExNC43OSA3OTQuOTIgMTE0LjMxIDc5NS4xNyBDIDExMS43MSA3OTQuMTAgMTA5LjQ1IDc5Mi40MCAxMDYuOTEgNzkxLjE2IEMgMTA0LjU1IDc4OS44NCAxMDEuNTggNzg4LjI0IDEwMC4yMyA3ODUuODAgQyA5OS4xNiA3ODEuMTAgMTAwLjE1IDc3Ni43NSAxMDAuMTAgNzcyLjAwIEMgMTAxLjMxIDc1Ni4zNCAxMDMuMjggNzQwLjYyIDEwNC40MyA3MjUuMDggQyAxMDUuNTAgNzIxLjUzIDEwNy45NCA3MTguNTUgMTA5LjE2IDcxNS4wMiBDIDExMi40MSA3MDcuNDAgMTEyLjgwIDY5OS42NiAxMTMuODMgNjkxLjUzIEwgMTE0LjU1IDY5MS4xOCBDIDExNi4yMSA2OTQuMzkgMTE3Ljg4IDY5Ny41NyAxMTkuMjAgNzAwLjkzIEMgMTIwLjQ3IDcwMy42MSAxMTkuOTMgNzA3LjEwIDExOS45NCA3MTAuMDEgQyAxMTguNTIgNzE4LjU2IDExMy4yNSA3MjYuODggMTExLjI5IDczNS4wNiBDIDExMC40MiA3NDAuMTYgMTExLjM1IDc0Ni4wNSAxMTEuODAgNzUxLjA5IEMgMTEyLjMyIDc2Mi4yMiAxMTUuNTQgNzczLjA3IDExNC45OCA3ODQuMDAgQyAxMTUuMTAgNzg3LjMwIDExNC4wMSA3OTAuNTggMTE0LjM3IDc5My44NCBMIDExNS45NCA3OTMuNTIgQyAxMTYuMTkgNzg4Ljk3IDExNy4xMiA3ODQuNTggMTE3LjAyIDc4MC4wMSBDIDExNy40OCA3NzAuNzkgMTE0LjU2IDc2MS4yMSAxMTMuODIgNzUyLjAxIEMgMTEyLjk1IDc0Ni42OCAxMTIuODcgNzQxLjM3IDExMy4wNiA3MzUuOTggQyAxMTMuNzcgNzMwLjQxIDExNi42MCA3MjYuMDQgMTE4LjQ2IDcyMC4zOSBDIDEyMC43OSA3MTQuMjIgMTIzLjA5IDcwOC44NiAxMjEuNzAgNzAyLjA4IEMgMTIwLjAwIDY5Ni44MSAxMTcuMDggNjkyLjM0IDExNC42NSA2ODcuNDcgQyAxMTQuNTggNjgzLjg4IDExNS45MCA2ODAuMDggMTE2LjQ4IDY3Ni41MyBDIDExOS42MiA2NjIuNjQgMTIxLjI0IDY0OC41NSAxMjMuMzggNjM0LjUwIEMgMTI0LjczIDYyNy44NCAxMjguOTYgNjIzLjYyIDEzMS42MiA2MTcuNjggQyAxMzIuMTIgNjE2Ljk1IDEzMi4zOCA2MTQuNzIgMTMzLjY2IDYxNS4yNyBaIiBmaWxsPSIjZmZmZmZmIiAvPgo8cGF0aCBkPSJNIDI3NC4xNCA2MTcuOTMgTCAyNzUuMDcgNjE3LjY0IEMgMjc3LjA1IDYyMi4wMiAyNzkuNTQgNjI1Ljg1IDI4Mi4yMyA2MjkuODAgQyAyODQuNzggNjMzLjU0IDI4NC44OSA2MzcuNjQgMjg1Ljc3IDY0Mi4wMSBDIDI4Ny41NSA2NTcuMTUgMjkwLjM1IDY3Mi4xMSAyOTMuNTQgNjg3LjAxIEMgMjkzLjgyIDY4Ny45NiAyOTMuNzYgNjg4Ljc3IDI5My4zNSA2ODkuNDQgQyAyOTAuNTYgNjk0LjA5IDI4Ny45OSA2OTguOTQgMjg1Ljg5IDcwMy45NCBDIDI4NC42MSA3MDYuODAgMjg0LjQ3IDcwOS45NCAyODQuOTIgNzEzLjAwIEMgMjg2LjIxIDcyMi40MSAyOTEuOTkgNzMwLjMzIDI5My42MCA3MzkuMjYgQyAyOTQuMzkgNzQzLjM5IDI5My44NCA3NDcuODAgMjkzLjk5IDc1Mi4wMCBDIDI5Mi45NCA3NjUuNTIgMjg4LjMyIDc4MC42MCAyOTAuNjEgNzkzLjk5IEMgMjkwLjgxIDc5Ni4yOCAyOTAuNjMgNzk5LjEzIDI4OC43MiA4MDAuNzIgQyAyODUuODUgODAzLjI3IDI4MS41MiA4MDQuNTAgMjc4LjA3IDgwNi4xMyBDIDI3MS44MCA4MDkuMjIgMjYzLjg5IDgwOS42MCAyNTcuMDAgODA5Ljk1IEMgMjUxLjQ2IDgwOS4zNCAyNDUuNTIgODA5LjQ2IDI0MC4yMCA4MDcuNzMgQyAyMzkuMjcgODA1Ljc4IDI0MS42MSA4MDQuNzMgMjQzLjA2IDgwNC4xOCBDIDI0OS42MSA4MDIuMTAgMjU3Ljc5IDgwMy4xOCAyNjMuMDEgNzk3LjkyIEMgMjY5LjA4IDc5MS44MSAyNzIuMTQgNzgyLjM5IDI3My4xOCA3NzQuMDAgQyAyNzQuNjAgNzU1LjEyIDI2OC4xMiA3MzQuMzQgMjY0LjE5IDcxNS45MSBDIDI2Mi44NiA3MTEuNjUgMjYyLjY3IDcwNy40MiAyNjMuMzUgNzAzLjAxIEMgMjY1LjYzIDY5MS45MCAyNjIuOTEgNjc5LjM0IDI2My4yOCA2NjguMDAgQyAyNjMuNTkgNjYxLjM1IDI2My43NCA2NTQuNjQgMjY0LjMzIDY0OC4wMSBDIDI2NS41NyA2MzcuOTIgMjY4LjAwIDYyNi4yNCAyNzQuMTQgNjE3LjkzIFoiIGZpbGw9IiNmZmZmZmYiIC8+CjxwYXRoIGQ9Ik0gMjk0LjM0IDY5MS41NCBMIDI5NC40NyA2OTEuNDAgQyAyOTYuMDUgNzAxLjI3IDI5NS43MyA3MDkuODUgMjk5LjU1IDcxOC4zOSBDIDMwMC44MCA3MjEuOTYgMzAzLjMwIDcyNS4yOSAzMDQuMDEgNzI4Ljk4IEMgMzA1LjQzIDc0Ny45MCAzMDcuODYgNzY1Ljk1IDMwOC43MyA3ODUuMDAgQyAzMDguNTEgNzg3LjQ5IDMwNy42NyA3OTAuMDAgMzA1LjQ3IDc5MS40MSBDIDMwMS41NCA3OTQuMTEgMjk3LjI3IDc5Ni4zMiAyOTMuMjAgNzk4Ljc5IEMgMjkyLjEzIDc5MS41MiAyOTEuNjEgNzg0LjM0IDI5Mi4yMyA3NzcuMDEgQyAyOTMuNDggNzcwLjcxIDI5NC4xNSA3NjQuMzYgMjk0LjkxIDc1Ny45OSBDIDI5NS4xMyA3NTAuMjYgMjk3LjAyIDc0My42MiAyOTUuMTAgNzM1Ljk5IEMgMjkyLjUwIDcyNy40OCAyODcuMjUgNzE4LjkyIDI4Ni4zNCA3MTAuMDAgQyAyODYuMjMgNzAzLjYyIDI5MS4zMyA2OTYuOTggMjk0LjM0IDY5MS41NCBaIiBmaWxsPSIjZmZmZmZmIiAvPgo8L3N2Zz4=', + }, + ], ]; diff --git a/src/components/form/Form.form.js b/src/components/form/Form.form.js index 5edcd85f8f..1e46950247 100644 --- a/src/components/form/Form.form.js +++ b/src/components/form/Form.form.js @@ -3,23 +3,26 @@ import FormEditDisplay from './editForm/Form.edit.display'; import FormEditForm from './editForm/Form.edit.form'; import FormEditData from './editForm/Form.edit.data'; -export default function(...extend) { - return nestedComponentForm([ - { - key: 'display', - components: FormEditDisplay - }, - { - label: 'Form', - key: 'form', - weight: 10, - components: FormEditForm - }, - { - label: 'Data', - key: 'data', - weight: 10, - components: FormEditData - }, - ], ...extend); +export default function (...extend) { + return nestedComponentForm( + [ + { + key: 'display', + components: FormEditDisplay, + }, + { + label: 'Form', + key: 'form', + weight: 10, + components: FormEditForm, + }, + { + label: 'Data', + key: 'data', + weight: 10, + components: FormEditData, + }, + ], + ...extend, + ); } diff --git a/src/components/form/Form.js b/src/components/form/Form.js index 344f5b98c6..c2a44ba150 100644 --- a/src/components/form/Form.js +++ b/src/components/form/Form.js @@ -3,852 +3,959 @@ import Component from '../_classes/component/Component'; import ComponentModal from '../_classes/componentModal/ComponentModal'; import EventEmitter from 'eventemitter3'; import { - isMongoId, - eachComponent, - getStringFromComponentPath, - getArrayFromComponentPath, - componentValueTypes + isMongoId, + eachComponent, + getStringFromComponentPath, + getArrayFromComponentPath, + componentValueTypes, } from '../../utils/utils'; import { Formio } from '../../Formio'; import Form from '../../Form'; export default class FormComponent extends Component { - static schema(...extend) { - return Component.schema({ - label: 'Form', - type: 'form', - key: 'form', - src: '', - reference: true, - form: '', - path: '', - tableView: true, - }, ...extend); - } - - static get builderInfo() { - return { - title: 'Nested Form', - icon: 'wpforms', - group: 'premium', - documentation: '/userguide/form-building/premium-components#nested-form', - weight: 110, - schema: FormComponent.schema() - }; - } - - static savedValueTypes() { - return [componentValueTypes.object]; - } - - init() { - super.init(); - this.formObj = { - display: this.component.display, - settings: this.component.settings, - components: this.component.components - }; - this.valueChanged = false; - this.subForm = null; - this.formSrc = ''; - if (this.component.src) { - this.formSrc = this.component.src; - } - - if ( - !this.component.src && - !this.options.formio && - (this.component.form || this.component.path) - ) { - if (this.component.project) { - this.formSrc = Formio.getBaseUrl(); - // Check to see if it is a MongoID. - if (isMongoId(this.component.project)) { - this.formSrc += '/project'; - } - this.formSrc += `/${this.component.project}`; - this.options.project = this.formSrc; - } - else { - this.formSrc = Formio.getProjectUrl(); - this.options.project = this.formSrc; - } - if (this.component.form) { - if (isMongoId(this.component.form)) { - this.formSrc += `/form/${this.component.form}`; - } - else { - this.formSrc += `/${this.component.form}`; - } - } - else if (this.component.path) { - this.formSrc += `/${this.component.path}`; - } - } - - // Build the source based on the root src path. - if (!this.formSrc && this.options.formio) { - const rootSrc = this.options.formio.formsUrl; - if (this.component.form && isMongoId(this.component.form)) { - this.formSrc = `${rootSrc}/${this.component.form}`; - } - else { - const formPath = this.component.path || this.component.form; - this.formSrc = `${rootSrc.replace(/\/form$/, '')}/${formPath}`; - } - } - - if (this.builderMode && Object.prototype.hasOwnProperty.call(this.component, 'formRevision')) { - this.component.revision = this.component.formRevision; - delete this.component.formRevision; - } - - // Add revision version if set. - if (this.component.revision || this.component.revision === 0 || - this.component.formRevision || this.component.formRevision === 0 - || this.component.revisionId - ) { - this.setFormRevision(this.component.revisionId || this.component.revision || this.component.formRevision); - } - - return this.createSubForm(); - } - - get dataReady() { - return this.subFormReady || Promise.resolve(); - } - - get defaultValue() { - // Not not provide a default value unless the subform is ready so that it will initialize correctly. - return this.subForm ? super.defaultValue : null; - } - - get defaultSchema() { - return FormComponent.schema(); - } - - get emptyValue() { - return { data: {} }; - } + static schema(...extend) { + return Component.schema( + { + label: 'Form', + type: 'form', + key: 'form', + src: '', + reference: true, + form: '', + path: '', + tableView: true, + }, + ...extend, + ); + } + + static get builderInfo() { + return { + title: 'Nested Form', + icon: 'wpforms', + group: 'premium', + documentation: + '/userguide/form-building/premium-components#nested-form', + weight: 110, + schema: FormComponent.schema(), + }; + } + + static savedValueTypes() { + return [componentValueTypes.object]; + } + + init() { + super.init(); + this.formObj = { + display: this.component.display, + settings: this.component.settings, + components: this.component.components, + }; + this.valueChanged = false; + this.subForm = null; + this.formSrc = ''; + if (this.component.src) { + this.formSrc = this.component.src; + } - get ready() { - return this.subFormReady || Promise.resolve(); - } + if ( + !this.component.src && + !this.options.formio && + (this.component.form || this.component.path) + ) { + if (this.component.project) { + this.formSrc = Formio.getBaseUrl(); + // Check to see if it is a MongoID. + if (isMongoId(this.component.project)) { + this.formSrc += '/project'; + } + this.formSrc += `/${this.component.project}`; + this.options.project = this.formSrc; + } else { + this.formSrc = Formio.getProjectUrl(); + this.options.project = this.formSrc; + } + if (this.component.form) { + if (isMongoId(this.component.form)) { + this.formSrc += `/form/${this.component.form}`; + } else { + this.formSrc += `/${this.component.form}`; + } + } else if (this.component.path) { + this.formSrc += `/${this.component.path}`; + } + } - get useOriginalRevision() { - return this.component?.useOriginalRevision && !!this.formObj?.revisions; - } + // Build the source based on the root src path. + if (!this.formSrc && this.options.formio) { + const rootSrc = this.options.formio.formsUrl; + if (this.component.form && isMongoId(this.component.form)) { + this.formSrc = `${rootSrc}/${this.component.form}`; + } else { + const formPath = this.component.path || this.component.form; + this.formSrc = `${rootSrc.replace(/\/form$/, '')}/${formPath}`; + } + } - setFormRevision(rev) { - // Remove current revisions from src if it is - this.formSrc = this.formSrc.replace(/\/v\/[0-9a-z]+/, ''); - const revNumber = Number.parseInt(rev); + if ( + this.builderMode && + Object.prototype.hasOwnProperty.call(this.component, 'formRevision') + ) { + this.component.revision = this.component.formRevision; + delete this.component.formRevision; + } - if (!isNaN(revNumber)) { - this.subFormRevision = rev; - this.formSrc += `/v/${rev}`; - } - else { - this.subFormRevision = undefined; - } - } + // Add revision version if set. + if ( + this.component.revision || + this.component.revision === 0 || + this.component.formRevision || + this.component.formRevision === 0 || + this.component.revisionId + ) { + this.setFormRevision( + this.component.revisionId || + this.component.revision || + this.component.formRevision, + ); + } - getComponent(path, fn) { - path = getArrayFromComponentPath(path); - if (path[0] === 'data') { - path.shift(); + return this.createSubForm(); } - const originalPathStr = `${this.path}.data.${getStringFromComponentPath(path)}`; - if (this.subForm) { - return this.subForm.getComponent(path, fn, originalPathStr); + + get dataReady() { + return this.subFormReady || Promise.resolve(); } - } - getSubOptions(options = {}) { - options.parentPath = `${this.path}.data.`; - options.events = this.createEmitter(); + get defaultValue() { + // Not not provide a default value unless the subform is ready so that it will initialize correctly. + return this.subForm ? super.defaultValue : null; + } - // Make sure to not show the submit button in wizards in the nested forms. - _.set(options, 'buttonSettings.showSubmit', false); + get defaultSchema() { + return FormComponent.schema(); + } - if (!this.options) { - return options; + get emptyValue() { + return { data: {} }; } - if (this.options.base) { - options.base = this.options.base; + + get ready() { + return this.subFormReady || Promise.resolve(); } - if (this.options.project) { - options.project = this.options.project; + + get useOriginalRevision() { + return this.component?.useOriginalRevision && !!this.formObj?.revisions; } - if (this.options.readOnly || this.component.disabled) { - options.readOnly = this.options.readOnly || this.component.disabled; + + setFormRevision(rev) { + // Remove current revisions from src if it is + this.formSrc = this.formSrc.replace(/\/v\/[0-9a-z]+/, ''); + const revNumber = Number.parseInt(rev); + + if (!isNaN(revNumber)) { + this.subFormRevision = rev; + this.formSrc += `/v/${rev}`; + } else { + this.subFormRevision = undefined; + } } - if (this.options.breadcrumbSettings) { - options.breadcrumbSettings = this.options.breadcrumbSettings; + + getComponent(path, fn) { + path = getArrayFromComponentPath(path); + if (path[0] === 'data') { + path.shift(); + } + const originalPathStr = `${this.path}.data.${getStringFromComponentPath( + path, + )}`; + if (this.subForm) { + return this.subForm.getComponent(path, fn, originalPathStr); + } } - if (this.options.buttonSettings) { - options.buttonSettings = _.clone(this.options.buttonSettings); + + getSubOptions(options = {}) { + options.parentPath = `${this.path}.data.`; + options.events = this.createEmitter(); + + // Make sure to not show the submit button in wizards in the nested forms. + _.set(options, 'buttonSettings.showSubmit', false); + + if (!this.options) { + return options; + } + if (this.options.base) { + options.base = this.options.base; + } + if (this.options.project) { + options.project = this.options.project; + } + if (this.options.readOnly || this.component.disabled) { + options.readOnly = this.options.readOnly || this.component.disabled; + } + if (this.options.breadcrumbSettings) { + options.breadcrumbSettings = this.options.breadcrumbSettings; + } + if (this.options.buttonSettings) { + options.buttonSettings = _.clone(this.options.buttonSettings); + } + if (this.options.viewAsHtml) { + options.viewAsHtml = this.options.viewAsHtml; + } + if (this.options.language) { + options.language = this.options.language; + } + if (this.options.template) { + options.template = this.options.template; + } + if (this.options.templates) { + options.templates = this.options.templates; + } + if (this.options.renderMode) { + options.renderMode = this.options.renderMode; + } + if (this.options.attachMode) { + options.attachMode = this.options.attachMode; + } + if (this.options.iconset) { + options.iconset = this.options.iconset; + } + if (this.options.fileService) { + options.fileService = this.options.fileService; + } + if (this.options.onChange) { + options.onChange = this.options.onChange; + } + if (this.options.preview) { + options.preview = this.options.preview; + } + return options; } - if (this.options.viewAsHtml) { - options.viewAsHtml = this.options.viewAsHtml; + + render() { + if (this.builderMode) { + return super.render(this.component.label || 'Nested form'); + } + const subform = this.subForm + ? this.subForm.render() + : this.renderTemplate('loading'); + return super.render(subform); } - if (this.options.language) { - options.language = this.options.language; + + asString(value) { + return this.getValueAsString(value); } - if (this.options.template) { - options.template = this.options.template; + + /** + * Prints out the value of form components as a datagrid value. + */ + + getValueAsString(value, options) { + if (!value) { + return 'No data provided'; + } + if (!value.data && value._id) { + return value._id; + } + if (!value.data || !Object.keys(value.data).length) { + return 'No data provided'; + } + if (options?.email) { + let result = ` + + + `; + + this.everyComponent( + (component) => { + if ( + component.isInputComponent && + component.visible && + !component.skipInEmail + ) { + result += ` + + + + + `; + } + }, + { + ...options, + fromRoot: true, + }, + ); + + result += ` + +
    ${component.label}${component.getView( + component.dataValue, + options, + )}
    + `; + + return result; + } + if (_.isEmpty(value)) { + return ''; + } + + return '[Complex Data]'; } - if (this.options.templates) { - options.templates = this.options.templates; + + attach(element) { + // Don't attach in builder. + if (this.builderMode) { + return super.attach(element); + } + return super.attach(element).then(() => { + if ( + this.isSubFormLazyLoad() && + !this.hasLoadedForm && + !this.subFormLoading + ) { + this.createSubForm(true); + } + + return this.subFormReady.then(() => { + this.empty(element); + if (this.options.builder) { + this.setContent( + element, + this.ce( + 'div', + { + class: 'text-muted text-center p-2', + }, + this.text(this.formObj.title), + ), + ); + return; + } + + this.setContent(element, this.render()); + if (this.subForm) { + if (this.isNestedWizard) { + element = this.root.element; + } + this.subForm.attach(element); + this.valueChanged = this.hasSetValue; + + if ( + !this.valueChanged && + this.dataValue.state !== 'submitted' + ) { + this.setDefaultValue(); + } else { + this.restoreValue(); + } + } + if (!this.builderMode && this.component.modalEdit) { + const modalShouldBeOpened = this.componentModal + ? this.componentModal.isOpened + : false; + const currentValue = modalShouldBeOpened + ? this.componentModal.currentValue + : this.dataValue; + this.componentModal = new ComponentModal( + this, + element, + modalShouldBeOpened, + currentValue, + ); + this.setOpenModalElement(); + } + + this.calculateValue(); + }); + }); } - if (this.options.renderMode) { - options.renderMode = this.options.renderMode; + + detach() { + if (this.subForm) { + this.subForm.detach(); + } + super.detach(); } - if (this.options.attachMode) { - options.attachMode = this.options.attachMode; + + get currentForm() { + return this._currentForm; } - if (this.options.iconset) { - options.iconset = this.options.iconset; + + get hasLoadedForm() { + return ( + this.formObj && + this.formObj.components && + Array.isArray(this.formObj.components) && + this.formObj.components.length + ); } - if (this.options.fileService) { - options.fileService = this.options.fileService; + + set currentForm(instance) { + this._currentForm = instance; + if (!this.subForm) { + return; + } + this.subForm.getComponents().forEach((component) => { + component.currentForm = this; + }); } - if (this.options.onChange) { - options.onChange = this.options.onChange; + + get isRevisionChanged() { + return ( + _.isNumber(this.subFormRevision) && + _.isNumber(this.formObj._vid) && + this.formObj._vid !== this.subFormRevision + ); } - if (this.options.preview) { - options.preview = this.options.preview; + + destroy(all = false) { + if (this.subForm) { + this.subForm.destroy(all); + this.subForm = null; + this.subFormReady = null; + } + super.destroy(all); } - return options; - } - render() { - if (this.builderMode) { - return super.render(this.component.label || 'Nested form'); + redraw() { + if (this.subForm) { + this.subForm.form = this.formObj; + this.setSubFormDisabled(this.subForm); + } + return super.redraw(); } - const subform = this.subForm ? this.subForm.render() : this.renderTemplate('loading'); - return super.render(subform); - } - asString(value) { - return this.getValueAsString(value); - } + /** + * Pass everyComponent to subform. + * @param args + * @returns {*|void} + */ + everyComponent(...args) { + if (this.subForm) { + this.subForm.everyComponent(...args); + } + } - /** - * Prints out the value of form components as a datagrid value. - */ + setSubFormDisabled(subForm) { + subForm.disabled = this.disabled; // When the Nested Form is disabled make the subForm disabled + } - getValueAsString(value, options) { - if (!value) { - return 'No data provided'; + updateSubWizards(subForm) { + if ( + this.isNestedWizard && + this.root?.subWizards && + subForm?._form?.display === 'wizard' + ) { + const existedForm = this.root.subWizards.findIndex( + (form) => form.component.form === this.component.form, + ); + if (existedForm !== -1) { + this.root.subWizards[existedForm] = this; + } else { + this.root.subWizards.push(this); + } + this.emit('subWizardsUpdated', subForm); + } } - if (!value.data && value._id) { - return value._id; + + /** + * Create a subform instance. + * + * @return {*} + */ + createSubForm(fromAttach) { + this.subFormReady = this.loadSubForm(fromAttach) + .then((form) => { + if (!form) { + return; + } + + // Iterate through every component and hide the submit button. + eachComponent(form.components, (component) => { + this.hideSubmitButton(component); + }); + + // If the subform is already created then destroy the old one. + if (this.subForm) { + this.subForm.destroy(); + } + + // Render the form. + return new Form(form, this.getSubOptions()).ready.then( + (instance) => { + this.subForm = instance; + this.subForm.currentForm = this; + this.subForm.parent = this; + this.subForm.parentVisible = this.visible; + this.subForm.on('change', () => { + if (this.subForm) { + this.dataValue = this.subForm.getValue(); + this.triggerChange({ + noEmit: true, + }); + } + }); + this.subForm.url = this.formSrc; + this.subForm.nosubmit = true; + this.subForm.root = this.root; + this.subForm.localRoot = this.isNestedWizard + ? this.localRoot + : this.subForm; + this.restoreValue(); + this.valueChanged = this.hasSetValue; + this.onChange(); + return this.subForm; + }, + ); + }) + .then((subForm) => { + this.updateSubWizards(subForm); + return subForm; + }); + return this.subFormReady; } - if (!value.data || !Object.keys(value.data).length) { - return 'No data provided'; + + hideSubmitButton(component) { + const isSubmitButton = + component.type === 'button' && + (component.action === 'submit' || !component.action); + + if (isSubmitButton) { + component.hidden = true; + } } - if (options?.email) { - let result = (` - - - `); - this.everyComponent((component) => { - if (component.isInputComponent && component.visible && !component.skipInEmail) { - result += (` - - - - - `); + /** + * Load the subform. + */ + loadSubForm(fromAttach) { + if ( + this.builderMode || + this.isHidden() || + (this.isSubFormLazyLoad() && !fromAttach) + ) { + return Promise.resolve(); } - }, { - ...options, - fromRoot: true, - }); - result += (` - -
    ${component.label}${component.getView(component.dataValue, options)}
    - `); + if ( + this.hasLoadedForm && + !this.isRevisionChanged && + !( + this.options.pdf && + this.component?.useOriginalRevision && + _.isNull(this.subForm) && + !this.subFormLoading + ) + ) { + // Pass config down to sub forms. + if ( + this.root && + this.root.form && + this.root.form.config && + !this.formObj.config + ) { + this.formObj.config = this.root.form.config; + } + return Promise.resolve(this.formObj); + } else if (this.formSrc) { + this.subFormLoading = true; + return new Formio(this.formSrc) + .loadForm({ params: { live: 1 } }) + .then((formObj) => { + this.formObj = formObj; + if ( + this.options.pdf && + this.component.useOriginalRevision + ) { + this.formObj.display = 'form'; + } + this.subFormLoading = false; + return formObj; + }) + .catch((err) => { + console.log(err); + return null; + }); + } + return Promise.resolve(); + } - return result; + get subFormData() { + return this.dataValue?.data || {}; } - if (_.isEmpty(value)) { - return ''; + + checkComponentValidity(data, dirty, row, options) { + options = options || {}; + const silentCheck = options.silentCheck || false; + + if (this.subForm) { + return this.subForm.checkValidity( + this.subFormData, + dirty, + null, + silentCheck, + ); + } + + return super.checkComponentValidity(data, dirty, row, options); } - return '[Complex Data]'; - } + checkComponentConditions(data, flags, row) { + const visible = super.checkComponentConditions(data, flags, row); - attach(element) { - // Don't attach in builder. - if (this.builderMode) { - return super.attach(element); + // Return if already hidden + if (!visible) { + return visible; + } + + if (this.subForm) { + return this.subForm.checkConditions(this.subFormData); + } + // There are few cases when subForm is not loaded when a change is triggered, + // so we need to perform checkConditions after it is ready, or some conditional fields might be hidden in View mode + else if (this.subFormReady) { + this.subFormReady.then(() => { + if (this.subForm) { + return this.subForm.checkConditions(this.subFormData); + } + }); + } + + return visible; } - return super.attach(element) - .then(() => { - if (this.isSubFormLazyLoad() && !this.hasLoadedForm && !this.subFormLoading) { - this.createSubForm(true); + + calculateValue(data, flags, row) { + if (this.subForm) { + return this.subForm.calculateValue(this.subFormData, flags); } - return this.subFormReady.then(() => { - this.empty(element); - if (this.options.builder) { - this.setContent(element, this.ce('div', { - class: 'text-muted text-center p-2' - }, this.text(this.formObj.title))); - return; - } + return super.calculateValue(data, flags, row); + } - this.setContent(element, this.render()); - if (this.subForm) { - if (this.isNestedWizard) { - element = this.root.element; - } - this.subForm.attach(element); - this.valueChanged = this.hasSetValue; + setPristine(pristine) { + super.setPristine(pristine); + if (this.subForm) { + this.subForm.setPristine(pristine); + } + } - if (!this.valueChanged && this.dataValue.state !== 'submitted') { - this.setDefaultValue(); - } - else { - this.restoreValue(); - } - } - if (!this.builderMode && this.component.modalEdit) { - const modalShouldBeOpened = this.componentModal ? this.componentModal.isOpened : false; - const currentValue = modalShouldBeOpened ? this.componentModal.currentValue : this.dataValue; - this.componentModal = new ComponentModal(this, element, modalShouldBeOpened, currentValue); - this.setOpenModalElement(); - } - - this.calculateValue(); - }); - }); - } - - detach() { - if (this.subForm) { - this.subForm.detach(); - } - super.detach(); - } - - get currentForm() { - return this._currentForm; - } - - get hasLoadedForm() { - return this.formObj - && this.formObj.components - && Array.isArray(this.formObj.components) - && this.formObj.components.length; - } - - set currentForm(instance) { - this._currentForm = instance; - if (!this.subForm) { - return; - } - this.subForm.getComponents().forEach(component => { - component.currentForm = this; - }); - } - - get isRevisionChanged() { - return _.isNumber(this.subFormRevision) - && _.isNumber(this.formObj._vid) - && this.formObj._vid !== this.subFormRevision; - } - - destroy(all = false) { - if (this.subForm) { - this.subForm.destroy(all); - this.subForm = null; - this.subFormReady = null; - } - super.destroy(all); - } - - redraw() { - if (this.subForm) { - this.subForm.form = this.formObj; - this.setSubFormDisabled(this.subForm); - } - return super.redraw(); - } - - /** - * Pass everyComponent to subform. - * @param args - * @returns {*|void} - */ - everyComponent(...args) { - if (this.subForm) { - this.subForm.everyComponent(...args); - } - } - - setSubFormDisabled(subForm) { - subForm.disabled = this.disabled; // When the Nested Form is disabled make the subForm disabled - } - - updateSubWizards(subForm) { - if (this.isNestedWizard && this.root?.subWizards && subForm?._form?.display === 'wizard') { - const existedForm = this.root.subWizards.findIndex(form => form.component.form === this.component.form); - if (existedForm !== -1) { - this.root.subWizards[existedForm] = this; - } - else { - this.root.subWizards.push(this); - } - this.emit('subWizardsUpdated', subForm); - } - } - - /** - * Create a subform instance. - * - * @return {*} - */ - createSubForm(fromAttach) { - this.subFormReady = this.loadSubForm(fromAttach).then((form) => { - if (!form) { - return; - } - - // Iterate through every component and hide the submit button. - eachComponent(form.components, (component) => { - this.hideSubmitButton(component); - }); - - // If the subform is already created then destroy the old one. - if (this.subForm) { - this.subForm.destroy(); - } - - // Render the form. - return (new Form(form, this.getSubOptions())).ready.then((instance) => { - this.subForm = instance; - this.subForm.currentForm = this; - this.subForm.parent = this; - this.subForm.parentVisible = this.visible; - this.subForm.on('change', () => { - if (this.subForm) { - this.dataValue = this.subForm.getValue(); - this.triggerChange({ - noEmit: true + /** + * Determine if the subform should be submitted. + * @return {*|boolean} + */ + get shouldSubmit() { + return ( + this.subFormReady && + (!Object.prototype.hasOwnProperty.call( + this.component, + 'reference', + ) || + this.component.reference) && + !this.isHidden() + ); + } + + /** + * Returns the data for the subform. + * + * @return {*} + */ + getSubFormData() { + if (_.get(this.subForm, 'form.display') === 'pdf') { + return this.subForm.getSubmission(); + } else { + return Promise.resolve(this.dataValue); + } + } + + /** + * Submit the subform if configured to do so. + * + * @return {*} + */ + submitSubForm(rejectOnError) { + // If we wish to submit the form on next page, then do that here. + if (this.shouldSubmit) { + return this.subFormReady.then(() => { + if (!this.subForm) { + return this.dataValue; + } + this.subForm.nosubmit = false; + return this.subForm + .submitForm() + .then((result) => { + this.subForm.loading = false; + this.subForm.showAllErrors = false; + this.dataValue = result.submission; + return this.dataValue; + }) + .catch((err) => { + this.subForm.showAllErrors = true; + if (rejectOnError) { + this.subForm.onSubmissionError(err); + return Promise.reject(err); + } else { + return {}; + } + }); }); - } - }); - this.subForm.url = this.formSrc; - this.subForm.nosubmit = true; - this.subForm.root = this.root; - this.subForm.localRoot = this.isNestedWizard ? this.localRoot : this.subForm; - this.restoreValue(); - this.valueChanged = this.hasSetValue; - this.onChange(); - return this.subForm; - }); - }).then((subForm) => { - this.updateSubWizards(subForm); - return subForm; - }); - return this.subFormReady; - } - - hideSubmitButton(component) { - const isSubmitButton = (component.type === 'button') && - ((component.action === 'submit') || !component.action); - - if (isSubmitButton) { - component.hidden = true; - } - } - - /** - * Load the subform. - */ - loadSubForm(fromAttach) { - if (this.builderMode || this.isHidden() || (this.isSubFormLazyLoad() && !fromAttach)) { - return Promise.resolve(); - } - - if (this.hasLoadedForm && !this.isRevisionChanged && - !(this.options.pdf && this.component?.useOriginalRevision && _.isNull(this.subForm) && !this.subFormLoading) - ) { - // Pass config down to sub forms. - if (this.root && this.root.form && this.root.form.config && !this.formObj.config) { - this.formObj.config = this.root.form.config; - } - return Promise.resolve(this.formObj); - } - else if (this.formSrc) { - this.subFormLoading = true; - return (new Formio(this.formSrc)).loadForm({ params: { live: 1 } }) - .then((formObj) => { - this.formObj = formObj; - if (this.options.pdf && this.component.useOriginalRevision) { - this.formObj.display = 'form'; - } - this.subFormLoading = false; - return formObj; - }) - .catch((err) => { - console.log(err); - return null; - }); + } + return this.getSubFormData(); + } + + /** + * Submit the form before the next page is triggered. + */ + beforePage(next) { + // Should not submit child forms if we are going to the previous page + if (!next) { + return super.beforePage(next); + } + return this.submitSubForm(true).then(() => super.beforePage(next)); } - return Promise.resolve(); - } - get subFormData() { - return this.dataValue?.data || {}; - } + /** + * Submit the form before the whole form is triggered. + */ + beforeSubmit() { + const submission = this.dataValue; - checkComponentValidity(data, dirty, row, options) { - options = options || {}; - const silentCheck = options.silentCheck || false; + const isAlreadySubmitted = + submission && submission._id && submission.form; - if (this.subForm) { - return this.subForm.checkValidity(this.subFormData, dirty, null, silentCheck); + // This submission has already been submitted, so just return the reference data. + if (isAlreadySubmitted && !this.subForm?.wizard) { + this.dataValue = submission; + return Promise.resolve(this.dataValue); + } + return this.submitSubForm(false) + .then(() => { + return this.dataValue; + }) + .then(() => super.beforeSubmit()); } - return super.checkComponentValidity(data, dirty, row, options); - } + isSubFormLazyLoad() { + return ( + this.root?._form?.display === 'wizard' && this.component.lazyLoad + ); + } - checkComponentConditions(data, flags, row) { - const visible = super.checkComponentConditions(data, flags, row); + isHidden() { + if (!this.visible) { + return true; + } - // Return if already hidden - if (!visible) { - return visible; + return !super.checkConditions(this.rootValue); } - if (this.subForm) { - return this.subForm.checkConditions(this.subFormData); - } - // There are few cases when subForm is not loaded when a change is triggered, - // so we need to perform checkConditions after it is ready, or some conditional fields might be hidden in View mode - else if (this.subFormReady) { - this.subFormReady.then(() => { + setValue(submission, flags = {}) { + const changed = super.setValue(submission, flags); + this.valueChanged = true; if (this.subForm) { - return this.subForm.checkConditions(this.subFormData); + const revisionPath = submission._frid ? '_frid' : '_vid'; + const shouldLoadOriginalRevision = + this.useOriginalRevision && + (_.isNumber(submission[revisionPath]) || + _.isNumber(submission._fvid)) && + _.isNumber(this.subForm.form?.[revisionPath]) && + submission._fvid !== this.subForm.form[revisionPath]; + + if (shouldLoadOriginalRevision) { + this.setFormRevision(submission._frid || submission._fvid); + this.createSubForm().then(() => { + this.attach(this.element); + }); + } else { + this.setSubFormValue(submission, flags); + } + } + return changed; + } + + setSubFormValue(submission, flags) { + const shouldLoadSubmissionById = + submission && + submission._id && + this.subForm.formio && + _.isEmpty(submission.data); + + if (shouldLoadSubmissionById) { + const formId = + submission.form || this.formObj.form || this.component.form; + const submissionUrl = `${this.subForm.formio.formsUrl}/${formId}/submission/${submission._id}`; + this.subForm.setUrl(submissionUrl, this.options); + this.subForm.loadSubmission().catch((err) => { + console.error( + `Unable to load subform submission ${submission._id}:`, + err, + ); + }); + } else { + this.subForm.setValue(submission, flags); } - }); } - return visible; - } - - calculateValue(data, flags, row) { - if (this.subForm) { - return this.subForm.calculateValue(this.subFormData, flags); + isEmpty(value = this.dataValue) { + return ( + value === null || + _.isEqual(value, this.emptyValue) || + (this.areAllComponentsEmpty(value?.data) && !value?._id) + ); } - return super.calculateValue(data, flags, row); - } + areAllComponentsEmpty(data) { + let res = true; + if (this.subForm) { + this.subForm.everyComponent((comp) => { + const componentValue = _.get(data, comp.key); + res &= comp.isEmpty(componentValue); + }); + } else { + res = false; + } + return res; + } - setPristine(pristine) { - super.setPristine(pristine); - if (this.subForm) { - this.subForm.setPristine(pristine); + getValue() { + if (this.subForm) { + return this.subForm.getValue(); + } + return this.dataValue; } - } - /** - * Determine if the subform should be submitted. - * @return {*|boolean} - */ - get shouldSubmit() { - return this.subFormReady && (!Object.prototype.hasOwnProperty.call(this.component, 'reference') || this.component.reference) && !this.isHidden(); - } + get errors() { + let errors = super.errors; + if (this.subForm) { + errors = errors.concat(this.subForm.errors); + } + return errors; + } - /** - * Returns the data for the subform. - * - * @return {*} - */ - getSubFormData() { - if (_.get(this.subForm, 'form.display') === 'pdf') { - return this.subForm.getSubmission(); + updateSubFormVisibility() { + if (this.subForm) { + this.subForm.parentVisible = this.visible; + } } - else { - return Promise.resolve(this.dataValue); + + /** + * Determines if this form is a Nested Wizard + * which means it should be a Wizard itself and should be a direct child of a Wizard's page + * @returns {boolean} + */ + get isNestedWizard() { + return ( + this.subForm?._form?.display === 'wizard' && + this.parent?.parent?._form?.display === 'wizard' + ); + } + + get visible() { + return super.visible; + } + + set visible(value) { + const isNestedWizard = this.isNestedWizard; + + if (this._visible !== value) { + this._visible = value; + // Form doesn't load if hidden. If it becomes visible, create the form. + if (!this.subForm && value) { + this.createSubForm(); + this.subFormReady.then(() => { + this.updateSubFormVisibility(); + this.clearOnHide(); + }); + this.redraw(); + return; + } + this.updateSubFormVisibility(); + this.clearOnHide(); + isNestedWizard ? this.rebuild() : this.redraw(); + } + if (!value && isNestedWizard) { + this.root.redraw(); + } } - } - /** - * Submit the subform if configured to do so. - * - * @return {*} - */ - submitSubForm(rejectOnError) { - // If we wish to submit the form on next page, then do that here. - if (this.shouldSubmit) { - return this.subFormReady.then(() => { - if (!this.subForm) { - return this.dataValue; - } - this.subForm.nosubmit = false; - return this.subForm.submitForm().then(result => { - this.subForm.loading = false; - this.subForm.showAllErrors = false; - this.dataValue = result.submission; - return this.dataValue; - }).catch(err => { - this.subForm.showAllErrors = true; - if (rejectOnError) { - this.subForm.onSubmissionError(err); - return Promise.reject(err); - } - else { - return {}; - } - }); - }); + get parentVisible() { + return super.parentVisible; } - return this.getSubFormData(); - } - /** - * Submit the form before the next page is triggered. - */ - beforePage(next) { - // Should not submit child forms if we are going to the previous page - if (!next) { - return super.beforePage(next); + set parentVisible(value) { + if (this._parentVisible !== value) { + this._parentVisible = value; + this.clearOnHide(); + // Form doesn't load if hidden. If it becomes visible, create the form. + if (!this.subForm && value) { + this.createSubForm(); + this.subFormReady.then(() => { + this.updateSubFormVisibility(); + }); + this.redraw(); + return; + } + this.updateSubFormVisibility(); + this.redraw(); + } } - return this.submitSubForm(true).then(() => super.beforePage(next)); - } - /** - * Submit the form before the whole form is triggered. - */ - beforeSubmit() { - const submission = this.dataValue; + isInternalEvent(event) { + switch (event) { + case 'focus': + case 'blur': + case 'componentChange': + case 'componentError': + case 'error': + case 'formLoad': + case 'languageChanged': + case 'render': + case 'checkValidity': + case 'initialized': + case 'submit': + case 'submitButton': + case 'nosubmit': + case 'updateComponent': + case 'submitDone': + case 'submissionDeleted': + case 'requestDone': + case 'nextPage': + case 'prevPage': + case 'wizardNavigationClicked': + case 'updateWizardNav': + case 'restoreDraft': + case 'saveDraft': + case 'saveComponent': + case 'pdfUploaded': + return true; + default: + return false; + } + } - const isAlreadySubmitted = submission && submission._id && submission.form; + createEmitter() { + const emitter = new EventEmitter(); + const nativeEmit = emitter.emit; + const that = this; + emitter.emit = function (event, ...args) { + const eventType = event.replace(`${that.options.namespace}.`, ''); + nativeEmit.call(this, event, ...args); + if (!that.isInternalEvent(eventType)) { + that.emit(eventType, ...args); + } + }; - // This submission has already been submitted, so just return the reference data. - if (isAlreadySubmitted && !this.subForm?.wizard) { - this.dataValue = submission; - return Promise.resolve(this.dataValue); + return emitter; } - return this.submitSubForm(false) - .then(() => { - return this.dataValue; - }) - .then(() => super.beforeSubmit()); - } - - isSubFormLazyLoad() { - return this.root?._form?.display === 'wizard' && this.component.lazyLoad; - } - - isHidden() { - if (!this.visible) { - return true; - } - - return !super.checkConditions(this.rootValue); - } - - setValue(submission, flags = {}) { - const changed = super.setValue(submission, flags); - this.valueChanged = true; - if (this.subForm) { - const revisionPath = submission._frid ? '_frid' : '_vid'; - const shouldLoadOriginalRevision = this.useOriginalRevision - && (_.isNumber(submission[revisionPath]) || _.isNumber(submission._fvid)) - && _.isNumber(this.subForm.form?.[revisionPath]) - && submission._fvid !== this.subForm.form[revisionPath]; - - if (shouldLoadOriginalRevision) { - this.setFormRevision( submission._frid || submission._fvid); - this.createSubForm().then(() => { - this.attach(this.element); - }); - } - else { - this.setSubFormValue(submission, flags); - } - } - return changed; - } - - setSubFormValue(submission, flags) { - const shouldLoadSubmissionById = submission - && submission._id - && this.subForm.formio - && _.isEmpty(submission.data); - - if (shouldLoadSubmissionById) { - const formId = submission.form || this.formObj.form || this.component.form; - const submissionUrl = `${this.subForm.formio.formsUrl}/${formId}/submission/${submission._id}`; - this.subForm.setUrl(submissionUrl, this.options); - this.subForm.loadSubmission().catch((err) => { - console.error(`Unable to load subform submission ${submission._id}:`, err); - }); - } - else { - this.subForm.setValue(submission, flags); - } - } - - isEmpty(value = this.dataValue) { - return value === null || _.isEqual(value, this.emptyValue) || (this.areAllComponentsEmpty(value?.data) && !value?._id); - } - - areAllComponentsEmpty(data) { - let res = true; - if (this.subForm) { - this.subForm.everyComponent((comp) => { - const componentValue = _.get(data, comp.key); - res &= comp.isEmpty(componentValue); - }); - } - else { - res = false; - } - return res; - } - - getValue() { - if (this.subForm) { - return this.subForm.getValue(); - } - return this.dataValue; - } - - get errors() { - let errors = super.errors; - if (this.subForm) { - errors = errors.concat(this.subForm.errors); - } - return errors; - } - - updateSubFormVisibility() { - if (this.subForm) { - this.subForm.parentVisible = this.visible; - } - } - - /** - * Determines if this form is a Nested Wizard - * which means it should be a Wizard itself and should be a direct child of a Wizard's page - * @returns {boolean} - */ - get isNestedWizard() { - return this.subForm?._form?.display === 'wizard' && this.parent?.parent?._form?.display === 'wizard'; - } - - get visible() { - return super.visible; - } - - set visible(value) { - const isNestedWizard = this.isNestedWizard; - - if (this._visible !== value) { - this._visible = value; - // Form doesn't load if hidden. If it becomes visible, create the form. - if (!this.subForm && value) { - this.createSubForm(); - this.subFormReady.then(() => { - this.updateSubFormVisibility(); - this.clearOnHide(); - }); - this.redraw(); - return; - } - this.updateSubFormVisibility(); - this.clearOnHide(); - isNestedWizard ? this.rebuild() : this.redraw(); - } - if (!value && isNestedWizard) { - this.root.redraw(); - } - } - - get parentVisible() { - return super.parentVisible; - } - - set parentVisible(value) { - if (this._parentVisible !== value) { - this._parentVisible = value; - this.clearOnHide(); - // Form doesn't load if hidden. If it becomes visible, create the form. - if (!this.subForm && value) { - this.createSubForm(); - this.subFormReady.then(() => { - this.updateSubFormVisibility(); + + deleteValue() { + super.setValue(null, { + noUpdateEvent: true, + noDefault: true, }); - this.redraw(); - return; - } - this.updateSubFormVisibility(); - this.redraw(); - } - } - - isInternalEvent(event) { - switch (event) { - case 'focus': - case 'blur': - case 'componentChange': - case 'componentError': - case 'error': - case 'formLoad': - case 'languageChanged': - case 'render': - case 'checkValidity': - case 'initialized': - case 'submit': - case 'submitButton': - case 'nosubmit': - case 'updateComponent': - case 'submitDone': - case 'submissionDeleted': - case 'requestDone': - case 'nextPage': - case 'prevPage': - case 'wizardNavigationClicked': - case 'updateWizardNav': - case 'restoreDraft': - case 'saveDraft': - case 'saveComponent': - case 'pdfUploaded': - return true; - default: - return false; - } - } - - createEmitter() { - const emitter = new EventEmitter(); - const nativeEmit = emitter.emit; - const that = this; - emitter.emit = function(event, ...args) { - const eventType = event.replace(`${that.options.namespace}.`, ''); - nativeEmit.call(this, event, ...args); - if (!that.isInternalEvent(eventType)) { - that.emit(eventType, ...args); - } - }; - - return emitter; - } - - deleteValue() { - super.setValue(null, { - noUpdateEvent: true, - noDefault: true - }); - this.unset(); - } + this.unset(); + } } diff --git a/src/components/form/Form.unit.js b/src/components/form/Form.unit.js index 1d2c5ea3bb..c424ff4f8c 100644 --- a/src/components/form/Form.unit.js +++ b/src/components/form/Form.unit.js @@ -3,274 +3,321 @@ import FormComponent from './Form'; import { expect } from 'chai'; import assert from 'power-assert'; -import { - comp1, - comp3, - comp4, - comp5, - comp6 -} from './fixtures'; +import { comp1, comp3, comp4, comp5, comp6 } from './fixtures'; import Webform from '../../Webform'; import formModalEdit from './fixtures/formModalEdit'; import { formComponentWithConditionalRenderingForm } from '../../../test/formtest'; -describe('Form Component', function() { - it('Should build a form component', function() { - return Harness.testCreate(FormComponent, comp1); - }); +describe('Form Component', function () { + it('Should build a form component', function () { + return Harness.testCreate(FormComponent, comp1); + }); - describe('Value inside Nested Form', function() { - it('Should be able to set value to Nested Form Component and check result in the email template', function(done) { - const formElement = document.createElement('div'); - const form = new Webform(formElement); - form.setForm(comp5) - .then(() => { - const textField = form.getComponent(['form', 'textField']); - textField.setValue('123', { modified: true }); - assert.equal(textField.dataValue, '123', 'Should set value'); - const toString = form.getValueAsString(textField.data, { email: true }); - assert.ok(toString.includes('table'), 'Email template should render html table'); - assert.ok(toString.includes(textField.label), 'Email template should have Text Field label'); - assert.ok(toString.includes(textField.dataValue), 'Email template should have Text Field value'); - done(); - }) - .catch(done); + describe('Value inside Nested Form', function () { + it('Should be able to set value to Nested Form Component and check result in the email template', function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement); + form.setForm(comp5) + .then(() => { + const textField = form.getComponent(['form', 'textField']); + textField.setValue('123', { modified: true }); + assert.equal( + textField.dataValue, + '123', + 'Should set value', + ); + const toString = form.getValueAsString(textField.data, { + email: true, + }); + assert.ok( + toString.includes('table'), + 'Email template should render html table', + ); + assert.ok( + toString.includes(textField.label), + 'Email template should have Text Field label', + ); + assert.ok( + toString.includes(textField.dataValue), + 'Email template should have Text Field value', + ); + done(); + }) + .catch(done); + }); }); - }); - it('Test refreshOn inside NestedForm', function(done) { - const formElement = document.createElement('div'); - const form = new Webform(formElement); - form.setForm(comp4) - .then(() => { - const make = form.getComponent(['form', 'make']); - const model = form.getComponent(['form', 'model']); - make.setValue('ford'); - setTimeout(() => { - assert.equal(make.dataValue, 'ford', 'Should set value'); - model.setValue('Focus', { modified: true }); - setTimeout(() => { - assert.equal(model.dataValue, 'Focus', 'Should set value'); - make.setValue('honda', { modified: true }); - setTimeout(() => { - assert.equal(make.dataValue, 'honda', 'Should set value'); - assert.equal(model.dataValue, '', 'Should refresh and clear value'); - done(); - }, 300); - }, 300); - }, 300); - }) - .catch(done); - }); + it('Test refreshOn inside NestedForm', function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement); + form.setForm(comp4) + .then(() => { + const make = form.getComponent(['form', 'make']); + const model = form.getComponent(['form', 'model']); + make.setValue('ford'); + setTimeout(() => { + assert.equal(make.dataValue, 'ford', 'Should set value'); + model.setValue('Focus', { modified: true }); + setTimeout(() => { + assert.equal( + model.dataValue, + 'Focus', + 'Should set value', + ); + make.setValue('honda', { modified: true }); + setTimeout(() => { + assert.equal( + make.dataValue, + 'honda', + 'Should set value', + ); + assert.equal( + model.dataValue, + '', + 'Should refresh and clear value', + ); + done(); + }, 300); + }, 300); + }, 300); + }) + .catch(done); + }); - describe('renderSubForm', function() { - let formcmp = null; + describe('renderSubForm', function () { + let formcmp = null; - it('should set sub form parentVisible', function(done) { - Harness.testCreate(FormComponent, comp1) - .then(cmp => { - formcmp = cmp; - formcmp.visible = false; - return formcmp.subFormReady; - }, done) - .then(subForm => { - expect(formcmp).to.not.be.null; - expect(formcmp.visible).to.be.false; - expect(subForm.parentVisible).to.be.false; - done(); - }, done) - .catch(done); + it('should set sub form parentVisible', function (done) { + Harness.testCreate(FormComponent, comp1) + .then((cmp) => { + formcmp = cmp; + formcmp.visible = false; + return formcmp.subFormReady; + }, done) + .then((subForm) => { + expect(formcmp).to.not.be.null; + expect(formcmp.visible).to.be.false; + expect(subForm.parentVisible).to.be.false; + done(); + }, done) + .catch(done); + }); }); - }); - describe('set visible', function() { - it('should set visible flag on instance', function(done) { - Harness.testCreate(FormComponent, comp1) - .then(formcmp => { - expect(formcmp._visible).to.be.true; - formcmp.visible = false; - expect(formcmp._visible).to.be.false; - done(); - }, done) - .catch(done); - }); + describe('set visible', function () { + it('should set visible flag on instance', function (done) { + Harness.testCreate(FormComponent, comp1) + .then((formcmp) => { + expect(formcmp._visible).to.be.true; + formcmp.visible = false; + expect(formcmp._visible).to.be.false; + done(); + }, done) + .catch(done); + }); - it('should update sub form visibility', function(done) { - let formcmp; - Harness.testCreate(FormComponent, comp1) - .then(cmp => { - formcmp = cmp; - return formcmp.subFormReady; - }, done) - .then(subform => { - expect(formcmp.visible).to.be.true; - expect(subform.parentVisible).to.be.true; - formcmp.visible = false; - expect(formcmp.visible).to.be.false; - expect(subform.parentVisible).to.be.false; - formcmp.visible = true; - expect(formcmp.visible).to.be.true; - expect(subform.parentVisible).to.be.true; - done(); - }, done) - .catch(done); + it('should update sub form visibility', function (done) { + let formcmp; + Harness.testCreate(FormComponent, comp1) + .then((cmp) => { + formcmp = cmp; + return formcmp.subFormReady; + }, done) + .then((subform) => { + expect(formcmp.visible).to.be.true; + expect(subform.parentVisible).to.be.true; + formcmp.visible = false; + expect(formcmp.visible).to.be.false; + expect(subform.parentVisible).to.be.false; + formcmp.visible = true; + expect(formcmp.visible).to.be.true; + expect(subform.parentVisible).to.be.true; + done(); + }, done) + .catch(done); + }); }); - }); - describe('get visible', function() { - it('should get visible flag from instance', function(done) { - Harness.testCreate(FormComponent, comp1) - .then(formcmp => { - expect(formcmp._visible).to.be.true; - expect(formcmp.visible).to.be.true; - formcmp.visible = false; - expect(formcmp.visible).to.be.false; - done(); - }, done) - .catch(done); + describe('get visible', function () { + it('should get visible flag from instance', function (done) { + Harness.testCreate(FormComponent, comp1) + .then((formcmp) => { + expect(formcmp._visible).to.be.true; + expect(formcmp.visible).to.be.true; + formcmp.visible = false; + expect(formcmp.visible).to.be.false; + done(); + }, done) + .catch(done); + }); }); - }); - describe('set parentVisible', function() { - it('should set parentVisible flag on instance', function(done) { - Harness.testCreate(FormComponent, comp1) - .then(formcmp => { - expect(formcmp._parentVisible).to.be.true; - formcmp.parentVisible = false; - expect(formcmp._parentVisible).to.be.false; - done(); - }, done) - .catch(done); - }); + describe('set parentVisible', function () { + it('should set parentVisible flag on instance', function (done) { + Harness.testCreate(FormComponent, comp1) + .then((formcmp) => { + expect(formcmp._parentVisible).to.be.true; + formcmp.parentVisible = false; + expect(formcmp._parentVisible).to.be.false; + done(); + }, done) + .catch(done); + }); - it('should update sub form visibility', function(done) { - let formcmp; - Harness.testCreate(FormComponent, comp1) - .then(cmp => { - formcmp = cmp; - return formcmp.subFormReady; - }, done) - .then(subform => { - expect(formcmp.parentVisible).to.be.true; - expect(subform.parentVisible).to.be.true; - formcmp.parentVisible = false; - expect(formcmp.parentVisible).to.be.false; - expect(subform.parentVisible).to.be.false; - formcmp.parentVisible = true; - expect(formcmp.parentVisible).to.be.true; - expect(subform.parentVisible).to.be.true; - done(); - }, done) - .catch(done); + it('should update sub form visibility', function (done) { + let formcmp; + Harness.testCreate(FormComponent, comp1) + .then((cmp) => { + formcmp = cmp; + return formcmp.subFormReady; + }, done) + .then((subform) => { + expect(formcmp.parentVisible).to.be.true; + expect(subform.parentVisible).to.be.true; + formcmp.parentVisible = false; + expect(formcmp.parentVisible).to.be.false; + expect(subform.parentVisible).to.be.false; + formcmp.parentVisible = true; + expect(formcmp.parentVisible).to.be.true; + expect(subform.parentVisible).to.be.true; + done(); + }, done) + .catch(done); + }); }); - }); - describe('get parentVisible', function() { - it('should get parentVisible flag from instance', function(done) { - Harness.testCreate(FormComponent, comp1) - .then(formcmp => { - expect(formcmp._parentVisible).to.be.true; - expect(formcmp.parentVisible).to.be.true; - formcmp.parentVisible = false; - expect(formcmp.parentVisible).to.be.false; - done(); - }, done) - .catch(done); + describe('get parentVisible', function () { + it('should get parentVisible flag from instance', function (done) { + Harness.testCreate(FormComponent, comp1) + .then((formcmp) => { + expect(formcmp._parentVisible).to.be.true; + expect(formcmp.parentVisible).to.be.true; + formcmp.parentVisible = false; + expect(formcmp.parentVisible).to.be.false; + done(); + }, done) + .catch(done); + }); }); - }); - describe('Modal Edit', function() { - it('Should render preview when modalEdit', function(done) { - const formElement = document.createElement('div'); - const form = new Webform(formElement); - form.setForm(formModalEdit).then(() => { - const preview = form.element.querySelector('[ref="openModal"]'); - assert(preview, 'Should contain element to open a modal window'); - done(); - }).catch(done); + describe('Modal Edit', function () { + it('Should render preview when modalEdit', function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement); + form.setForm(formModalEdit) + .then(() => { + const preview = + form.element.querySelector('[ref="openModal"]'); + assert( + preview, + 'Should contain element to open a modal window', + ); + done(); + }) + .catch(done); + }); }); - }); - describe('Conditional rendering', function() { - it('Should render and set submission to conditional form component', function(done) { - const formElement = document.createElement('div'); - const form = new Webform(formElement); - form.setForm(formComponentWithConditionalRenderingForm).then(() => { - form.setSubmission({ - data: { - checkbox: true, - form: { - data: { - textField: 'test' - } - } - } - }).then(() => { - setTimeout(() => { - const checkbox = formElement.querySelector('input[name="data[checkbox]"]'); - const textField = formElement.querySelector('input[name="data[textField]"]'); - expect(checkbox).to.not.be.null; - assert.equal(checkbox.checked, true); - expect(textField).to.not.be.null; - assert.equal(textField.value, 'test'); - done(); - }, 250); + describe('Conditional rendering', function () { + it('Should render and set submission to conditional form component', function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement); + form.setForm(formComponentWithConditionalRenderingForm) + .then(() => { + form.setSubmission({ + data: { + checkbox: true, + form: { + data: { + textField: 'test', + }, + }, + }, + }).then(() => { + setTimeout(() => { + const checkbox = formElement.querySelector( + 'input[name="data[checkbox]"]', + ); + const textField = formElement.querySelector( + 'input[name="data[textField]"]', + ); + expect(checkbox).to.not.be.null; + assert.equal(checkbox.checked, true); + expect(textField).to.not.be.null; + assert.equal(textField.value, 'test'); + done(); + }, 250); + }); + }) + .catch(done); }); - }).catch(done); }); - }); - describe('Advanced Logic', function() { - it('Should disable all components of the form', function(done) { - const formElement = document.createElement('div'); - const form = new Webform(formElement); - form.setForm(comp6) - .then(() => { - const textField = form.getComponent(['textField']); - const nestedForm = form.getComponent(['form']); - textField.setValue('test', { modified: true }); - setTimeout(() => { - assert.equal(textField.dataValue, 'test', 'Should set value'); - assert.equal(nestedForm.disabled, true, 'Nested Form should be disabled'); - done(); - }, 300); - }) - .catch(done); + describe('Advanced Logic', function () { + it('Should disable all components of the form', function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement); + form.setForm(comp6) + .then(() => { + const textField = form.getComponent(['textField']); + const nestedForm = form.getComponent(['form']); + textField.setValue('test', { modified: true }); + setTimeout(() => { + assert.equal( + textField.dataValue, + 'test', + 'Should set value', + ); + assert.equal( + nestedForm.disabled, + true, + 'Nested Form should be disabled', + ); + done(); + }, 300); + }) + .catch(done); + }); }); - }); - describe('Inside Collapsed Panel', function() { - it('Should be able to set value to Nested Form Component inside collapsed Panel', function(done) { - const formElement = document.createElement('div'); - const form = new Webform(formElement); - form.setForm(comp5) - .then(() => { - const textField = form.getComponent(['form', 'textField']); - const panel = form.getComponent('panel333'); - textField.setValue('123', { modified: true }); - setTimeout(() => { - assert.equal(textField.dataValue, '123', 'Should set value'); - panel.collapsed = false; - setTimeout(() => { - assert.equal(textField.dataValue, '123', 'Should keep the set value after the panel was expanded'); - done(); - }, 300); - }, 300); - }) - .catch(done); + describe('Inside Collapsed Panel', function () { + it('Should be able to set value to Nested Form Component inside collapsed Panel', function (done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement); + form.setForm(comp5) + .then(() => { + const textField = form.getComponent(['form', 'textField']); + const panel = form.getComponent('panel333'); + textField.setValue('123', { modified: true }); + setTimeout(() => { + assert.equal( + textField.dataValue, + '123', + 'Should set value', + ); + panel.collapsed = false; + setTimeout(() => { + assert.equal( + textField.dataValue, + '123', + 'Should keep the set value after the panel was expanded', + ); + done(); + }, 300); + }, 300); + }) + .catch(done); + }); }); - }); }); -describe('Wizard Component', function() { - it('Should build a wizard component and disable cancel, next and breadcrumbs', function(done) { - Harness.testCreate(FormComponent, comp3, { - breadcrumbSettings: - { clickable: false }, - buttonSettings: - { showCancel: false, showPrevious: false } - }).then(() => { - done(); +describe('Wizard Component', function () { + it('Should build a wizard component and disable cancel, next and breadcrumbs', function (done) { + Harness.testCreate(FormComponent, comp3, { + breadcrumbSettings: { clickable: false }, + buttonSettings: { showCancel: false, showPrevious: false }, + }).then(() => { + done(); + }); }); - }); }); diff --git a/src/components/form/editForm/Form.edit.data.js b/src/components/form/editForm/Form.edit.data.js index c091424ea3..be8367435f 100644 --- a/src/components/form/editForm/Form.edit.data.js +++ b/src/components/form/editForm/Form.edit.data.js @@ -1,22 +1,30 @@ import EditFormUtils from '../../_classes/component/editForm/utils'; /* eslint-disable max-len */ export default [ - EditFormUtils.javaScriptValue('Custom Default Value', 'customDefaultValue', 'customDefaultValue', 120, - '

    Example:

    value = data.firstName + " " + data.lastName;

    ', - '

    Example:

    {"cat": [{"var": "data.firstName"}, " ", {"var": "data.lastName"}]}
    ' - ), - EditFormUtils.javaScriptValue('Calculated Value', 'calculateValue', 'calculateValue', 130, - '

    Example:

    value = data.a + data.b + data.c;

    ', - '

    Example:

    {"+": [{"var": "data.a"}, {"var": "data.b"}, {"var": "data.c"}]}

    Click here for an example

    ' - ), - { - weight: 140, - type: 'checkbox', - label: 'Clear Value When Hidden', - key: 'clearOnHide', - defaultValue: true, - tooltip: 'When a field is hidden, clear the value.', - input: true - }, + EditFormUtils.javaScriptValue( + 'Custom Default Value', + 'customDefaultValue', + 'customDefaultValue', + 120, + '

    Example:

    value = data.firstName + " " + data.lastName;

    ', + '

    Example:

    {"cat": [{"var": "data.firstName"}, " ", {"var": "data.lastName"}]}
    ', + ), + EditFormUtils.javaScriptValue( + 'Calculated Value', + 'calculateValue', + 'calculateValue', + 130, + '

    Example:

    value = data.a + data.b + data.c;

    ', + '

    Example:

    {"+": [{"var": "data.a"}, {"var": "data.b"}, {"var": "data.c"}]}

    Click here for an example

    ', + ), + { + weight: 140, + type: 'checkbox', + label: 'Clear Value When Hidden', + key: 'clearOnHide', + defaultValue: true, + tooltip: 'When a field is hidden, clear the value.', + input: true, + }, ]; /* eslint-enable max-len */ diff --git a/src/components/form/editForm/Form.edit.display.js b/src/components/form/editForm/Form.edit.display.js index a913d73847..ebbc365d80 100644 --- a/src/components/form/editForm/Form.edit.display.js +++ b/src/components/form/editForm/Form.edit.display.js @@ -1,27 +1,27 @@ export default [ - { - key: 'labelPosition', - ignore: true - }, - { - key: 'placeholder', - ignore: true - }, - { - key: 'description', - ignore: true - }, - { - key: 'tooltip', - ignore: true - }, - { - key: 'tabIndex', - ignore: true - }, - { - key: 'autofocus', - ignore: true - }, + { + key: 'labelPosition', + ignore: true, + }, + { + key: 'placeholder', + ignore: true, + }, + { + key: 'description', + ignore: true, + }, + { + key: 'tooltip', + ignore: true, + }, + { + key: 'tabIndex', + ignore: true, + }, + { + key: 'autofocus', + ignore: true, + }, ]; /* eslint-enable max-len */ diff --git a/src/components/form/editForm/Form.edit.form.js b/src/components/form/editForm/Form.edit.form.js index 7fab9e942d..07bcba8deb 100644 --- a/src/components/form/editForm/Form.edit.form.js +++ b/src/components/form/editForm/Form.edit.form.js @@ -1,73 +1,85 @@ export default [ - { - type: 'select', - input: true, - dataSrc: 'url', - data: { - url: '/form?limit=1000000&select=_id,title,display' + { + type: 'select', + input: true, + dataSrc: 'url', + data: { + url: '/form?limit=1000000&select=_id,title,display', + }, + searchField: 'title__regex', + template: '{{ item.title }}', + valueProperty: '_id', + authenticate: true, + label: 'Form', + key: 'form', + weight: 10, + lazyLoad: false, + tooltip: 'The form to load within this form component.', + validate: { + required: true, + }, }, - searchField: 'title__regex', - template: '{{ item.title }}', - valueProperty: '_id', - authenticate: true, - label: 'Form', - key: 'form', - weight: 10, - lazyLoad: false, - tooltip: 'The form to load within this form component.', - validate: { - required: true, - }, - }, - { - label: 'Lazy Load', - inputType: 'checkbox', - defaultValue: true, - clearOnHide: true, - errorLabel: '', - key: 'lazyLoad', - type: 'checkbox', - tooltip: 'if it is checked, the subform is loaded after navigation to the page with this component within the wizard.', - input: true, - customConditional( { instance, data }) { - const formInfo = instance.root?.getComponent('form')?.defaultDownloadedResources.find(res => res._id === data.form); - const displayMode = 'wizard'; + { + label: 'Lazy Load', + inputType: 'checkbox', + defaultValue: true, + clearOnHide: true, + errorLabel: '', + key: 'lazyLoad', + type: 'checkbox', + tooltip: + 'if it is checked, the subform is loaded after navigation to the page with this component within the wizard.', + input: true, + customConditional({ instance, data }) { + const formInfo = instance.root + ?.getComponent('form') + ?.defaultDownloadedResources.find( + (res) => res._id === data.form, + ); + const displayMode = 'wizard'; - return instance.options?.editForm?.display === displayMode && formInfo && formInfo.display !== displayMode; + return ( + instance.options?.editForm?.display === displayMode && + formInfo && + formInfo.display !== displayMode + ); + }, + }, + { + type: 'select', + input: true, + dataSrc: 'url', + data: { + url: '/form/{{ data.form }}/v', + }, + searchField: 'title__regex', + template: '{{ item._vid }}', + valueProperty: '_id', + authenticate: true, + label: 'Form Revision', + key: 'revision', + weight: 10, + lazyLoad: true, + tooltip: + 'You can lock the nested form to a specific revision by choosing the revision number here.', + customConditional: 'show = !!data.form', + }, + { + type: 'checkbox', + input: true, + weight: 19, + key: 'useOriginalRevision', + label: 'Use Original Revision while Submissions Viewing', + tooltip: + 'Using this option will make form load the original revision (the one which was used to make a submission) when viewing a submission.', }, - }, - { - type: 'select', - input: true, - dataSrc: 'url', - data: { - url: '/form/{{ data.form }}/v' + { + type: 'checkbox', + input: true, + weight: 20, + key: 'reference', + label: 'Save as reference', + tooltip: + 'Using this option will save this field as a reference and link its value to the value of the origin record.', }, - searchField: 'title__regex', - template: '{{ item._vid }}', - valueProperty: '_id', - authenticate: true, - label: 'Form Revision', - key: 'revision', - weight: 10, - lazyLoad: true, - tooltip: 'You can lock the nested form to a specific revision by choosing the revision number here.', - customConditional: 'show = !!data.form' - }, - { - type: 'checkbox', - input: true, - weight: 19, - key: 'useOriginalRevision', - label: 'Use Original Revision while Submissions Viewing', - tooltip: 'Using this option will make form load the original revision (the one which was used to make a submission) when viewing a submission.' - }, - { - type: 'checkbox', - input: true, - weight: 20, - key: 'reference', - label: 'Save as reference', - tooltip: 'Using this option will save this field as a reference and link its value to the value of the origin record.' - } ]; diff --git a/src/components/form/fixtures/comp1.js b/src/components/form/fixtures/comp1.js index 058f99504e..260d151d33 100644 --- a/src/components/form/fixtures/comp1.js +++ b/src/components/form/fixtures/comp1.js @@ -1,84 +1,78 @@ export default { - key: 'form1', - input: false, - tableView: true, - components: [ - { - input: true, - tableView: true, - inputType: 'text', - inputMask: '', - label: 'First Name', - key: 'firstName', - placeholder: '', - prefix: '', - suffix: '', - multiple: false, - defaultValue: '', - protected: false, - unique: false, - persistent: true, - clearOnHide: true, - validate: { - required: false, - minLength: '', - maxLength: '', - pattern: '', - custom: '', - customPrivate: false - }, - conditional: { + key: 'form1', + input: false, + tableView: true, + components: [ + { + input: true, + tableView: true, + inputType: 'text', + inputMask: '', + label: 'First Name', + key: 'firstName', + placeholder: '', + prefix: '', + suffix: '', + multiple: false, + defaultValue: '', + protected: false, + unique: false, + persistent: true, + clearOnHide: true, + validate: { + required: false, + minLength: '', + maxLength: '', + pattern: '', + custom: '', + customPrivate: false, + }, + conditional: { + show: '', + when: null, + eq: '', + }, + type: 'textfield', + tags: [], + }, + { + input: true, + tableView: true, + inputType: 'text', + inputMask: '', + label: 'Last Name', + key: 'lastName', + placeholder: '', + prefix: '', + suffix: '', + multiple: false, + defaultValue: '', + protected: false, + unique: false, + persistent: true, + clearOnHide: true, + validate: { + required: false, + minLength: '', + maxLength: '', + pattern: '', + custom: '', + customPrivate: false, + }, + conditional: { + show: '', + when: null, + eq: '', + }, + type: 'textfield', + tags: [], + }, + ], + type: 'form', + tags: [], + conditional: { show: '', when: null, - eq: '' - }, - type: 'textfield', - tags: [ - - ] + eq: '', }, - { - input: true, - tableView: true, - inputType: 'text', - inputMask: '', - label: 'Last Name', - key: 'lastName', - placeholder: '', - prefix: '', - suffix: '', - multiple: false, - defaultValue: '', - protected: false, - unique: false, - persistent: true, - clearOnHide: true, - validate: { - required: false, - minLength: '', - maxLength: '', - pattern: '', - custom: '', - customPrivate: false - }, - conditional: { - show: '', - when: null, - eq: '' - }, - type: 'textfield', - tags: [ - - ] - } - ], - type: 'form', - tags: [ - - ], - conditional: { - show: '', - when: null, - eq: '' - } }; diff --git a/src/components/form/fixtures/comp2.js b/src/components/form/fixtures/comp2.js index 761be5589e..fb0e3347fb 100644 --- a/src/components/form/fixtures/comp2.js +++ b/src/components/form/fixtures/comp2.js @@ -1,15 +1,13 @@ export default { - key: 'form1', - input: false, - tableView: true, - src: 'https://remote.form.io/testproject/example', - type: 'form', - tags: [ - - ], - conditional: { - show: '', - when: null, - eq: '' - } + key: 'form1', + input: false, + tableView: true, + src: 'https://remote.form.io/testproject/example', + type: 'form', + tags: [], + conditional: { + show: '', + when: null, + eq: '', + }, }; diff --git a/src/components/form/fixtures/comp3.js b/src/components/form/fixtures/comp3.js index b0caf74039..5bb910af9b 100644 --- a/src/components/form/fixtures/comp3.js +++ b/src/components/form/fixtures/comp3.js @@ -1,168 +1,168 @@ export default { - type: 'form', - tags: [], - components: [ - { - type: 'panel', - title: 'Page 1', - isNew: false, - components: [ + type: 'form', + tags: [], + components: [ { - autofocus: false, - input: true, - tableView: true, - inputType: 'text', - inputMask: '', - label: 'Text', - key: 'page1Text', - placeholder: '', - prefix: '', - suffix: '', - multiple: false, - defaultValue: '', - protected: false, - unique: false, - persistent: true, - hidden: false, - clearOnHide: true, - spellcheck: true, - validate: { - required: false, - minLength: '', - maxLength: '', - pattern: '', - custom: '', - customPrivate: false - }, - conditional: { - show: '', - when: null, - eq: '' - }, - type: 'textfield', - labelPosition: 'top', - inputFormat: 'plain', - tags: [], - properties: { } - } - ], - input: false, - key: 'page1', - clearOnHide: false, - theme: 'default', - tableView: false, - hideLabel: false - }, - { - type: 'panel', - title: 'Page 2', - isNew: false, - components: [ + type: 'panel', + title: 'Page 1', + isNew: false, + components: [ + { + autofocus: false, + input: true, + tableView: true, + inputType: 'text', + inputMask: '', + label: 'Text', + key: 'page1Text', + placeholder: '', + prefix: '', + suffix: '', + multiple: false, + defaultValue: '', + protected: false, + unique: false, + persistent: true, + hidden: false, + clearOnHide: true, + spellcheck: true, + validate: { + required: false, + minLength: '', + maxLength: '', + pattern: '', + custom: '', + customPrivate: false, + }, + conditional: { + show: '', + when: null, + eq: '', + }, + type: 'textfield', + labelPosition: 'top', + inputFormat: 'plain', + tags: [], + properties: {}, + }, + ], + input: false, + key: 'page1', + clearOnHide: false, + theme: 'default', + tableView: false, + hideLabel: false, + }, { - autofocus: false, - input: true, - tableView: true, - inputType: 'number', - label: 'Number', - key: 'page2Number', - placeholder: '', - prefix: '', - suffix: '', - defaultValue: '', - protected: false, - persistent: true, - hidden: false, - clearOnHide: true, - validate: { - required: false, - min: '', - max: '', - step: 'any', - integer: '', - multiple: '', - custom: '' - }, - type: 'number', - labelPosition: 'top', - tags: [], - conditional: { - show: '', - when: null, - eq: '' - }, - properties: { } - } - ], - input: false, - key: 'page2', - clearOnHide: false, - theme: 'default', - tableView: false, - hideLabel: false - }, - { - type: 'panel', - title: 'Page 3', - isNew: false, - components: [ + type: 'panel', + title: 'Page 2', + isNew: false, + components: [ + { + autofocus: false, + input: true, + tableView: true, + inputType: 'number', + label: 'Number', + key: 'page2Number', + placeholder: '', + prefix: '', + suffix: '', + defaultValue: '', + protected: false, + persistent: true, + hidden: false, + clearOnHide: true, + validate: { + required: false, + min: '', + max: '', + step: 'any', + integer: '', + multiple: '', + custom: '', + }, + type: 'number', + labelPosition: 'top', + tags: [], + conditional: { + show: '', + when: null, + eq: '', + }, + properties: {}, + }, + ], + input: false, + key: 'page2', + clearOnHide: false, + theme: 'default', + tableView: false, + hideLabel: false, + }, { - autofocus: false, - input: true, - inputType: 'checkbox', - tableView: true, - label: 'Hola', - dataGridLabel: false, - key: 'page3Hola', - defaultValue: false, - protected: false, - persistent: true, - hidden: false, - name: '', - value: '', - clearOnHide: true, - validate: { - required: false - }, - type: 'checkbox', - labelPosition: 'right', - hideLabel: false, - tags: [], - conditional: { - show: '', - when: null, - eq: '' - }, - properties: { } - } - ], - input: false, - key: 'page3', - clearOnHide: false, - theme: 'default', - tableView: false, - hideLabel: false - }, - { - autofocus: false, - input: true, - label: 'Submit', - tableView: false, - key: 'submit', - size: 'md', - leftIcon: '', - rightIcon: '', - block: false, - action: 'submit', - disableOnInvalid: false, - theme: 'primary', - type: 'button' - } - ], - revisions: '', - title: 'Wiizard', - display: 'wizard', - submissionAccess: [], - settings: { }, - name: 'wiizard', - path: 'wiizard' + type: 'panel', + title: 'Page 3', + isNew: false, + components: [ + { + autofocus: false, + input: true, + inputType: 'checkbox', + tableView: true, + label: 'Hola', + dataGridLabel: false, + key: 'page3Hola', + defaultValue: false, + protected: false, + persistent: true, + hidden: false, + name: '', + value: '', + clearOnHide: true, + validate: { + required: false, + }, + type: 'checkbox', + labelPosition: 'right', + hideLabel: false, + tags: [], + conditional: { + show: '', + when: null, + eq: '', + }, + properties: {}, + }, + ], + input: false, + key: 'page3', + clearOnHide: false, + theme: 'default', + tableView: false, + hideLabel: false, + }, + { + autofocus: false, + input: true, + label: 'Submit', + tableView: false, + key: 'submit', + size: 'md', + leftIcon: '', + rightIcon: '', + block: false, + action: 'submit', + disableOnInvalid: false, + theme: 'primary', + type: 'button', + }, + ], + revisions: '', + title: 'Wiizard', + display: 'wizard', + submissionAccess: [], + settings: {}, + name: 'wiizard', + path: 'wiizard', }; diff --git a/src/components/form/fixtures/comp4.js b/src/components/form/fixtures/comp4.js index d433aaac24..b25f3f0cbd 100644 --- a/src/components/form/fixtures/comp4.js +++ b/src/components/form/fixtures/comp4.js @@ -1,89 +1,89 @@ export default { - type: 'form', - owner: null, - components: [ - { - label: 'Form', - tableView: true, - display: 'form', - components: [ + type: 'form', + owner: null, + components: [ { - input: true, - tableView: true, - label: 'Make', - key: 'make', - data: { - values: [ - { - value: 'ford', - label: 'Ford' - }, - { - value: 'honda', - label: 'Honda' - } + label: 'Form', + tableView: true, + display: 'form', + components: [ + { + input: true, + tableView: true, + label: 'Make', + key: 'make', + data: { + values: [ + { + value: 'ford', + label: 'Ford', + }, + { + value: 'honda', + label: 'Honda', + }, + ], + }, + dataSrc: 'values', + template: '{{ item.label }}', + validate: { + required: true, + }, + type: 'select', + }, + { + label: 'Model', + widget: 'choicesjs', + placeholder: 'Select your model', + tableView: true, + dataSrc: 'url', + data: { + url: 'https://vpic.nhtsa.dot.gov/api/vehicles/getmodelsformake/{{ data.make }}?format=json', + headers: [ + { + key: '', + value: '', + }, + ], + }, + valueProperty: 'Model_Name', + template: '{{ item.Model_Name }}', + refreshOn: 'make', + clearOnRefresh: true, + selectThreshold: 0.3, + clearOnHide: false, + validate: { + required: true, + }, + key: 'model', + type: 'select', + indexeddb: { + filter: {}, + }, + selectValues: 'Results', + input: true, + disableLimit: false, + lazyLoad: false, + }, + { + input: true, + label: 'Submit', + tableView: false, + key: 'submit', + type: 'button', + }, ], - }, - dataSrc: 'values', - template: '{{ item.label }}', - validate: { - required: true - }, - type: 'select', + key: 'form', + type: 'form', + input: true, }, { - label: 'Model', - widget: 'choicesjs', - placeholder: 'Select your model', - tableView: true, - dataSrc: 'url', - data: { - url: 'https://vpic.nhtsa.dot.gov/api/vehicles/getmodelsformake/{{ data.make }}?format=json', - headers: [ - { - key: '', - value: '' - } - ] - }, - valueProperty: 'Model_Name', - template: '{{ item.Model_Name }}', - refreshOn: 'make', - clearOnRefresh: true, - selectThreshold: 0.3, - clearOnHide: false, - validate: { - required: true - }, - key: 'model', - type: 'select', - indexeddb: { - filter: {} - }, - selectValues: 'Results', - input: true, - disableLimit: false, - lazyLoad: false + type: 'button', + label: 'Submit', + key: 'submit', + disableOnInvalid: true, + input: true, + tableView: false, }, - { - input: true, - label: 'Submit', - tableView: false, - key: 'submit', - type: 'button' - } - ], - key: 'form', - type: 'form', - input: true - }, - { - type: 'button', - label: 'Submit', - key: 'submit', - disableOnInvalid: true, - input: true, - tableView: false - } - ], + ], }; diff --git a/src/components/form/fixtures/comp5.js b/src/components/form/fixtures/comp5.js index be8dfd0cdd..81a897d332 100644 --- a/src/components/form/fixtures/comp5.js +++ b/src/components/form/fixtures/comp5.js @@ -1,43 +1,43 @@ export default { - type: 'form', - owner: null, - components: [ - { - title: 'Panel 333', - collapsible: true, - key: 'panel333', - type: 'panel', - label: 'Panel 333', - input: false, - tableView: false, - components: [ + type: 'form', + owner: null, + components: [ { - label: 'Form', - tableView: true, - display: 'form', - components: [ - { - label: 'Text Field', - tableView: true, - key: 'textField', - type: 'textfield', - input: true, - }, - ], - key: 'form', - type: 'form', - input: true, + title: 'Panel 333', + collapsible: true, + key: 'panel333', + type: 'panel', + label: 'Panel 333', + input: false, + tableView: false, + components: [ + { + label: 'Form', + tableView: true, + display: 'form', + components: [ + { + label: 'Text Field', + tableView: true, + key: 'textField', + type: 'textfield', + input: true, + }, + ], + key: 'form', + type: 'form', + input: true, + }, + ], + collapsed: true, }, - ], - collapsed: true, - }, - { - type: 'button', - label: 'Submit', - key: 'submit', - disableOnInvalid: true, - input: true, - tableView: false, - }, - ], + { + type: 'button', + label: 'Submit', + key: 'submit', + disableOnInvalid: true, + input: true, + tableView: false, + }, + ], }; diff --git a/src/components/form/fixtures/comp6.js b/src/components/form/fixtures/comp6.js index 5bc0f6a440..936cd27fef 100644 --- a/src/components/form/fixtures/comp6.js +++ b/src/components/form/fixtures/comp6.js @@ -1,92 +1,96 @@ export default { - components: [ - { - label: 'Text Field', - tableView: true, - key: 'textField', - type: 'textfield', - input: true, - }, - { - label: 'Form', - tableView: true, - display: 'form', - components: [ + components: [ { - label: 'Text Field', - tableView: true, - key: 'textField', - type: 'textfield', - input: true, + label: 'Text Field', + tableView: true, + key: 'textField', + type: 'textfield', + input: true, }, { - label: 'Text Area', - autoExpand: false, - tableView: true, - key: 'textArea', - type: 'textarea', - input: true, + label: 'Form', + tableView: true, + display: 'form', + components: [ + { + label: 'Text Field', + tableView: true, + key: 'textField', + type: 'textfield', + input: true, + }, + { + label: 'Text Area', + autoExpand: false, + tableView: true, + key: 'textArea', + type: 'textarea', + input: true, + }, + ], + logic: [ + { + name: 'disabled', + trigger: { + type: 'simple', + simple: { show: true, when: 'textField', eq: 'test' }, + conditionConditionGrid: [], + }, + actions: [ + { + name: 'Disable', + type: 'property', + property: { + label: 'Disabled', + value: 'disabled', + type: 'boolean', + }, + state: true, + variableVariableGrid: [], + }, + ], + }, + { + name: 'required', + trigger: { + type: 'simple', + simple: { + show: true, + when: 'textField', + eq: 'required', + }, + conditionConditionGrid: [], + }, + actions: [ + { + name: 'required', + type: 'property', + property: { + label: 'Required', + value: 'validate.required', + type: 'boolean', + }, + state: true, + variableVariableGrid: [], + }, + ], + }, + ], + key: 'form', + type: 'form', + input: true, }, - ], - logic: [ { - name: 'disabled', - trigger: { - type: 'simple', - simple: { show: true, when: 'textField', eq: 'test' }, - conditionConditionGrid: [], - }, - actions: [ - { - name: 'Disable', - type: 'property', - property: { - label: 'Disabled', - value: 'disabled', - type: 'boolean', - }, - state: true, - variableVariableGrid: [], - }, - ], + type: 'button', + label: 'Submit', + key: 'submit', + disableOnInvalid: true, + input: true, + tableView: false, }, - { - name: 'required', - trigger: { - type: 'simple', - simple: { show: true, when: 'textField', eq: 'required' }, - conditionConditionGrid: [], - }, - actions: [ - { - name: 'required', - type: 'property', - property: { - label: 'Required', - value: 'validate.required', - type: 'boolean', - }, - state: true, - variableVariableGrid: [], - }, - ], - }, - ], - key: 'form', - type: 'form', - input: true, - }, - { - type: 'button', - label: 'Submit', - key: 'submit', - disableOnInvalid: true, - input: true, - tableView: false, - }, - ], - title: 'MainFormWithNested', - display: 'form', - path: 'nestedmain', - name: 'mainFormWithNested', + ], + title: 'MainFormWithNested', + display: 'form', + path: 'nestedmain', + name: 'mainFormWithNested', }; diff --git a/src/components/form/fixtures/formModalEdit.js b/src/components/form/fixtures/formModalEdit.js index ba020d71e8..30e3b858f5 100644 --- a/src/components/form/fixtures/formModalEdit.js +++ b/src/components/form/fixtures/formModalEdit.js @@ -1,50 +1,49 @@ export default { - '_id': '5ed8d0f9b507cf407c61f2d6', - 'type': 'form', - 'owner': '5e05a6b7549cdc2ece30c6b0', - 'components': [ - { - 'label': 'Form', - 'tableView': true, - 'modalEdit': true, - 'components': [ + _id: '5ed8d0f9b507cf407c61f2d6', + type: 'form', + owner: '5e05a6b7549cdc2ece30c6b0', + components: [ { - 'label': 'Nested Text Field', - 'tableView': true, - 'key': 'nestedTextField', - 'type': 'textfield', - 'input': true - } - ], - 'key': 'form', - 'type': 'form', - 'input': true - }, - { - 'type': 'button', - 'label': 'Submit', - 'key': 'submit', - 'disableOnInvalid': true, - 'input': true, - 'tableView': false - } - ], - 'controller': '', - 'revisions': '', - '_vid': 0, - 'title': 'nestedModal', - 'display': 'form', - 'access': [ - { - 'roles': [ - '5e96e79ee1c3ad3178454100', - '5e96e79ee1c3ad3178454101', - '5e96e79ee1c3ad3178454102' - ], - 'type': 'read_all' - } - ], - 'name': 'nestedModal', - 'path': 'nestedmodal', + label: 'Form', + tableView: true, + modalEdit: true, + components: [ + { + label: 'Nested Text Field', + tableView: true, + key: 'nestedTextField', + type: 'textfield', + input: true, + }, + ], + key: 'form', + type: 'form', + input: true, + }, + { + type: 'button', + label: 'Submit', + key: 'submit', + disableOnInvalid: true, + input: true, + tableView: false, + }, + ], + controller: '', + revisions: '', + _vid: 0, + title: 'nestedModal', + display: 'form', + access: [ + { + roles: [ + '5e96e79ee1c3ad3178454100', + '5e96e79ee1c3ad3178454101', + '5e96e79ee1c3ad3178454102', + ], + type: 'read_all', + }, + ], + name: 'nestedModal', + path: 'nestedmodal', }; - diff --git a/src/components/form/fixtures/index.js b/src/components/form/fixtures/index.js index 182144551c..9780714f32 100644 --- a/src/components/form/fixtures/index.js +++ b/src/components/form/fixtures/index.js @@ -6,4 +6,3 @@ import comp4 from './comp4'; import comp5 from './comp5'; import comp6 from './comp6'; export { formModalEdit, comp1, comp2, comp3, comp4, comp5, comp6 }; - diff --git a/src/components/form/fixtures/values.js b/src/components/form/fixtures/values.js index 50fb1e801d..185ebabba2 100644 --- a/src/components/form/fixtures/values.js +++ b/src/components/form/fixtures/values.js @@ -1,7 +1,7 @@ export default [ - { - data: { - foo: 'bar', + { + data: { + foo: 'bar', + }, }, - }, ]; diff --git a/src/components/hidden/Hidden.form.js b/src/components/hidden/Hidden.form.js index 1e11a9404b..fd41483eb4 100644 --- a/src/components/hidden/Hidden.form.js +++ b/src/components/hidden/Hidden.form.js @@ -2,23 +2,26 @@ import Components from '../Components'; import HiddenEditDisplay from './editForm/Hidden.edit.display'; import HiddenEditData from './editForm/Hidden.edit.data'; -export default function(...extend) { - return Components.baseEditForm([ - { - key: 'display', - components: HiddenEditDisplay - }, - { - key: 'data', - components: HiddenEditData - }, - { - key: 'validation', - ignore: true - }, - { - key: 'conditional', - ignore: true - }, - ], ...extend); +export default function (...extend) { + return Components.baseEditForm( + [ + { + key: 'display', + components: HiddenEditDisplay, + }, + { + key: 'data', + components: HiddenEditData, + }, + { + key: 'validation', + ignore: true, + }, + { + key: 'conditional', + ignore: true, + }, + ], + ...extend, + ); } diff --git a/src/components/hidden/Hidden.js b/src/components/hidden/Hidden.js index 0f09c4adf5..25ddb17ccf 100644 --- a/src/components/hidden/Hidden.js +++ b/src/components/hidden/Hidden.js @@ -1,65 +1,68 @@ import Input from '../_classes/input/Input'; export default class HiddenComponent extends Input { - static schema(...extend) { - return Input.schema({ - type: 'hidden', - tableView: false, - inputType: 'hidden' - }, ...extend); - } + static schema(...extend) { + return Input.schema( + { + type: 'hidden', + tableView: false, + inputType: 'hidden', + }, + ...extend, + ); + } - static get builderInfo() { - return { - title: 'Hidden', - group: 'data', - icon: 'user-secret', - weight: 0, - documentation: '/userguide/form-building/data-components#hidden', - showPreview: false, - schema: HiddenComponent.schema() - }; - } + static get builderInfo() { + return { + title: 'Hidden', + group: 'data', + icon: 'user-secret', + weight: 0, + documentation: '/userguide/form-building/data-components#hidden', + showPreview: false, + schema: HiddenComponent.schema(), + }; + } - get defaultSchema() { - return HiddenComponent.schema(); - } + get defaultSchema() { + return HiddenComponent.schema(); + } - get inputInfo() { - const info = super.elementInfo(); - info.type = 'input'; - info.attr.type = 'hidden'; - info.changeEvent = 'change'; - return info; - } + get inputInfo() { + const info = super.elementInfo(); + info.type = 'input'; + info.attr.type = 'hidden'; + info.changeEvent = 'change'; + return info; + } - get skipInEmail() { - return true; - } + get skipInEmail() { + return true; + } - /** - * Check if a component is eligible for multiple validation - * - * @return {boolean} - */ - validateMultiple() { - // Since "arrays" are able to be stored in hidden components, we need to turn off multiple validation. - return false; - } + /** + * Check if a component is eligible for multiple validation + * + * @return {boolean} + */ + validateMultiple() { + // Since "arrays" are able to be stored in hidden components, we need to turn off multiple validation. + return false; + } - labelIsHidden() { - return true; - } + labelIsHidden() { + return true; + } - get emptyValue() { - return ''; - } + get emptyValue() { + return ''; + } - setValue(value, flags = {}) { - return this.updateValue(value, flags); - } + setValue(value, flags = {}) { + return this.updateValue(value, flags); + } - getValue() { - return this.dataValue; - } + getValue() { + return this.dataValue; + } } diff --git a/src/components/hidden/Hidden.unit.js b/src/components/hidden/Hidden.unit.js index 3fd527d381..647189552f 100644 --- a/src/components/hidden/Hidden.unit.js +++ b/src/components/hidden/Hidden.unit.js @@ -1,12 +1,10 @@ import Harness from '../../../test/harness'; import HiddenComponent from './Hidden'; -import { - comp1 -} from './fixtures'; +import { comp1 } from './fixtures'; -describe('Hidden Component', function() { - it('Should build a hidden component', function() { - return Harness.testCreate(HiddenComponent, comp1); - }); +describe('Hidden Component', function () { + it('Should build a hidden component', function () { + return Harness.testCreate(HiddenComponent, comp1); + }); }); diff --git a/src/components/hidden/editForm/Hidden.edit.data.js b/src/components/hidden/editForm/Hidden.edit.data.js index 1f1b9c78f8..ff9e38f41a 100644 --- a/src/components/hidden/editForm/Hidden.edit.data.js +++ b/src/components/hidden/editForm/Hidden.edit.data.js @@ -1,14 +1,14 @@ export default [ - { - key: 'multiple', - ignore: true - }, - { - key: 'clearOnHide', - ignore: true - }, - { - key: 'allowCalculateOverride', - ignore: true - }, + { + key: 'multiple', + ignore: true, + }, + { + key: 'clearOnHide', + ignore: true, + }, + { + key: 'allowCalculateOverride', + ignore: true, + }, ]; diff --git a/src/components/hidden/editForm/Hidden.edit.display.js b/src/components/hidden/editForm/Hidden.edit.display.js index 2b10976da1..e5395374ec 100644 --- a/src/components/hidden/editForm/Hidden.edit.display.js +++ b/src/components/hidden/editForm/Hidden.edit.display.js @@ -1,42 +1,42 @@ export default [ - { - key: 'labelPosition', - ignore: true - }, - { - key: 'placeholder', - ignore: true - }, - { - key: 'description', - ignore: true - }, - { - key: 'tooltip', - ignore: true - }, - { - key: 'hideLabel', - ignore: true - }, - { - key: 'autofocus', - ignore: true - }, - { - key: 'tabindex', - ignore: true - }, - { - key: 'hidden', - ignore: true - }, - { - key: 'tableView', - ignore: true - }, - { - key: 'disabled', - ignore: true - }, + { + key: 'labelPosition', + ignore: true, + }, + { + key: 'placeholder', + ignore: true, + }, + { + key: 'description', + ignore: true, + }, + { + key: 'tooltip', + ignore: true, + }, + { + key: 'hideLabel', + ignore: true, + }, + { + key: 'autofocus', + ignore: true, + }, + { + key: 'tabindex', + ignore: true, + }, + { + key: 'hidden', + ignore: true, + }, + { + key: 'tableView', + ignore: true, + }, + { + key: 'disabled', + ignore: true, + }, ]; diff --git a/src/components/hidden/fixtures/comp1.js b/src/components/hidden/fixtures/comp1.js index 6ab5705187..c50e289426 100644 --- a/src/components/hidden/fixtures/comp1.js +++ b/src/components/hidden/fixtures/comp1.js @@ -1,18 +1,16 @@ export default { - 'conditional': { - 'eq': '', - 'when': null, - 'show': '' - }, - 'tags': [ - - ], - 'type': 'hidden', - 'persistent': true, - 'unique': false, - 'protected': false, - 'label': 'Secret', - 'key': 'secret', - 'tableView': true, - 'input': true + conditional: { + eq: '', + when: null, + show: '', + }, + tags: [], + type: 'hidden', + persistent: true, + unique: false, + protected: false, + label: 'Secret', + key: 'secret', + tableView: true, + input: true, }; diff --git a/src/components/hidden/fixtures/values.js b/src/components/hidden/fixtures/values.js index 142a5b4e6d..fb939ef522 100644 --- a/src/components/hidden/fixtures/values.js +++ b/src/components/hidden/fixtures/values.js @@ -1,4 +1 @@ -export default [ - 'one', - 1, -]; +export default ['one', 1]; diff --git a/src/components/html/HTML.form.js b/src/components/html/HTML.form.js index 7f793d24f8..0d5f4329ec 100644 --- a/src/components/html/HTML.form.js +++ b/src/components/html/HTML.form.js @@ -2,23 +2,26 @@ import Components from '../Components'; import HTMLEditDisplay from './editForm/HTML.edit.display'; import HTMLEditLogic from './editForm/HTML.edit.logic'; -export default function(...extend) { - return Components.baseEditForm([ - { - key: 'display', - components: HTMLEditDisplay, - }, - { - key: 'data', - ignore: true, - }, - { - key: 'validation', - ignore: true, - }, - { - key: 'logic', - components: HTMLEditLogic, - }, - ], ...extend); +export default function (...extend) { + return Components.baseEditForm( + [ + { + key: 'display', + components: HTMLEditDisplay, + }, + { + key: 'data', + ignore: true, + }, + { + key: 'validation', + ignore: true, + }, + { + key: 'logic', + components: HTMLEditLogic, + }, + ], + ...extend, + ); } diff --git a/src/components/html/HTML.js b/src/components/html/HTML.js index 9ff661a67e..65fb8e00fc 100644 --- a/src/components/html/HTML.js +++ b/src/components/html/HTML.js @@ -2,107 +2,121 @@ import Component from '../_classes/component/Component'; import _ from 'lodash'; export default class HTMLComponent extends Component { - static schema(...extend) { - return Component.schema({ - label: 'HTML', - type: 'htmlelement', - tag: 'p', - attrs: [], - content: '', - input: false, - persistent: false - }, ...extend); - } - - static get builderInfo() { - return { - title: 'HTML Element', - group: 'layout', - icon: 'code', - weight: 0, - documentation: '/userguide/form-building/layout-components#html-element', - showPreview: false, - schema: HTMLComponent.schema() - }; - } - - static savedValueTypes() { - return []; - } + static schema(...extend) { + return Component.schema( + { + label: 'HTML', + type: 'htmlelement', + tag: 'p', + attrs: [], + content: '', + input: false, + persistent: false, + }, + ...extend, + ); + } - get defaultSchema() { - return HTMLComponent.schema(); - } + static get builderInfo() { + return { + title: 'HTML Element', + group: 'layout', + icon: 'code', + weight: 0, + documentation: + '/userguide/form-building/layout-components#html-element', + showPreview: false, + schema: HTMLComponent.schema(), + }; + } - get content() { - if (this.builderMode) { - return this.component.content; + static savedValueTypes() { + return []; } - // i18n returns error exactly with word 'select', spaces will be trimmed - if (this.component.content.replace(/(<(\/?[^>]+)>)/g, '').trim() === 'select') { - return ` ${this.component.content} `; + get defaultSchema() { + return HTMLComponent.schema(); } - const submission = _.get(this.root, 'submission', {}); - const content = this.component.content ? this.interpolate(this.component.content, { - metadata: submission.metadata || {}, - submission: submission, - data: this.rootValue, - row: this.data - }) : ''; - return this.sanitize(content, this.shouldSanitizeValue); - } + get content() { + if (this.builderMode) { + return this.component.content; + } - get singleTags() { - return ['br', 'img', 'hr']; - } + // i18n returns error exactly with word 'select', spaces will be trimmed + if ( + this.component.content.replace(/(<(\/?[^>]+)>)/g, '').trim() === + 'select' + ) { + return ` ${this.component.content} `; + } - checkRefreshOn(changed) { - super.checkRefreshOn(changed); - if (!this.builderMode && this.component.refreshOnChange && this.element && - !_.isUndefined(changed) && ((_.isBoolean(changed) && changed) || !_.isEmpty(changed)) && - this.conditionallyVisible(this.data, this.row)) { - this.setContent(this.element, this.renderContent()); + const submission = _.get(this.root, 'submission', {}); + const content = this.component.content + ? this.interpolate(this.component.content, { + metadata: submission.metadata || {}, + submission: submission, + data: this.rootValue, + row: this.data, + }) + : ''; + return this.sanitize(content, this.shouldSanitizeValue); } - } - renderContent() { - const submission = _.get(this.root, 'submission', {}); - return this.renderTemplate('html', { - component: this.component, - tag: this.component.tag, - attrs: (this.component.attrs || []).map((attr) => { - return { - attr: attr.attr, - value: this.interpolate(attr.value, { - metadata: submission.metadata || {}, - submission: submission, - data: this.rootValue, - row: this.data - }) - }; - }), - content: this.content, - singleTags: this.singleTags, - }); - } + get singleTags() { + return ['br', 'img', 'hr']; + } + + checkRefreshOn(changed) { + super.checkRefreshOn(changed); + if ( + !this.builderMode && + this.component.refreshOnChange && + this.element && + !_.isUndefined(changed) && + ((_.isBoolean(changed) && changed) || !_.isEmpty(changed)) && + this.conditionallyVisible(this.data, this.row) + ) { + this.setContent(this.element, this.renderContent()); + } + } + + renderContent() { + const submission = _.get(this.root, 'submission', {}); + return this.renderTemplate('html', { + component: this.component, + tag: this.component.tag, + attrs: (this.component.attrs || []).map((attr) => { + return { + attr: attr.attr, + value: this.interpolate(attr.value, { + metadata: submission.metadata || {}, + submission: submission, + data: this.rootValue, + row: this.data, + }), + }; + }), + content: this.content, + singleTags: this.singleTags, + }); + } - render() { - return super.render(this.renderContent()); - } + render() { + return super.render(this.renderContent()); + } - get dataReady() { - return this.root?.submissionReady || Promise.resolve(); - } + get dataReady() { + return this.root?.submissionReady || Promise.resolve(); + } - attach(element) { - this.loadRefs(element, { html: 'single' }); - this.dataReady.then(() => { - if (this.element) { - this.setContent(this.elemet, this.content); - } - }); - return super.attach(element); - } + attach(element) { + this.loadRefs(element, { html: 'single' }); + this.dataReady.then(() => { + if (this.element) { + this.setContent(this.elemet, this.content); + } + }); + return super.attach(element); + } } diff --git a/src/components/html/HTML.unit.js b/src/components/html/HTML.unit.js index 176102c982..8c21b463d4 100644 --- a/src/components/html/HTML.unit.js +++ b/src/components/html/HTML.unit.js @@ -3,31 +3,28 @@ import HTMLComponent from './HTML'; import sinon from 'sinon'; import assert from 'power-assert'; -import { - comp1, - comp2 -} from './fixtures'; +import { comp1, comp2 } from './fixtures'; -describe('HTML Component', function() { - it('Should build an html component', function() { - return Harness.testCreate(HTMLComponent, comp1); - }); - - it('Should build an html component and ignore empty attribute name', function() { - const comp = comp1; - comp.attrs.push({ - 'attr': '', - 'value': '' +describe('HTML Component', function () { + it('Should build an html component', function () { + return Harness.testCreate(HTMLComponent, comp1); }); - return Harness.testCreate(HTMLComponent, comp1); - }); + it('Should build an html component and ignore empty attribute name', function () { + const comp = comp1; + comp.attrs.push({ + attr: '', + value: '', + }); + + return Harness.testCreate(HTMLComponent, comp1); + }); - it('setContent should not be called if it is not conditionally visible', function() { - return Harness.testCreate(HTMLComponent, comp2).then((component) => { - const emit = sinon.spy(component, 'setContent'); - component.checkRefreshOn(null); - assert.equal(emit.callCount, 0); + it('setContent should not be called if it is not conditionally visible', function () { + return Harness.testCreate(HTMLComponent, comp2).then((component) => { + const emit = sinon.spy(component, 'setContent'); + component.checkRefreshOn(null); + assert.equal(emit.callCount, 0); + }); }); - }); }); diff --git a/src/components/html/editForm/HTML.edit.display.js b/src/components/html/editForm/HTML.edit.display.js index 3e02c68270..dc3efd14ec 100644 --- a/src/components/html/editForm/HTML.edit.display.js +++ b/src/components/html/editForm/HTML.edit.display.js @@ -1,98 +1,99 @@ export default [ - { - key: 'labelPosition', - ignore: true - }, - { - key: 'placeholder', - ignore: true - }, - { - key: 'description', - ignore: true - }, - { - key: 'tooltip', - ignore: true - }, - { - key: 'hideLabel', - ignore: true - }, - { - key: 'autofocus', - ignore: true - }, - { - key: 'disabled', - ignore: true - }, - { - key: 'tabindex', - ignore: true - }, - { - key: 'tableView', - ignore: true - }, - { - type: 'textfield', - input: true, - key: 'tag', - weight: 50, - label: 'HTML Tag', - placeholder: 'HTML Element Tag', - tooltip: 'The tag of this HTML element.' - }, - { - type: 'textfield', - input: true, - key: 'className', - weight: 60, - label: 'CSS Class', - placeholder: 'CSS Class', - tooltip: 'The CSS class for this HTML element.' - }, - { - type: 'datagrid', - input: true, - label: 'Attributes', - key: 'attrs', - tooltip: 'The attributes for this HTML element. Only safe attributes are allowed, such as src, href, and title.', - weight: 70, - components: [ - { - label: 'Attribute', - key: 'attr', + { + key: 'labelPosition', + ignore: true, + }, + { + key: 'placeholder', + ignore: true, + }, + { + key: 'description', + ignore: true, + }, + { + key: 'tooltip', + ignore: true, + }, + { + key: 'hideLabel', + ignore: true, + }, + { + key: 'autofocus', + ignore: true, + }, + { + key: 'disabled', + ignore: true, + }, + { + key: 'tabindex', + ignore: true, + }, + { + key: 'tableView', + ignore: true, + }, + { + type: 'textfield', input: true, - type: 'textfield' - }, - { - label: 'Value', - key: 'value', + key: 'tag', + weight: 50, + label: 'HTML Tag', + placeholder: 'HTML Element Tag', + tooltip: 'The tag of this HTML element.', + }, + { + type: 'textfield', input: true, - type: 'textfield' - } - ] - }, - { - type: 'textarea', - input: true, - editor: 'ace', - rows: 10, - as: 'html', - label: 'Content', - tooltip: 'The content of this HTML element.', - defaultValue: '
    Content
    ', - key: 'content', - weight: 80 - }, - { - weight: 85, - type: 'checkbox', - label: 'Refresh On Change', - tooltip: 'Rerender the field whenever a value on the form changes.', - key: 'refreshOnChange', - input: true - }, + key: 'className', + weight: 60, + label: 'CSS Class', + placeholder: 'CSS Class', + tooltip: 'The CSS class for this HTML element.', + }, + { + type: 'datagrid', + input: true, + label: 'Attributes', + key: 'attrs', + tooltip: + 'The attributes for this HTML element. Only safe attributes are allowed, such as src, href, and title.', + weight: 70, + components: [ + { + label: 'Attribute', + key: 'attr', + input: true, + type: 'textfield', + }, + { + label: 'Value', + key: 'value', + input: true, + type: 'textfield', + }, + ], + }, + { + type: 'textarea', + input: true, + editor: 'ace', + rows: 10, + as: 'html', + label: 'Content', + tooltip: 'The content of this HTML element.', + defaultValue: '
    Content
    ', + key: 'content', + weight: 80, + }, + { + weight: 85, + type: 'checkbox', + label: 'Refresh On Change', + tooltip: 'Rerender the field whenever a value on the form changes.', + key: 'refreshOnChange', + input: true, + }, ]; diff --git a/src/components/html/editForm/HTML.edit.logic.js b/src/components/html/editForm/HTML.edit.logic.js index d7b3535ec6..3249aa9f87 100644 --- a/src/components/html/editForm/HTML.edit.logic.js +++ b/src/components/html/editForm/HTML.edit.logic.js @@ -1,98 +1,105 @@ export default [ - { - key: 'logic', - components: [ - { - key: 'actions', + { + key: 'logic', components: [ - { - key: 'actionPanel', - components: [ - { - data: { - json: [ + { + key: 'actions', + components: [ { - label: 'Hidden', - value: 'hidden', - type: 'boolean', + key: 'actionPanel', + components: [ + { + data: { + json: [ + { + label: 'Hidden', + value: 'hidden', + type: 'boolean', + }, + { + label: 'Required', + value: 'validate.required', + type: 'boolean', + }, + { + label: 'Disabled', + value: 'disabled', + type: 'boolean', + }, + { + label: 'Label', + value: 'label', + type: 'string', + }, + { + label: 'Title', + value: 'title', + type: 'string', + }, + { + label: 'Tooltip', + value: 'tooltip', + type: 'string', + }, + { + label: 'Description', + value: 'description', + type: 'string', + }, + { + label: 'Placeholder', + value: 'placeholder', + type: 'string', + }, + { + label: 'CSS Class', + value: 'className', + type: 'string', + }, + { + label: 'Container Custom Class', + value: 'customClass', + type: 'string', + }, + { + label: 'Content', + value: 'content', + type: 'string', + component: 'content', + }, + ], + }, + key: 'property', + }, + { + type: 'textarea', + editor: 'ace', + rows: 10, + as: 'html', + label: 'Content', + tooltip: 'The content of this HTML element.', + defaultValue: '
    Content
    ', + key: 'content', + weight: 30, + input: true, + customConditional(context) { + return ( + context.row.type === 'property' && + Object.prototype.hasOwnProperty.call( + context.row, + 'property', + ) && + context.row.property.type === + 'string' && + context.row.property.component === + 'content' + ); + }, + }, + ], }, - { - label: 'Required', - value: 'validate.required', - type: 'boolean', - }, - { - label: 'Disabled', - value: 'disabled', - type: 'boolean', - }, - { - label: 'Label', - value: 'label', - type: 'string', - }, - { - label: 'Title', - value: 'title', - type: 'string', - }, - { - label: 'Tooltip', - value: 'tooltip', - type: 'string', - }, - { - label: 'Description', - value: 'description', - type: 'string', - }, - { - label: 'Placeholder', - value: 'placeholder', - type: 'string', - }, - { - label: 'CSS Class', - value: 'className', - type: 'string', - }, - { - label: 'Container Custom Class', - value: 'customClass', - type: 'string', - }, - { - label: 'Content', - value: 'content', - type: 'string', - component: 'content', - }, - ], - }, - key: 'property', - }, - { - type: 'textarea', - editor: 'ace', - rows: 10, - as: 'html', - label: 'Content', - tooltip: 'The content of this HTML element.', - defaultValue: '
    Content
    ', - key: 'content', - weight: 30, - input: true, - customConditional(context) { - return context.row.type === 'property' && - Object.prototype.hasOwnProperty.call(context.row, 'property') && - context.row.property.type === 'string' && - context.row.property.component === 'content'; - }, - }, - ], - }, + ], + }, ], - }, - ], - }, + }, ]; diff --git a/src/components/html/fixtures/comp1.js b/src/components/html/fixtures/comp1.js index ebc33259a4..3f430bfa21 100644 --- a/src/components/html/fixtures/comp1.js +++ b/src/components/html/fixtures/comp1.js @@ -1,22 +1,20 @@ export default { - 'key': 'html1', - 'input': false, - 'tag': 'img', - 'attrs': [ - { - 'value': 'https://www.w3.org/html/logo/downloads/HTML5_Logo_512.png', - 'attr': 'src' - } - ], - 'className': 'thumbnail', - 'content': '', - 'type': 'htmlelement', - 'tags': [ - - ], - 'conditional': { - 'show': '', - 'when': null, - 'eq': '' - } + key: 'html1', + input: false, + tag: 'img', + attrs: [ + { + value: 'https://www.w3.org/html/logo/downloads/HTML5_Logo_512.png', + attr: 'src', + }, + ], + className: 'thumbnail', + content: '', + type: 'htmlelement', + tags: [], + conditional: { + show: '', + when: null, + eq: '', + }, }; diff --git a/src/components/html/fixtures/comp2.js b/src/components/html/fixtures/comp2.js index e2e248e4d0..d76569ef13 100644 --- a/src/components/html/fixtures/comp2.js +++ b/src/components/html/fixtures/comp2.js @@ -1,24 +1,22 @@ export default { - 'key': 'html1', - 'input': false, - 'tag': 'img', - 'attrs': [ - { - 'value': 'https://www.w3.org/html/logo/downloads/HTML5_Logo_512.png', - 'attr': 'src' - } - ], - 'className': 'thumbnail', - 'content': '', - 'type': 'htmlelement', - 'tags': [ - - ], - 'customConditional':'show = false;', - 'conditional': { - 'show': '', - 'when': '', - 'eq': '' - }, - 'refreshOnChange': true + key: 'html1', + input: false, + tag: 'img', + attrs: [ + { + value: 'https://www.w3.org/html/logo/downloads/HTML5_Logo_512.png', + attr: 'src', + }, + ], + className: 'thumbnail', + content: '', + type: 'htmlelement', + tags: [], + customConditional: 'show = false;', + conditional: { + show: '', + when: '', + eq: '', + }, + refreshOnChange: true, }; diff --git a/src/components/number/Number.form.js b/src/components/number/Number.form.js index 0819732999..6e61c026af 100644 --- a/src/components/number/Number.form.js +++ b/src/components/number/Number.form.js @@ -4,19 +4,22 @@ import NumberEditDisplay from './editForm/Number.edit.display'; import NumberEditData from './editForm/Number.edit.data'; import NumberEditValidation from './editForm/Number.edit.validation'; -export default function(...extend) { - return textEditForm([ - { - key: 'display', - components: NumberEditDisplay - }, - { - key: 'data', - components: NumberEditData - }, - { - key: 'validation', - components: NumberEditValidation - }, - ], ...extend); +export default function (...extend) { + return textEditForm( + [ + { + key: 'display', + components: NumberEditDisplay, + }, + { + key: 'data', + components: NumberEditData, + }, + { + key: 'validation', + components: NumberEditValidation, + }, + ], + ...extend, + ); } diff --git a/src/components/number/Number.js b/src/components/number/Number.js index 97f17b1edc..3aaf71ecab 100644 --- a/src/components/number/Number.js +++ b/src/components/number/Number.js @@ -251,13 +251,20 @@ export default class NumberComponent extends Input { value && !value.includes(this.decimalSeparator) ) { - return `${value}${this.decimalSeparator}${_.repeat('0', this.decimalLimit)}`; + return `${value}${this.decimalSeparator}${_.repeat( + '0', + this.decimalLimit, + )}`; } else if ( this.component.requireDecimal && value && value.includes(this.decimalSeparator) ) { - return `${value}${_.repeat('0', this.decimalLimit - value.split(this.decimalSeparator)[1].length)}`; + return `${value}${_.repeat( + '0', + this.decimalLimit - + value.split(this.decimalSeparator)[1].length, + )}`; } return value; diff --git a/src/components/number/Number.unit.js b/src/components/number/Number.unit.js index d88e4e3a96..2d5f22371f 100644 --- a/src/components/number/Number.unit.js +++ b/src/components/number/Number.unit.js @@ -5,497 +5,684 @@ import Harness from '../../../test/harness'; import { Formio } from './../../Formio'; import NumberComponent from './Number'; -import { - comp1, - comp2, - comp3, - comp4, - comp5, - comp6, - comp7, -} from './fixtures'; - -describe('Number Component', function() { - it('Should build an number component', function() { - return Harness.testCreate(NumberComponent, comp1).then((component) => { - Harness.testElements(component, 'input[type="text"]', 1); - }); - }); - - it('Should format submissions for table view for French locale', function() { - return Harness.testCreate(NumberComponent, comp4, { language: 'fr' }).then((component) => { - const value1 = component.getValueAsString(1); - const value2 = component.getValueAsString(1.1); - const value3 = component.getValueAsString(1.11); - const value4 = component.getValueAsString(1111); - const value5 = component.getValueAsString(1111111); - const value6 = component.getValueAsString(-11111); - - assert.equal(value1, '1,00'); - assert.equal(value2, '1,10'); - assert.equal(value3, '1,11'); - assert.equal(value4, '1 111,00'); - assert.equal(value5, '1 111 111,00'); - assert.equal(value6, '-11 111,00'); - }); - }); - - it('Should format sumbissions for table view for USA locale', function() { - return Harness.testCreate(NumberComponent, comp4, { language: 'en-US' }).then((component) => { - const value1 = component.getValueAsString(1); - const value2 = component.getValueAsString(1.1); - const value3 = component.getValueAsString(1.11); - const value4 = component.getValueAsString(1111); - const value5 = component.getValueAsString(1111111); - const value6 = component.getValueAsString(-11111); - - assert.equal(value1, '1.00'); - assert.equal(value2, '1.10'); - assert.equal(value3, '1.11'); - assert.equal(value4, '1,111.00'); - assert.equal(value5, '1,111,111.00'); - assert.equal(value6, '-11,111.00'); - }); - }); - - it('Should format value on blur for USA locale', function() { - return Harness.testCreate(NumberComponent, comp4, { language: 'en-US' }).then((component) => { - component.root = { - onChange: ()=>{}, - triggerChange: ()=>{}, - }; - - const blurEvent = new Event('blur'); - const inputEvent = new Event('input'); - const valueElement = component.element.querySelector('[name="data[number]"]'); - - valueElement.value = 22222222; - valueElement.dispatchEvent(inputEvent); - valueElement.dispatchEvent(blurEvent); - assert.equal(valueElement.value, '22,222,222.00'); - - valueElement.value = 22222222.2; - valueElement.dispatchEvent(inputEvent); - valueElement.dispatchEvent(blurEvent); - assert.equal(valueElement.value, '22,222,222.20'); - - valueElement.value = 22222; - valueElement.dispatchEvent(inputEvent); - valueElement.dispatchEvent(blurEvent); - assert.equal(valueElement.value, '22,222.00'); - - valueElement.value = 2; - valueElement.dispatchEvent(inputEvent); - valueElement.dispatchEvent(blurEvent); - assert.equal(valueElement.value, '2.00'); - }); - }); - - it('Should format value on blur for French locale', function(done) { - Harness.testCreate(NumberComponent, comp4, { language: 'fr' }).then((component) => { - component.root = { - onChange: ()=>{}, - triggerChange: ()=>{}, - }; - - const blurEvent = new Event('blur'); - const inputEvent = new Event('input'); - const valueElement = component.element.querySelector('[name="data[number]"]'); - - valueElement.value = 22222222; - valueElement.dispatchEvent(inputEvent); - valueElement.dispatchEvent(blurEvent); - assert.deepEqual(valueElement.value, '22 222 222,00'); - - valueElement.value = '22222222,2'; - valueElement.dispatchEvent(inputEvent); - valueElement.dispatchEvent(blurEvent); - assert.deepEqual(valueElement.value, '22 222 222,20'); - - valueElement.value = 22222; - valueElement.dispatchEvent(inputEvent); - valueElement.dispatchEvent(blurEvent); - assert.deepEqual(valueElement.value, '22 222,00'); - - valueElement.value = 222; - valueElement.dispatchEvent(inputEvent); - valueElement.dispatchEvent(blurEvent); - assert.deepEqual(valueElement.value, '222,00'); - - valueElement.value = 2; - valueElement.dispatchEvent(inputEvent); - valueElement.dispatchEvent(blurEvent); - assert.deepEqual(valueElement.value, '2,00'); - - done(); - }); - }); +import { comp1, comp2, comp3, comp4, comp5, comp6, comp7 } from './fixtures'; - it('Should not change entered value on blur if multiple value is set', function(done) { - Harness.testCreate(NumberComponent, comp5).then((component) => { - component.root = { - onChange: ()=>{}, - triggerChange: ()=>{}, - }; - const blurEvent = new Event('blur'); - const clickEvent = new Event('click'); - const addBtn = component.refs.addButton[0]; +describe('Number Component', function () { + it('Should build an number component', function () { + return Harness.testCreate(NumberComponent, comp1).then((component) => { + Harness.testElements(component, 'input[type="text"]', 1); + }); + }); - addBtn.dispatchEvent(clickEvent); + it('Should format submissions for table view for French locale', function () { + return Harness.testCreate(NumberComponent, comp4, { + language: 'fr', + }).then((component) => { + const value1 = component.getValueAsString(1); + const value2 = component.getValueAsString(1.1); + const value3 = component.getValueAsString(1.11); + const value4 = component.getValueAsString(1111); + const value5 = component.getValueAsString(1111111); + const value6 = component.getValueAsString(-11111); + + assert.equal(value1, '1,00'); + assert.equal(value2, '1,10'); + assert.equal(value3, '1,11'); + assert.equal(value4, '1 111,00'); + assert.equal(value5, '1 111 111,00'); + assert.equal(value6, '-11 111,00'); + }); + }); - const firstValueElement = component.element.querySelectorAll('[name="data[number]"]')[0]; - const secondValueElement = component.element.querySelectorAll('[name="data[number]"]')[1]; + it('Should format sumbissions for table view for USA locale', function () { + return Harness.testCreate(NumberComponent, comp4, { + language: 'en-US', + }).then((component) => { + const value1 = component.getValueAsString(1); + const value2 = component.getValueAsString(1.1); + const value3 = component.getValueAsString(1.11); + const value4 = component.getValueAsString(1111); + const value5 = component.getValueAsString(1111111); + const value6 = component.getValueAsString(-11111); + + assert.equal(value1, '1.00'); + assert.equal(value2, '1.10'); + assert.equal(value3, '1.11'); + assert.equal(value4, '1,111.00'); + assert.equal(value5, '1,111,111.00'); + assert.equal(value6, '-11,111.00'); + }); + }); - component.setValue([111,222]); + it('Should format value on blur for USA locale', function () { + return Harness.testCreate(NumberComponent, comp4, { + language: 'en-US', + }).then((component) => { + component.root = { + onChange: () => {}, + triggerChange: () => {}, + }; + + const blurEvent = new Event('blur'); + const inputEvent = new Event('input'); + const valueElement = component.element.querySelector( + '[name="data[number]"]', + ); + + valueElement.value = 22222222; + valueElement.dispatchEvent(inputEvent); + valueElement.dispatchEvent(blurEvent); + assert.equal(valueElement.value, '22,222,222.00'); + + valueElement.value = 22222222.2; + valueElement.dispatchEvent(inputEvent); + valueElement.dispatchEvent(blurEvent); + assert.equal(valueElement.value, '22,222,222.20'); + + valueElement.value = 22222; + valueElement.dispatchEvent(inputEvent); + valueElement.dispatchEvent(blurEvent); + assert.equal(valueElement.value, '22,222.00'); + + valueElement.value = 2; + valueElement.dispatchEvent(inputEvent); + valueElement.dispatchEvent(blurEvent); + assert.equal(valueElement.value, '2.00'); + }); + }); - firstValueElement.dispatchEvent(blurEvent); - secondValueElement.dispatchEvent(blurEvent); + it('Should format value on blur for French locale', function (done) { + Harness.testCreate(NumberComponent, comp4, { language: 'fr' }).then( + (component) => { + component.root = { + onChange: () => {}, + triggerChange: () => {}, + }; + + const blurEvent = new Event('blur'); + const inputEvent = new Event('input'); + const valueElement = component.element.querySelector( + '[name="data[number]"]', + ); + + valueElement.value = 22222222; + valueElement.dispatchEvent(inputEvent); + valueElement.dispatchEvent(blurEvent); + assert.deepEqual(valueElement.value, '22 222 222,00'); + + valueElement.value = '22222222,2'; + valueElement.dispatchEvent(inputEvent); + valueElement.dispatchEvent(blurEvent); + assert.deepEqual(valueElement.value, '22 222 222,20'); + + valueElement.value = 22222; + valueElement.dispatchEvent(inputEvent); + valueElement.dispatchEvent(blurEvent); + assert.deepEqual(valueElement.value, '22 222,00'); + + valueElement.value = 222; + valueElement.dispatchEvent(inputEvent); + valueElement.dispatchEvent(blurEvent); + assert.deepEqual(valueElement.value, '222,00'); + + valueElement.value = 2; + valueElement.dispatchEvent(inputEvent); + valueElement.dispatchEvent(blurEvent); + assert.deepEqual(valueElement.value, '2,00'); + + done(); + }, + ); + }); - assert.equal(component.dataValue[0], component.getValue()[0]); - assert.equal(component.dataValue[1], component.getValue()[1]); - done(); + it('Should not change entered value on blur if multiple value is set', function (done) { + Harness.testCreate(NumberComponent, comp5).then((component) => { + component.root = { + onChange: () => {}, + triggerChange: () => {}, + }; + const blurEvent = new Event('blur'); + const clickEvent = new Event('click'); + const addBtn = component.refs.addButton[0]; + + addBtn.dispatchEvent(clickEvent); + + const firstValueElement = component.element.querySelectorAll( + '[name="data[number]"]', + )[0]; + const secondValueElement = component.element.querySelectorAll( + '[name="data[number]"]', + )[1]; + + component.setValue([111, 222]); + + firstValueElement.dispatchEvent(blurEvent); + secondValueElement.dispatchEvent(blurEvent); + + assert.equal(component.dataValue[0], component.getValue()[0]); + assert.equal(component.dataValue[1], component.getValue()[1]); + done(); + }); }); - }); - - it('Should limit decimals using step', function() { - return Harness.testCreate(NumberComponent, _merge({}, comp2, { - validate: { - step: '0.001' - } - })).then((component) => { - Harness.testSetInput(component, 123456789.123456789, 123456789.123, '123,456,789.123'); - Harness.testSetInput(component, -123456789.123456789, -123456789.123, '-123,456,789.123'); - Harness.testSetInput(component, '123456789.123456789', 123456789.123, '123,456,789.123'); - Harness.testSetInput(component, '-123456789.123456789', -123456789.123, '-123,456,789.123'); + + it('Should limit decimals using step', function () { + return Harness.testCreate( + NumberComponent, + _merge({}, comp2, { + validate: { + step: '0.001', + }, + }), + ).then((component) => { + Harness.testSetInput( + component, + 123456789.123456789, + 123456789.123, + '123,456,789.123', + ); + Harness.testSetInput( + component, + -123456789.123456789, + -123456789.123, + '-123,456,789.123', + ); + Harness.testSetInput( + component, + '123456789.123456789', + 123456789.123, + '123,456,789.123', + ); + Harness.testSetInput( + component, + '-123456789.123456789', + -123456789.123, + '-123,456,789.123', + ); + }); }); - }); - - it('Should format sumissions for table view for USA locale', function() { - return Harness.testCreate(NumberComponent, comp2, { language: 'en-US' }).then((component) => { - const value1 = component.getValueAsString(1); - const value2 = component.getValueAsString(1.1); - const value3 = component.getValueAsString(1.1111111); - const value4 = component.getValueAsString(1111); - const value5 = component.getValueAsString(1111111); - const value6 = component.getValueAsString(-11111.1111); - - assert.equal(value1, '1'); - assert.equal(value2, '1.1'); - assert.equal(value3, '1.1111111'); - assert.equal(value4, '1,111'); - assert.equal(value5, '1,111,111'); - assert.equal(value6, '-11,111.1111'); + + it('Should format sumissions for table view for USA locale', function () { + return Harness.testCreate(NumberComponent, comp2, { + language: 'en-US', + }).then((component) => { + const value1 = component.getValueAsString(1); + const value2 = component.getValueAsString(1.1); + const value3 = component.getValueAsString(1.1111111); + const value4 = component.getValueAsString(1111); + const value5 = component.getValueAsString(1111111); + const value6 = component.getValueAsString(-11111.1111); + + assert.equal(value1, '1'); + assert.equal(value2, '1.1'); + assert.equal(value3, '1.1111111'); + assert.equal(value4, '1,111'); + assert.equal(value5, '1,111,111'); + assert.equal(value6, '-11,111.1111'); + }); }); - }); - - it('Should format numbers for USA locale', function() { - /* eslint-disable max-statements */ - return Harness.testCreate(NumberComponent, comp2, { language: 'en-US' }).then((component) => { - Harness.testSetInput(component, null, null, ''); - Harness.testSetInput(component, undefined, null, ''); - Harness.testSetInput(component, '', null, ''); - Harness.testSetInput(component, {}, null, ''); - Harness.testSetInput(component, [], null, ''); - Harness.testSetInput(component, [''], null, ''); - Harness.testSetInput(component, ['1'], 1, '1'); - Harness.testSetInput(component, 0, 0, '0'); - Harness.testSetInput(component, 1, 1, '1'); - Harness.testSetInput(component, -1, -1, '-1'); - Harness.testSetInput(component, 1000, 1000, '1,000'); - Harness.testSetInput(component, -1000, -1000, '-1,000'); - Harness.testSetInput(component, 1000.00, 1000, '1,000'); - Harness.testSetInput(component, -1000.00, -1000, '-1,000'); - Harness.testSetInput(component, 1000.01, 1000.01, '1,000.01'); - Harness.testSetInput(component, -1000.01, -1000.01, '-1,000.01'); - Harness.testSetInput(component, 1000.001, 1000.001, '1,000.001'); - Harness.testSetInput(component, -1000.001, -1000.001, '-1,000.001'); - Harness.testSetInput(component, 1234567890.12, 1234567890.12, '1,234,567,890.12'); - Harness.testSetInput(component, -1234567890.12, -1234567890.12, '-1,234,567,890.12'); - Harness.testSetInput(component, 12.123456789, 12.123456789, '12.123456789'); - Harness.testSetInput(component, -12.123456789, -12.123456789, '-12.123456789'); - // These tests run into the maximum number of significant digits for floats. - Harness.testSetInput(component, 123456789.123456789, 123456789.123456789, '123,456,789.12345679'); - Harness.testSetInput(component, -123456789.123456789, -123456789.123456789, '-123,456,789.12345679'); - Harness.testSetInput(component, '0', 0, '0'); - Harness.testSetInput(component, '1', 1, '1'); - Harness.testSetInput(component, '-1', -1, '-1'); - Harness.testSetInput(component, '1000', 1000, '1,000'); - Harness.testSetInput(component, '-1000', -1000, '-1,000'); - Harness.testSetInput(component, '1000.01', 1000.01, '1,000.01'); - Harness.testSetInput(component, '-1000.01', -1000.01, '-1,000.01'); - Harness.testSetInput(component, '1000.00', 1000, '1,000'); - Harness.testSetInput(component, '-1000.00', -1000, '-1,000'); - Harness.testSetInput(component, '1000.001', 1000.001, '1,000.001'); - Harness.testSetInput(component, '-1000.001', -1000.001, '-1,000.001'); - Harness.testSetInput(component, '1234567890.12', 1234567890.12, '1,234,567,890.12'); - Harness.testSetInput(component, '-1234567890.12', -1234567890.12, '-1,234,567,890.12'); - Harness.testSetInput(component, '12.123456789', 12.123456789, '12.123456789'); - Harness.testSetInput(component, '-12.123456789', -12.123456789, '-12.123456789'); - Harness.testSetInput(component, '123456789.123456789', 123456789.123456789, '123,456,789.12345679'); - Harness.testSetInput(component, '-123456789.123456789', -123456789.123456789, '-123,456,789.12345679'); + + it('Should format numbers for USA locale', function () { + /* eslint-disable max-statements */ + return Harness.testCreate(NumberComponent, comp2, { + language: 'en-US', + }).then((component) => { + Harness.testSetInput(component, null, null, ''); + Harness.testSetInput(component, undefined, null, ''); + Harness.testSetInput(component, '', null, ''); + Harness.testSetInput(component, {}, null, ''); + Harness.testSetInput(component, [], null, ''); + Harness.testSetInput(component, [''], null, ''); + Harness.testSetInput(component, ['1'], 1, '1'); + Harness.testSetInput(component, 0, 0, '0'); + Harness.testSetInput(component, 1, 1, '1'); + Harness.testSetInput(component, -1, -1, '-1'); + Harness.testSetInput(component, 1000, 1000, '1,000'); + Harness.testSetInput(component, -1000, -1000, '-1,000'); + Harness.testSetInput(component, 1000.0, 1000, '1,000'); + Harness.testSetInput(component, -1000.0, -1000, '-1,000'); + Harness.testSetInput(component, 1000.01, 1000.01, '1,000.01'); + Harness.testSetInput(component, -1000.01, -1000.01, '-1,000.01'); + Harness.testSetInput(component, 1000.001, 1000.001, '1,000.001'); + Harness.testSetInput(component, -1000.001, -1000.001, '-1,000.001'); + Harness.testSetInput( + component, + 1234567890.12, + 1234567890.12, + '1,234,567,890.12', + ); + Harness.testSetInput( + component, + -1234567890.12, + -1234567890.12, + '-1,234,567,890.12', + ); + Harness.testSetInput( + component, + 12.123456789, + 12.123456789, + '12.123456789', + ); + Harness.testSetInput( + component, + -12.123456789, + -12.123456789, + '-12.123456789', + ); + // These tests run into the maximum number of significant digits for floats. + Harness.testSetInput( + component, + 123456789.123456789, + 123456789.123456789, + '123,456,789.12345679', + ); + Harness.testSetInput( + component, + -123456789.123456789, + -123456789.123456789, + '-123,456,789.12345679', + ); + Harness.testSetInput(component, '0', 0, '0'); + Harness.testSetInput(component, '1', 1, '1'); + Harness.testSetInput(component, '-1', -1, '-1'); + Harness.testSetInput(component, '1000', 1000, '1,000'); + Harness.testSetInput(component, '-1000', -1000, '-1,000'); + Harness.testSetInput(component, '1000.01', 1000.01, '1,000.01'); + Harness.testSetInput(component, '-1000.01', -1000.01, '-1,000.01'); + Harness.testSetInput(component, '1000.00', 1000, '1,000'); + Harness.testSetInput(component, '-1000.00', -1000, '-1,000'); + Harness.testSetInput(component, '1000.001', 1000.001, '1,000.001'); + Harness.testSetInput( + component, + '-1000.001', + -1000.001, + '-1,000.001', + ); + Harness.testSetInput( + component, + '1234567890.12', + 1234567890.12, + '1,234,567,890.12', + ); + Harness.testSetInput( + component, + '-1234567890.12', + -1234567890.12, + '-1,234,567,890.12', + ); + Harness.testSetInput( + component, + '12.123456789', + 12.123456789, + '12.123456789', + ); + Harness.testSetInput( + component, + '-12.123456789', + -12.123456789, + '-12.123456789', + ); + Harness.testSetInput( + component, + '123456789.123456789', + 123456789.123456789, + '123,456,789.12345679', + ); + Harness.testSetInput( + component, + '-123456789.123456789', + -123456789.123456789, + '-123,456,789.12345679', + ); + }); + /* eslint-enable max-statements */ }); - /* eslint-enable max-statements */ - }); - - it('Should format numbers for British locale', function() { - return Harness.testCreate(NumberComponent, comp2, { language: 'en-GB' }).then((component) => { - Harness.testSetInput(component, null, null, ''); - Harness.testSetInput(component, 0, 0, '0'); - Harness.testSetInput(component, 1, 1, '1'); - Harness.testSetInput(component, -1, -1, '-1'); - Harness.testSetInput(component, 1000, 1000, '1,000'); - Harness.testSetInput(component, -1000, -1000, '-1,000'); - Harness.testSetInput(component, 1000.00, 1000, '1,000'); - Harness.testSetInput(component, -1000.00, -1000, '-1,000'); - Harness.testSetInput(component, 1000.01, 1000.01, '1,000.01'); - Harness.testSetInput(component, -1000.01, -1000.01, '-1,000.01'); - Harness.testSetInput(component, 1000.001, 1000.001, '1,000.001'); - Harness.testSetInput(component, -1000.001, -1000.001, '-1,000.001'); - Harness.testSetInput(component, 1234567890.12, 1234567890.12, '1,234,567,890.12'); - Harness.testSetInput(component, -1234567890.12, -1234567890.12, '-1,234,567,890.12'); - Harness.testSetInput(component, 12.123456789, 12.123456789, '12.123456789'); - Harness.testSetInput(component, -12.123456789, -12.123456789, '-12.123456789'); + + it('Should format numbers for British locale', function () { + return Harness.testCreate(NumberComponent, comp2, { + language: 'en-GB', + }).then((component) => { + Harness.testSetInput(component, null, null, ''); + Harness.testSetInput(component, 0, 0, '0'); + Harness.testSetInput(component, 1, 1, '1'); + Harness.testSetInput(component, -1, -1, '-1'); + Harness.testSetInput(component, 1000, 1000, '1,000'); + Harness.testSetInput(component, -1000, -1000, '-1,000'); + Harness.testSetInput(component, 1000.0, 1000, '1,000'); + Harness.testSetInput(component, -1000.0, -1000, '-1,000'); + Harness.testSetInput(component, 1000.01, 1000.01, '1,000.01'); + Harness.testSetInput(component, -1000.01, -1000.01, '-1,000.01'); + Harness.testSetInput(component, 1000.001, 1000.001, '1,000.001'); + Harness.testSetInput(component, -1000.001, -1000.001, '-1,000.001'); + Harness.testSetInput( + component, + 1234567890.12, + 1234567890.12, + '1,234,567,890.12', + ); + Harness.testSetInput( + component, + -1234567890.12, + -1234567890.12, + '-1,234,567,890.12', + ); + Harness.testSetInput( + component, + 12.123456789, + 12.123456789, + '12.123456789', + ); + Harness.testSetInput( + component, + -12.123456789, + -12.123456789, + '-12.123456789', + ); + }); }); - }); - - it('Should format numbers for French locale', function() { - return Harness.testCreate(NumberComponent, comp2, { language: 'fr' }).then((component) => { - // The spaces in these tests are a weird unicode space so be careful duplicating the tests. - Harness.testSetInput(component, null, null, ''); - Harness.testSetInput(component, 0, 0, '0'); - Harness.testSetInput(component, 1, 1, '1'); - Harness.testSetInput(component, -1, -1, '-1'); - Harness.testSetInput(component, 1000, 1000, '1 000'); - Harness.testSetInput(component, -1000, -1000, '-1 000'); - Harness.testSetInput(component, 1000.00, 1000, '1 000'); - Harness.testSetInput(component, -1000.00, -1000, '-1 000'); - Harness.testSetInput(component, 1000.01, 1000.01, '1 000,01'); - Harness.testSetInput(component, -1000.01, -1000.01, '-1 000,01'); - Harness.testSetInput(component, 1000.001, 1000.001, '1 000,001'); - Harness.testSetInput(component, -1000.001, -1000.001, '-1 000,001'); - Harness.testSetInput(component, 1234567890.12, 1234567890.12, '1 234 567 890,12'); - Harness.testSetInput(component, -1234567890.12, -1234567890.12, '-1 234 567 890,12'); - Harness.testSetInput(component, 12.123456789, 12.123456789, '12,123456789'); - Harness.testSetInput(component, -12.123456789, -12.123456789, '-12,123456789'); + + it('Should format numbers for French locale', function () { + return Harness.testCreate(NumberComponent, comp2, { + language: 'fr', + }).then((component) => { + // The spaces in these tests are a weird unicode space so be careful duplicating the tests. + Harness.testSetInput(component, null, null, ''); + Harness.testSetInput(component, 0, 0, '0'); + Harness.testSetInput(component, 1, 1, '1'); + Harness.testSetInput(component, -1, -1, '-1'); + Harness.testSetInput(component, 1000, 1000, '1 000'); + Harness.testSetInput(component, -1000, -1000, '-1 000'); + Harness.testSetInput(component, 1000.0, 1000, '1 000'); + Harness.testSetInput(component, -1000.0, -1000, '-1 000'); + Harness.testSetInput(component, 1000.01, 1000.01, '1 000,01'); + Harness.testSetInput(component, -1000.01, -1000.01, '-1 000,01'); + Harness.testSetInput(component, 1000.001, 1000.001, '1 000,001'); + Harness.testSetInput(component, -1000.001, -1000.001, '-1 000,001'); + Harness.testSetInput( + component, + 1234567890.12, + 1234567890.12, + '1 234 567 890,12', + ); + Harness.testSetInput( + component, + -1234567890.12, + -1234567890.12, + '-1 234 567 890,12', + ); + Harness.testSetInput( + component, + 12.123456789, + 12.123456789, + '12,123456789', + ); + Harness.testSetInput( + component, + -12.123456789, + -12.123456789, + '-12,123456789', + ); + }); }); - }); - - it('Should format numbers for German locale', function() { - return Harness.testCreate(NumberComponent, comp2, { language: 'de' }).then((component) => { - Harness.testSetInput(component, null, null, ''); - Harness.testSetInput(component, 0, 0, '0'); - Harness.testSetInput(component, 1, 1, '1'); - Harness.testSetInput(component, -1, -1, '-1'); - Harness.testSetInput(component, 1000, 1000, '1.000'); - Harness.testSetInput(component, -1000, -1000, '-1.000'); - Harness.testSetInput(component, 1000.00, 1000, '1.000'); - Harness.testSetInput(component, -1000.00, -1000, '-1.000'); - Harness.testSetInput(component, 1000.01, 1000.01, '1.000,01'); - Harness.testSetInput(component, -1000.01, -1000.01, '-1.000,01'); - Harness.testSetInput(component, 1000.001, 1000.001, '1.000,001'); - Harness.testSetInput(component, -1000.001, -1000.001, '-1.000,001'); - Harness.testSetInput(component, 1234567890.12, 1234567890.12, '1.234.567.890,12'); - Harness.testSetInput(component, -1234567890.12, -1234567890.12, '-1.234.567.890,12'); - Harness.testSetInput(component, 12.123456789, 12.123456789, '12,123456789'); - Harness.testSetInput(component, -12.123456789, -12.123456789, '-12,123456789'); + + it('Should format numbers for German locale', function () { + return Harness.testCreate(NumberComponent, comp2, { + language: 'de', + }).then((component) => { + Harness.testSetInput(component, null, null, ''); + Harness.testSetInput(component, 0, 0, '0'); + Harness.testSetInput(component, 1, 1, '1'); + Harness.testSetInput(component, -1, -1, '-1'); + Harness.testSetInput(component, 1000, 1000, '1.000'); + Harness.testSetInput(component, -1000, -1000, '-1.000'); + Harness.testSetInput(component, 1000.0, 1000, '1.000'); + Harness.testSetInput(component, -1000.0, -1000, '-1.000'); + Harness.testSetInput(component, 1000.01, 1000.01, '1.000,01'); + Harness.testSetInput(component, -1000.01, -1000.01, '-1.000,01'); + Harness.testSetInput(component, 1000.001, 1000.001, '1.000,001'); + Harness.testSetInput(component, -1000.001, -1000.001, '-1.000,001'); + Harness.testSetInput( + component, + 1234567890.12, + 1234567890.12, + '1.234.567.890,12', + ); + Harness.testSetInput( + component, + -1234567890.12, + -1234567890.12, + '-1.234.567.890,12', + ); + Harness.testSetInput( + component, + 12.123456789, + 12.123456789, + '12,123456789', + ); + Harness.testSetInput( + component, + -12.123456789, + -12.123456789, + '-12,123456789', + ); + }); }); - }); - it('Should display default integer value', function() { - return Harness.testCreate(NumberComponent, comp3).then(number => { - assert.deepEqual(_.get(number, ['refs', 'input', '0', 'value']), '42'); + it('Should display default integer value', function () { + return Harness.testCreate(NumberComponent, comp3).then((number) => { + assert.deepEqual( + _.get(number, ['refs', 'input', '0', 'value']), + '42', + ); + }); }); - }); - it('Should display default decimal value', function() { - const TEST_VAL = 4.2; - const comp = _.cloneDeep(comp3); + it('Should display default decimal value', function () { + const TEST_VAL = 4.2; + const comp = _.cloneDeep(comp3); - comp.defaultValue = TEST_VAL; - comp.decimalLimit = 2; - comp.requireDecimal = true; + comp.defaultValue = TEST_VAL; + comp.decimalLimit = 2; + comp.requireDecimal = true; - return Harness.testCreate(NumberComponent, comp).then(number => { - assert.deepEqual(_.get(number, ['refs', 'input', '0', 'value']), '4.20'); + return Harness.testCreate(NumberComponent, comp).then((number) => { + assert.deepEqual( + _.get(number, ['refs', 'input', '0', 'value']), + '4.20', + ); + }); }); - }); - - it('Should provide min/max validation', function(done) { - const form = _.cloneDeep(comp6); - - const validValues = [ - null, - 20, - 555, - 34, - 20.000001, - 554.999 - ]; - - const invalidMin = [ - 19.99, - 0, - 1, - 0.34, - -0.1, - -20 - ]; - - const invalidMax = [ - 555.00000001, - 100000, - 5555, - ]; - - const testValidity = (values, valid, message, lastValue) => { - _.each(values, (value) => { - const element = document.createElement('div'); - - Formio.createForm(element, form, { language: 'en-US' }).then(form => { - form.setPristine(false); - - const component = form.getComponent('number'); - const changed = component.setValue(value); - const error = message; - - if (value) { - assert.equal(changed, true, 'Should set value'); - } - - setTimeout(() => { - if (valid) { - assert.equal(!!component.error, false, 'Should not contain error'); - } - else { - assert.equal(!!component.error, true, 'Should contain error'); - assert.equal(component.error.message, error, 'Should contain error message'); - assert.equal(component.element.classList.contains('has-error'), true, 'Should contain error class'); - assert.equal(component.refs.messageContainer.textContent.trim(), error, 'Should show error'); - } - - if (_.isEqual(value, lastValue)) { - done(); - } - }, 300); - }).catch(done); - }); - }; - - testValidity(validValues, true); - testValidity(invalidMin, false, 'Number cannot be less than 20.'); - testValidity(invalidMax, false, 'Number cannot be greater than 555.', invalidMax[invalidMax.length-1]); - }); - - it('Should be able to switch between multiple and single values', function(done) { - Harness.testCreate(NumberComponent, comp5).then((component) => { - assert.equal(_.isEqual(component.defaultValue, [null]), true); - component.component.multiple = false; - component.redraw().then(() => { - assert.equal(component.defaultValue, null); - done(); - }); + + it('Should provide min/max validation', function (done) { + const form = _.cloneDeep(comp6); + + const validValues = [null, 20, 555, 34, 20.000001, 554.999]; + + const invalidMin = [19.99, 0, 1, 0.34, -0.1, -20]; + + const invalidMax = [555.00000001, 100000, 5555]; + + const testValidity = (values, valid, message, lastValue) => { + _.each(values, (value) => { + const element = document.createElement('div'); + + Formio.createForm(element, form, { language: 'en-US' }) + .then((form) => { + form.setPristine(false); + + const component = form.getComponent('number'); + const changed = component.setValue(value); + const error = message; + + if (value) { + assert.equal(changed, true, 'Should set value'); + } + + setTimeout(() => { + if (valid) { + assert.equal( + !!component.error, + false, + 'Should not contain error', + ); + } else { + assert.equal( + !!component.error, + true, + 'Should contain error', + ); + assert.equal( + component.error.message, + error, + 'Should contain error message', + ); + assert.equal( + component.element.classList.contains( + 'has-error', + ), + true, + 'Should contain error class', + ); + assert.equal( + component.refs.messageContainer.textContent.trim(), + error, + 'Should show error', + ); + } + + if (_.isEqual(value, lastValue)) { + done(); + } + }, 300); + }) + .catch(done); + }); + }; + + testValidity(validValues, true); + testValidity(invalidMin, false, 'Number cannot be less than 20.'); + testValidity( + invalidMax, + false, + 'Number cannot be greater than 555.', + invalidMax[invalidMax.length - 1], + ); }); - }); - - it('Should return value as string properly for multiple values', function(done) { - Harness.testCreate(NumberComponent, comp7).then((component) => { - component.refs.input = null; - assert.equal(component.getValueAsString([1, 2, 3, 4, 5]), '1, 2, 3, 4, 5'); - done(); - }).catch(done); - }); - - // it('Should add trailing zeros on blur, if decimal required', (done) => { - // const comp = _.cloneDeep(comp3); - // - // comp.decimalLimit = 2; - // comp.requireDecimal = true; - // - // Harness.testCreate(NumberComponent, comp).then(number => { - // const testset = [ - // // [inv, outv, display] - // ['42', 42, '42.00'], - // ['42.1', 42.1, '42.10'], - // ['42.01', 42.01, '42.01'], - // ['4200', 4200, '4200.00'], - // ['4200.4', 4200.4, '4200.40'], - // ['4200.42', 4200.42, '4200.42'], - // ['4200.', 4200, '4200.00'], - // ['99999999.', 99999999, '99999999.00'] - // ]; - // - // testset.forEach((set, index) => { - // try { - // Harness.testNumberBlur(number, ...set); - // } - // catch (err) { - // done(new Error(`Test case #${index}, set: ${set}, err: ${err.message}`)); - // } - // }); - // - // done(); - // }, done); - // }); - // - // it('Should add trailing zeros on blur, if decimal and delimiter is required', (done) => { - // const comp = _.cloneDeep(comp3); - // - // comp.decimalLimit = 2; - // comp.requireDecimal = true; - // comp.delimiter = true; - // - // /* eslint-disable max-statements */ - // Harness.testCreate(NumberComponent, comp).then(number => { - // const testset = [ - // // [inv, outv, display] - // ['42', 42, '42.00'], - // ['42.1', 42.1, '42.10'], - // ['42.01', 42.01, '42.01'], - // ['4200', 4200, '4,200.00'], - // ['4200.4', 4200.4, '4,200.40'], - // ['4200.42', 4200.42, '4,200.42'], - // ['4200.', 4200, '4,200.00'], - // ['99999999.', 99999999, '99,999,999.00'] - // ]; - // - // testset.forEach((set, index) => { - // try { - // Harness.testNumberBlur(number, ...set); - // } - // catch (err) { - // done(new Error(`Test case #${index}, set: ${set}, err: ${err.message}`)); - // } - // }); - // - // done(); - // }, done); - // }); - // - // it('Should add trailing zeros on blur with `multiple` flag', (done) => { - // Harness.testCreate(NumberComponent, comp4).then(number => { - // const testset = [ - // ['42', 42, '42.00'], - // ['42.1', 42.1, '42.10'], - // ['42.01', 42.01, '42.01'], - // ['4200', 4200, '4,200.00'], - // ['4200.4', 4200.4, '4,200.40'], - // ['4200.42', 4200.42, '4,200.42'], - // ['4200.', 4200, '4,200.00'], - // ['99999999.', 99999999, '99,999,999.00'] - // ]; - // - // testset.forEach((set, index) => { - // try { - // assert.strictEqual(number.inputs.length, index + 1); - // Harness.testNumberBlur(number, ...set, index); - // number.addValue(); - // } - // catch (err) { - // done(new Error(`Test case #${index}, set: ${set}, err: ${err.message}`)); - // } - // }); - // - // done(); - // }, done); - // }); + + it('Should be able to switch between multiple and single values', function (done) { + Harness.testCreate(NumberComponent, comp5).then((component) => { + assert.equal(_.isEqual(component.defaultValue, [null]), true); + component.component.multiple = false; + component.redraw().then(() => { + assert.equal(component.defaultValue, null); + done(); + }); + }); + }); + + it('Should return value as string properly for multiple values', function (done) { + Harness.testCreate(NumberComponent, comp7) + .then((component) => { + component.refs.input = null; + assert.equal( + component.getValueAsString([1, 2, 3, 4, 5]), + '1, 2, 3, 4, 5', + ); + done(); + }) + .catch(done); + }); + + // it('Should add trailing zeros on blur, if decimal required', (done) => { + // const comp = _.cloneDeep(comp3); + // + // comp.decimalLimit = 2; + // comp.requireDecimal = true; + // + // Harness.testCreate(NumberComponent, comp).then(number => { + // const testset = [ + // // [inv, outv, display] + // ['42', 42, '42.00'], + // ['42.1', 42.1, '42.10'], + // ['42.01', 42.01, '42.01'], + // ['4200', 4200, '4200.00'], + // ['4200.4', 4200.4, '4200.40'], + // ['4200.42', 4200.42, '4200.42'], + // ['4200.', 4200, '4200.00'], + // ['99999999.', 99999999, '99999999.00'] + // ]; + // + // testset.forEach((set, index) => { + // try { + // Harness.testNumberBlur(number, ...set); + // } + // catch (err) { + // done(new Error(`Test case #${index}, set: ${set}, err: ${err.message}`)); + // } + // }); + // + // done(); + // }, done); + // }); + // + // it('Should add trailing zeros on blur, if decimal and delimiter is required', (done) => { + // const comp = _.cloneDeep(comp3); + // + // comp.decimalLimit = 2; + // comp.requireDecimal = true; + // comp.delimiter = true; + // + // /* eslint-disable max-statements */ + // Harness.testCreate(NumberComponent, comp).then(number => { + // const testset = [ + // // [inv, outv, display] + // ['42', 42, '42.00'], + // ['42.1', 42.1, '42.10'], + // ['42.01', 42.01, '42.01'], + // ['4200', 4200, '4,200.00'], + // ['4200.4', 4200.4, '4,200.40'], + // ['4200.42', 4200.42, '4,200.42'], + // ['4200.', 4200, '4,200.00'], + // ['99999999.', 99999999, '99,999,999.00'] + // ]; + // + // testset.forEach((set, index) => { + // try { + // Harness.testNumberBlur(number, ...set); + // } + // catch (err) { + // done(new Error(`Test case #${index}, set: ${set}, err: ${err.message}`)); + // } + // }); + // + // done(); + // }, done); + // }); + // + // it('Should add trailing zeros on blur with `multiple` flag', (done) => { + // Harness.testCreate(NumberComponent, comp4).then(number => { + // const testset = [ + // ['42', 42, '42.00'], + // ['42.1', 42.1, '42.10'], + // ['42.01', 42.01, '42.01'], + // ['4200', 4200, '4,200.00'], + // ['4200.4', 4200.4, '4,200.40'], + // ['4200.42', 4200.42, '4,200.42'], + // ['4200.', 4200, '4,200.00'], + // ['99999999.', 99999999, '99,999,999.00'] + // ]; + // + // testset.forEach((set, index) => { + // try { + // assert.strictEqual(number.inputs.length, index + 1); + // Harness.testNumberBlur(number, ...set, index); + // number.addValue(); + // } + // catch (err) { + // done(new Error(`Test case #${index}, set: ${set}, err: ${err.message}`)); + // } + // }); + // + // done(); + // }, done); + // }); }); diff --git a/src/components/number/editForm/Number.edit.data.js b/src/components/number/editForm/Number.edit.data.js index 1c937e491b..db632be1f5 100644 --- a/src/components/number/editForm/Number.edit.data.js +++ b/src/components/number/editForm/Number.edit.data.js @@ -1,30 +1,30 @@ export default [ - { - type: 'checkbox', - input: true, - weight: 70, - key: 'delimiter', - label: 'Use Thousands Separator', - tooltip: 'Separate thousands by local delimiter.' - }, - { - type: 'number', - input: true, - weight: 80, - key: 'decimalLimit', - label: 'Decimal Places', - tooltip: 'The maximum number of decimal places.' - }, - { - type: 'checkbox', - input: true, - weight: 90, - key: 'requireDecimal', - label: 'Require Decimal', - tooltip: 'Always show decimals, even if trailing zeros.' - }, - { - key: 'case', - ignore: true, - }, + { + type: 'checkbox', + input: true, + weight: 70, + key: 'delimiter', + label: 'Use Thousands Separator', + tooltip: 'Separate thousands by local delimiter.', + }, + { + type: 'number', + input: true, + weight: 80, + key: 'decimalLimit', + label: 'Decimal Places', + tooltip: 'The maximum number of decimal places.', + }, + { + type: 'checkbox', + input: true, + weight: 90, + key: 'requireDecimal', + label: 'Require Decimal', + tooltip: 'Always show decimals, even if trailing zeros.', + }, + { + key: 'case', + ignore: true, + }, ]; diff --git a/src/components/number/editForm/Number.edit.display.js b/src/components/number/editForm/Number.edit.display.js index 06689713fe..0fc97ddb2b 100644 --- a/src/components/number/editForm/Number.edit.display.js +++ b/src/components/number/editForm/Number.edit.display.js @@ -1,22 +1,22 @@ export default [ - { - key: 'spellcheck', - ignore: true - }, - { - key: 'inputMask', - ignore: true - }, - { - key: 'allowMultipleMasks', - ignore: true - }, - { - key: 'showWordCount', - ignore: true, - }, - { - key: 'showCharCount', - ignore: true, - } + { + key: 'spellcheck', + ignore: true, + }, + { + key: 'inputMask', + ignore: true, + }, + { + key: 'allowMultipleMasks', + ignore: true, + }, + { + key: 'showWordCount', + ignore: true, + }, + { + key: 'showCharCount', + ignore: true, + }, ]; diff --git a/src/components/number/editForm/Number.edit.validation.js b/src/components/number/editForm/Number.edit.validation.js index 458e4b795d..ba8219fc98 100644 --- a/src/components/number/editForm/Number.edit.validation.js +++ b/src/components/number/editForm/Number.edit.validation.js @@ -1,44 +1,46 @@ export default [ - { - key: 'unique', - ignore: true - }, - { - key: 'validate.minLength', - ignore: true - }, - { - key: 'validate.maxLength', - ignore: true - }, - { - key: 'validate.minWords', - ignore: true - }, - { - key: 'validate.maxWords', - ignore: true - }, - { - key: 'validate.pattern', - ignore: true - }, - { - type: 'number', - label: 'Minimum Value', - key: 'validate.min', - input: true, - placeholder: 'Minimum Value', - tooltip: 'The minimum value this field must have before the form can be submitted.', - weight: 150 - }, - { - type: 'number', - label: 'Maximum Value', - key: 'validate.max', - input: true, - placeholder: 'Maximum Value', - tooltip: 'The maximum value this field can have before the form can be submitted.', - weight: 160 - } + { + key: 'unique', + ignore: true, + }, + { + key: 'validate.minLength', + ignore: true, + }, + { + key: 'validate.maxLength', + ignore: true, + }, + { + key: 'validate.minWords', + ignore: true, + }, + { + key: 'validate.maxWords', + ignore: true, + }, + { + key: 'validate.pattern', + ignore: true, + }, + { + type: 'number', + label: 'Minimum Value', + key: 'validate.min', + input: true, + placeholder: 'Minimum Value', + tooltip: + 'The minimum value this field must have before the form can be submitted.', + weight: 150, + }, + { + type: 'number', + label: 'Maximum Value', + key: 'validate.max', + input: true, + placeholder: 'Maximum Value', + tooltip: + 'The maximum value this field can have before the form can be submitted.', + weight: 160, + }, ]; diff --git a/src/components/number/fixtures/comp1.js b/src/components/number/fixtures/comp1.js index b8e3930699..ac08994f2d 100644 --- a/src/components/number/fixtures/comp1.js +++ b/src/components/number/fixtures/comp1.js @@ -1,32 +1,30 @@ export default { - 'multiple': true, - 'conditional': { - 'eq': '', - 'when': null, - 'show': '' - }, - 'tags': [ - - ], - 'type': 'number', - 'validate': { - 'custom': '', - 'multiple': '', - 'integer': '', - 'step': 'any', - 'max': '', - 'min': '', - 'required': false - }, - 'persistent': true, - 'protected': false, - 'defaultValue': '', - 'suffix': 'USD', - 'prefix': '$', - 'placeholder': 'Enter some money!', - 'key': 'money', - 'label': 'Money', - 'inputType': 'number', - 'tableView': true, - 'input': true + multiple: true, + conditional: { + eq: '', + when: null, + show: '', + }, + tags: [], + type: 'number', + validate: { + custom: '', + multiple: '', + integer: '', + step: 'any', + max: '', + min: '', + required: false, + }, + persistent: true, + protected: false, + defaultValue: '', + suffix: 'USD', + prefix: '$', + placeholder: 'Enter some money!', + key: 'money', + label: 'Money', + inputType: 'number', + tableView: true, + input: true, }; diff --git a/src/components/number/fixtures/comp2.js b/src/components/number/fixtures/comp2.js index 28e4e20952..979a865bef 100644 --- a/src/components/number/fixtures/comp2.js +++ b/src/components/number/fixtures/comp2.js @@ -1,31 +1,29 @@ export default { - 'multiple': false, - 'conditional': { - 'eq': '', - 'when': null, - 'show': '' - }, - 'tags': [ - - ], - 'type': 'number', - 'validate': { - 'custom': '', - 'multiple': '', - 'integer': '', - 'step': 'any', - 'max': '', - 'min': '', - 'required': false - }, - 'persistent': true, - 'protected': false, - 'defaultValue': '', - 'placeholder': 'Enter a number', - 'key': 'number', - 'label': 'Number', - 'inputType': 'number', - 'tableView': true, - 'delimiter': true, - 'input': true + multiple: false, + conditional: { + eq: '', + when: null, + show: '', + }, + tags: [], + type: 'number', + validate: { + custom: '', + multiple: '', + integer: '', + step: 'any', + max: '', + min: '', + required: false, + }, + persistent: true, + protected: false, + defaultValue: '', + placeholder: 'Enter a number', + key: 'number', + label: 'Number', + inputType: 'number', + tableView: true, + delimiter: true, + input: true, }; diff --git a/src/components/number/fixtures/comp3.js b/src/components/number/fixtures/comp3.js index 14931a4a91..8ff8e0e36c 100644 --- a/src/components/number/fixtures/comp3.js +++ b/src/components/number/fixtures/comp3.js @@ -1,13 +1,13 @@ export default { - label: 'Number', - mask: false, - tableView: true, - alwaysEnabled: false, - type: 'number', - input: true, - key: 'number', - delimiter: false, - requireDecimal: false, - encrypted: false, - defaultValue: 42 + label: 'Number', + mask: false, + tableView: true, + alwaysEnabled: false, + type: 'number', + input: true, + key: 'number', + delimiter: false, + requireDecimal: false, + encrypted: false, + defaultValue: 42, }; diff --git a/src/components/number/fixtures/comp4.js b/src/components/number/fixtures/comp4.js index efdf080f5d..9daa307453 100644 --- a/src/components/number/fixtures/comp4.js +++ b/src/components/number/fixtures/comp4.js @@ -1,13 +1,13 @@ export default { - 'label': 'Number', - 'mask': false, - 'spellcheck': true, - 'tableView': true, - 'delimiter': true, - 'requireDecimal': true, - 'inputFormat': 'plain', - 'key': 'number', - 'type': 'number', - 'decimalLimit': 2, - 'input': true + label: 'Number', + mask: false, + spellcheck: true, + tableView: true, + delimiter: true, + requireDecimal: true, + inputFormat: 'plain', + key: 'number', + type: 'number', + decimalLimit: 2, + input: true, }; diff --git a/src/components/number/fixtures/comp5.js b/src/components/number/fixtures/comp5.js index 77857918f7..e0aa4c2299 100644 --- a/src/components/number/fixtures/comp5.js +++ b/src/components/number/fixtures/comp5.js @@ -1,18 +1,18 @@ export default { - 'label': 'Number', - 'mask': false, - 'spellcheck': true, - 'tableView': false, - 'multiple': true, - 'delimiter': false, - 'requireDecimal': false, - 'inputFormat': 'plain', - 'calculateServer': false, - 'validate': { - 'multiple': true - }, - 'key': 'number', - 'type': 'number', - 'input': true, - 'defaultValue': [null] + label: 'Number', + mask: false, + spellcheck: true, + tableView: false, + multiple: true, + delimiter: false, + requireDecimal: false, + inputFormat: 'plain', + calculateServer: false, + validate: { + multiple: true, + }, + key: 'number', + type: 'number', + input: true, + defaultValue: [null], }; diff --git a/src/components/number/fixtures/comp6.js b/src/components/number/fixtures/comp6.js index 1329193fbc..f86df50ad6 100644 --- a/src/components/number/fixtures/comp6.js +++ b/src/components/number/fixtures/comp6.js @@ -1,25 +1,32 @@ export default { - type: 'form', - components: [ - { - label: 'Number', - mask: false, - spellcheck: true, - tableView: false, - delimiter: false, - requireDecimal: false, - inputFormat: 'plain', - validate: { min: 20, max: 555 }, - key: 'number', - type: 'number', - input: true - }, - { label: 'Submit', showValidations: false, tableView: false, key: 'submit', type: 'button', input: true } - ], - revisions: '', - _vid: 0, - title: 'number tests', - display: 'form', - name: 'numberTests', - path: 'numbertests', + type: 'form', + components: [ + { + label: 'Number', + mask: false, + spellcheck: true, + tableView: false, + delimiter: false, + requireDecimal: false, + inputFormat: 'plain', + validate: { min: 20, max: 555 }, + key: 'number', + type: 'number', + input: true, + }, + { + label: 'Submit', + showValidations: false, + tableView: false, + key: 'submit', + type: 'button', + input: true, + }, + ], + revisions: '', + _vid: 0, + title: 'number tests', + display: 'form', + name: 'numberTests', + path: 'numbertests', }; diff --git a/src/components/number/fixtures/comp7.js b/src/components/number/fixtures/comp7.js index b4a027e188..b7dc9eae62 100644 --- a/src/components/number/fixtures/comp7.js +++ b/src/components/number/fixtures/comp7.js @@ -1,14 +1,14 @@ export default { - label: 'Number', - mask: false, - tableView: false, - modalEdit: true, - multiple: true, - delimiter: false, - requireDecimal: false, - inputFormat: 'plain', - truncateMultipleSpaces: false, - key: 'number', - type: 'number', - input: true + label: 'Number', + mask: false, + tableView: false, + modalEdit: true, + multiple: true, + delimiter: false, + requireDecimal: false, + inputFormat: 'plain', + truncateMultipleSpaces: false, + key: 'number', + type: 'number', + input: true, }; diff --git a/src/components/number/fixtures/values.js b/src/components/number/fixtures/values.js index 05aefb23d0..47e691458e 100644 --- a/src/components/number/fixtures/values.js +++ b/src/components/number/fixtures/values.js @@ -1,9 +1 @@ -export default [ - 1, - -1, - 0, - 1.000, - 10000, - 10000.0001, - 1234567890, -]; +export default [1, -1, 0, 1.0, 10000, 10000.0001, 1234567890]; diff --git a/src/components/panel/Panel.form.js b/src/components/panel/Panel.form.js index 3335332268..16e035ff43 100644 --- a/src/components/panel/Panel.form.js +++ b/src/components/panel/Panel.form.js @@ -3,15 +3,18 @@ import nestedComponentForm from '../_classes/nested/NestedComponent.form'; import PanelEditDisplay from './editForm/Panel.edit.display'; import PanelEditConditional from './editForm/Panel.edit.conditional'; -export default function(...extend) { - return nestedComponentForm([ - { - key: 'display', - components: PanelEditDisplay - }, - { - key: 'conditional', - components: PanelEditConditional, - }, - ], ...extend); +export default function (...extend) { + return nestedComponentForm( + [ + { + key: 'display', + components: PanelEditDisplay, + }, + { + key: 'conditional', + components: PanelEditConditional, + }, + ], + ...extend, + ); } diff --git a/src/components/panel/Panel.js b/src/components/panel/Panel.js index b6a3aeb32b..9fc1138e88 100644 --- a/src/components/panel/Panel.js +++ b/src/components/panel/Panel.js @@ -3,60 +3,63 @@ import { hasInvalidComponent } from '../../utils/utils'; import FormComponent from '../form/Form'; export default class PanelComponent extends NestedComponent { - static schema(...extend) { - return NestedComponent.schema({ - label: 'Panel', - type: 'panel', - key: 'panel', - title: 'Panel', - theme: 'default', - breadcrumb: 'default', - components: [], - clearOnHide: false, - input: false, - tableView: false, - persistent: false - }, ...extend); - } + static schema(...extend) { + return NestedComponent.schema( + { + label: 'Panel', + type: 'panel', + key: 'panel', + title: 'Panel', + theme: 'default', + breadcrumb: 'default', + components: [], + clearOnHide: false, + input: false, + tableView: false, + persistent: false, + }, + ...extend, + ); + } - static get builderInfo() { - return { - title: 'Panel', - icon: 'list-alt', - group: 'layout', - documentation: '/userguide/form-building/layout-components#panel', - weight: 30, - schema: PanelComponent.schema() - }; - } + static get builderInfo() { + return { + title: 'Panel', + icon: 'list-alt', + group: 'layout', + documentation: '/userguide/form-building/layout-components#panel', + weight: 30, + schema: PanelComponent.schema(), + }; + } - get defaultSchema() { - return PanelComponent.schema(); - } + get defaultSchema() { + return PanelComponent.schema(); + } - get templateName() { - return 'panel'; - } + get templateName() { + return 'panel'; + } - static savedValueTypes() { - return []; - } + static savedValueTypes() { + return []; + } - constructor(...args) { - super(...args); - this.noField = true; - this.on('componentError', () => { - //change collapsed value only when the panel is collapsed to avoid additional redrawing that prevents validation messages - if (hasInvalidComponent(this) && this.collapsed) { - this.collapsed = false; - } - }); - } + constructor(...args) { + super(...args); + this.noField = true; + this.on('componentError', () => { + //change collapsed value only when the panel is collapsed to avoid additional redrawing that prevents validation messages + if (hasInvalidComponent(this) && this.collapsed) { + this.collapsed = false; + } + }); + } - getComponent(path, fn, originalPath) { - if (this.root?.parent instanceof FormComponent) { - path = path.replace(this._parentPath, ''); + getComponent(path, fn, originalPath) { + if (this.root?.parent instanceof FormComponent) { + path = path.replace(this._parentPath, ''); + } + return super.getComponent(path, fn, originalPath); } - return super.getComponent(path, fn, originalPath); - } } diff --git a/src/components/panel/Panel.unit.js b/src/components/panel/Panel.unit.js index 6d4f261f7b..f601043a7d 100644 --- a/src/components/panel/Panel.unit.js +++ b/src/components/panel/Panel.unit.js @@ -4,70 +4,79 @@ import { flattenComponents } from '../../utils/formUtils'; import PanelComponent from './Panel'; import panelEditForm from './Panel.form'; import { Formio } from '../../Formio'; -import { - comp1, - comp2 -} from './fixtures'; +import { comp1, comp2 } from './fixtures'; -describe('Panel Component', function() { - it('Should build a panel component', function() { - return Harness.testCreate(PanelComponent, comp1).then((component) => { - Harness.testElements(component, 'input[type="text"]', 2); +describe('Panel Component', function () { + it('Should build a panel component', function () { + return Harness.testCreate(PanelComponent, comp1).then((component) => { + Harness.testElements(component, 'input[type="text"]', 2); + }); }); - }); - it('Should keep validation errors after expanding collapsed panel', function(done) { - const element = document.createElement('div'); + it('Should keep validation errors after expanding collapsed panel', function (done) { + const element = document.createElement('div'); - Formio.createForm(element, comp2).then(form => { - const panel = form.getComponent('panel'); - const numberComp = form.getComponent('number'); - const textComp = form.getComponent('textField'); + Formio.createForm(element, comp2) + .then((form) => { + const panel = form.getComponent('panel'); + const numberComp = form.getComponent('number'); + const textComp = form.getComponent('textField'); - assert.equal(panel.collapsed, false); - assert.equal(!!numberComp.error, false); - assert.equal(!!textComp.error, false); + assert.equal(panel.collapsed, false); + assert.equal(!!numberComp.error, false); + assert.equal(!!textComp.error, false); - const numberInput = numberComp.refs?.input[0]; - numberInput.value = 5; - const inputEvent = new Event('input'); - numberInput.dispatchEvent(inputEvent); + const numberInput = numberComp.refs?.input[0]; + numberInput.value = 5; + const inputEvent = new Event('input'); + numberInput.dispatchEvent(inputEvent); - setTimeout(() => { - assert.equal(!!numberComp.error, true); - assert.equal(numberComp.error.messages.length, 1); - assert.equal(numberComp.refs.messageContainer.querySelectorAll('.error').length, 1); - assert.equal(!!textComp.error, false); + setTimeout(() => { + assert.equal(!!numberComp.error, true); + assert.equal(numberComp.error.messages.length, 1); + assert.equal( + numberComp.refs.messageContainer.querySelectorAll( + '.error', + ).length, + 1, + ); + assert.equal(!!textComp.error, false); - const clickEvent = new Event('click'); - panel.refs.header.dispatchEvent(clickEvent); + const clickEvent = new Event('click'); + panel.refs.header.dispatchEvent(clickEvent); - setTimeout(() => { - assert.equal(panel.collapsed, true); - panel.refs.header.dispatchEvent(clickEvent); + setTimeout(() => { + assert.equal(panel.collapsed, true); + panel.refs.header.dispatchEvent(clickEvent); - setTimeout(() => { - assert.equal(panel.collapsed, false); - assert.equal(!!numberComp.error, true); - assert.equal(numberComp.error.messages.length, 1); - assert.equal(numberComp.refs.messageContainer.querySelectorAll('.error').length, 1); - assert.equal(!!textComp.error, false); - done(); - }, 300); - }, 300); - }, 300); - }).catch(done); - }); + setTimeout(() => { + assert.equal(panel.collapsed, false); + assert.equal(!!numberComp.error, true); + assert.equal(numberComp.error.messages.length, 1); + assert.equal( + numberComp.refs.messageContainer.querySelectorAll( + '.error', + ).length, + 1, + ); + assert.equal(!!textComp.error, false); + done(); + }, 300); + }, 300); + }, 300); + }) + .catch(done); + }); - describe('Edit Form', function() { - it('should include components for important settings', function() { - const components = flattenComponents(panelEditForm().components); - const keys = Object.keys(components).map(path => components[path].key); - const settings = [ - 'breadcrumbClickable' - ]; + describe('Edit Form', function () { + it('should include components for important settings', function () { + const components = flattenComponents(panelEditForm().components); + const keys = Object.keys(components).map( + (path) => components[path].key, + ); + const settings = ['breadcrumbClickable']; - assert(settings.every(s => keys.includes(s))); + assert(settings.every((s) => keys.includes(s))); + }); }); - }); }); diff --git a/src/components/panel/editForm/Panel.edit.conditional.js b/src/components/panel/editForm/Panel.edit.conditional.js index 14455dfbb8..bc56425887 100644 --- a/src/components/panel/editForm/Panel.edit.conditional.js +++ b/src/components/panel/editForm/Panel.edit.conditional.js @@ -7,43 +7,47 @@ import _keys from 'lodash/keys'; const title = 'Advanced Next Page'; const jsonProp = 'nextPage'; const jsProp = 'nextPage'; -const jsDocHTML = (` +const jsDocHTML = `

    You must assign the next variable with the API key of the next page.

    The global variable data is provided, and allows you to access the data of any form component, by using its API key.

    Also moment library is available, and allows you to manipulate dates in a convenient way.

    Example
    next = data.addComment ? 'page3' : 'page4';
    -`); -const jsonDocHTML = (` +`; +const jsonDocHTML = `

    Submission data is available as JsonLogic variables, with the same api key as your components.

    -`); +`; const settingComponent = EditFormUtils.javaScriptValue( - title, - jsProp, - jsonProp, - 110, - jsDocHTML, - jsonDocHTML + title, + jsProp, + jsonProp, + 110, + jsDocHTML, + jsonDocHTML, ); export default [ - { - ...settingComponent, - customConditional(context) { - let isWizardPanel = false; - if (context.instance.options.editForm.display === 'wizard') { - const { components } = context.instance.options.editForm; - const component = context.instance.options.editComponent; - if (components && component) { - isWizardPanel = components.some((comp) => { - const diff = _difference(_keys(comp), _keys(component)) || []; - diff.push('components'); - return _isEqual(_omit(comp, diff), _omit(component, diff)); - }); - } - } - return isWizardPanel; - } - } + { + ...settingComponent, + customConditional(context) { + let isWizardPanel = false; + if (context.instance.options.editForm.display === 'wizard') { + const { components } = context.instance.options.editForm; + const component = context.instance.options.editComponent; + if (components && component) { + isWizardPanel = components.some((comp) => { + const diff = + _difference(_keys(comp), _keys(component)) || []; + diff.push('components'); + return _isEqual( + _omit(comp, diff), + _omit(component, diff), + ); + }); + } + } + return isWizardPanel; + }, + }, ]; /* eslint-enable quotes, max-len */ diff --git a/src/components/panel/editForm/Panel.edit.display.js b/src/components/panel/editForm/Panel.edit.display.js index 72076c705d..0ffb7688a5 100644 --- a/src/components/panel/editForm/Panel.edit.display.js +++ b/src/components/panel/editForm/Panel.edit.display.js @@ -3,193 +3,205 @@ import _omit from 'lodash/omit'; import _difference from 'lodash/difference'; import _keys from 'lodash/keys'; export default [ - { - key: 'labelPosition', - ignore: true - }, - { - key: 'placeholder', - ignore: true - }, - { - key: 'description', - ignore: true - }, - { - key: 'autofocus', - ignore: true - }, - { - key: 'tableView', - ignore: true - }, - { - key: 'label', - hidden: true, - calculateValue(context) { - return context.data.title; - } - }, - { - key: 'tabindex', - hidden: true, - }, - { - weight: 1, - type: 'textfield', - input: true, - placeholder: 'Panel Title', - label: 'Title', - key: 'title', - tooltip: 'The title text that appears in the header of this panel.' - }, - { - weight: 20, - type: 'textarea', - input: true, - key: 'tooltip', - label: 'Tooltip', - placeholder: 'To add a tooltip to this field, enter text here.', - tooltip: 'Adds a tooltip to the side of this field.' - }, - { - weight: 30, - type: 'select', - input: true, - label: 'Theme', - key: 'theme', - dataSrc: 'values', - data: { - values: [ - { label: 'Default', value: 'default' }, - { label: 'Primary', value: 'primary' }, - { label: 'Info', value: 'info' }, - { label: 'Success', value: 'success' }, - { label: 'Danger', value: 'danger' }, - { label: 'Warning', value: 'warning' } - ] - } - }, - { - weight: 40, - type: 'fieldset', - input: false, - components: [ - { + { + key: 'labelPosition', + ignore: true, + }, + { + key: 'placeholder', + ignore: true, + }, + { + key: 'description', + ignore: true, + }, + { + key: 'autofocus', + ignore: true, + }, + { + key: 'tableView', + ignore: true, + }, + { + key: 'label', + hidden: true, + calculateValue(context) { + return context.data.title; + }, + }, + { + key: 'tabindex', + hidden: true, + }, + { + weight: 1, + type: 'textfield', input: true, - type: 'checkbox', - label: 'Allow click on Breadcrumb', - key: 'breadcrumbClickable', - defaultValue: true, - customConditional({ data = {}, buildingForm = {} }) { - const formSettings = buildingForm.settings || {}; - return ![data.breadcrumb, formSettings.wizardBreadcrumbsType].includes('none'); - } - }, - { + placeholder: 'Panel Title', + label: 'Title', + key: 'title', + tooltip: 'The title text that appears in the header of this panel.', + }, + { + weight: 20, + type: 'textarea', input: true, - type: 'checkbox', - label: 'Allow Previous', - key: 'allowPrevious', - defaultValue: false, - tooltip: 'Determines if the breadcrumb bar is clickable or not for visited tabs.', - conditional: { - json: { '===': [{ var: 'data.breadcrumbClickable' }, false] } - } - }, - { - weight: 50, - label: 'Panel Navigation Buttons', - optionsLabelPosition: 'right', - values: [ - { - label: 'Previous', - value: 'previous', - }, - { - label: 'Cancel', - value: 'cancel', - }, - { - label: 'Next', - value: 'next', - } - ], - inline: true, - type: 'selectboxes', - key: 'buttonSettings', + key: 'tooltip', + label: 'Tooltip', + placeholder: 'To add a tooltip to this field, enter text here.', + tooltip: 'Adds a tooltip to the side of this field.', + }, + { + weight: 30, + type: 'select', input: true, - inputType: 'checkbox', - defaultValue: { - previous: true, - cancel: true, - next: true + label: 'Theme', + key: 'theme', + dataSrc: 'values', + data: { + values: [ + { label: 'Default', value: 'default' }, + { label: 'Primary', value: 'primary' }, + { label: 'Info', value: 'info' }, + { label: 'Success', value: 'success' }, + { label: 'Danger', value: 'danger' }, + { label: 'Warning', value: 'warning' }, + ], }, - }, - { - weight: 55, - label: 'Navigate Wizard on Enter', - type: 'checkbox', - key: 'navigateOnEnter', - input: true, - inputType: 'checkbox', - defaultValue: false, - tooltip: 'Use the Enter key to go forward through pages.' - }, - { - weight: 56, - label: 'Save on Enter', + }, + { + weight: 40, + type: 'fieldset', + input: false, + components: [ + { + input: true, + type: 'checkbox', + label: 'Allow click on Breadcrumb', + key: 'breadcrumbClickable', + defaultValue: true, + customConditional({ data = {}, buildingForm = {} }) { + const formSettings = buildingForm.settings || {}; + return ![ + data.breadcrumb, + formSettings.wizardBreadcrumbsType, + ].includes('none'); + }, + }, + { + input: true, + type: 'checkbox', + label: 'Allow Previous', + key: 'allowPrevious', + defaultValue: false, + tooltip: + 'Determines if the breadcrumb bar is clickable or not for visited tabs.', + conditional: { + json: { + '===': [{ var: 'data.breadcrumbClickable' }, false], + }, + }, + }, + { + weight: 50, + label: 'Panel Navigation Buttons', + optionsLabelPosition: 'right', + values: [ + { + label: 'Previous', + value: 'previous', + }, + { + label: 'Cancel', + value: 'cancel', + }, + { + label: 'Next', + value: 'next', + }, + ], + inline: true, + type: 'selectboxes', + key: 'buttonSettings', + input: true, + inputType: 'checkbox', + defaultValue: { + previous: true, + cancel: true, + next: true, + }, + }, + { + weight: 55, + label: 'Navigate Wizard on Enter', + type: 'checkbox', + key: 'navigateOnEnter', + input: true, + inputType: 'checkbox', + defaultValue: false, + tooltip: 'Use the Enter key to go forward through pages.', + }, + { + weight: 56, + label: 'Save on Enter', + type: 'checkbox', + key: 'saveOnEnter', + input: true, + inputType: 'checkbox', + defaultValue: false, + tooltip: 'Use the Enter key to submit form on last page.', + }, + { + weight: 60, + label: 'Scroll up on page opening', + type: 'checkbox', + key: 'scrollToTop', + input: true, + inputType: 'checkbox', + defaultValue: false, + tooltip: + 'Scroll to the top of the wizard page when user navigates to it', + }, + ], + customConditional(context) { + let isWizardPanel = false; + if (context.instance.options.editForm.display === 'wizard') { + const { components } = context.instance.options.editForm; + const component = context.instance.options.editComponent; + if (components && component) { + isWizardPanel = components.some((comp) => { + const diff = + _difference(_keys(comp), _keys(component)) || []; + diff.push('components'); + return _isEqual( + _omit(comp, diff), + _omit(component, diff), + ); + }); + } + } + return isWizardPanel; + }, + }, + { + weight: 650, type: 'checkbox', - key: 'saveOnEnter', + label: 'Collapsible', + tooltip: + 'If checked, this will turn this Panel into a collapsible panel.', + key: 'collapsible', input: true, - inputType: 'checkbox', - defaultValue: false, - tooltip: 'Use the Enter key to submit form on last page.' - }, - { - weight: 60, - label: 'Scroll up on page opening', + }, + { + weight: 651, type: 'checkbox', - key: 'scrollToTop', + label: 'Initially Collapsed', + tooltip: 'Determines the initial collapsed state of this Panel.', + key: 'collapsed', input: true, - inputType: 'checkbox', - defaultValue: false, - tooltip: 'Scroll to the top of the wizard page when user navigates to it' - } - ], - customConditional(context) { - let isWizardPanel = false; - if (context.instance.options.editForm.display === 'wizard') { - const { components } = context.instance.options.editForm; - const component = context.instance.options.editComponent; - if (components && component) { - isWizardPanel = components.some((comp) => { - const diff = _difference(_keys(comp), _keys(component)) || []; - diff.push('components'); - return _isEqual(_omit(comp, diff), _omit(component, diff)); - }); - } - } - return isWizardPanel; - } - }, - { - weight: 650, - type: 'checkbox', - label: 'Collapsible', - tooltip: 'If checked, this will turn this Panel into a collapsible panel.', - key: 'collapsible', - input: true - }, - { - weight: 651, - type: 'checkbox', - label: 'Initially Collapsed', - tooltip: 'Determines the initial collapsed state of this Panel.', - key: 'collapsed', - input: true, - conditional: { - json: { '===': [{ var: 'data.collapsible' }, true] } - } - } + conditional: { + json: { '===': [{ var: 'data.collapsible' }, true] }, + }, + }, ]; diff --git a/src/components/panel/fixtures/comp1.js b/src/components/panel/fixtures/comp1.js index ee20ac0746..759a375435 100644 --- a/src/components/panel/fixtures/comp1.js +++ b/src/components/panel/fixtures/comp1.js @@ -1,84 +1,78 @@ export default { - 'conditional': { - 'eq': '', - 'when': null, - 'show': '' - }, - 'tags': [ - - ], - 'type': 'panel', - 'components': [ - { - 'tags': [ - - ], - 'type': 'textfield', - 'conditional': { - 'eq': '', - 'when': null, - 'show': '' - }, - 'validate': { - 'customPrivate': false, - 'custom': '', - 'pattern': '', - 'maxLength': '', - 'minLength': '', - 'required': false - }, - 'persistent': true, - 'unique': false, - 'protected': false, - 'defaultValue': '', - 'multiple': false, - 'suffix': '', - 'prefix': '', - 'placeholder': '', - 'key': 'firstName', - 'label': 'First Name', - 'inputMask': '', - 'inputType': 'text', - 'tableView': true, - 'input': true + conditional: { + eq: '', + when: null, + show: '', }, - { - 'tags': [ - - ], - 'type': 'textfield', - 'conditional': { - 'eq': '', - 'when': null, - 'show': '' - }, - 'validate': { - 'customPrivate': false, - 'custom': '', - 'pattern': '', - 'maxLength': '', - 'minLength': '', - 'required': false - }, - 'persistent': true, - 'unique': false, - 'protected': false, - 'defaultValue': '', - 'multiple': false, - 'suffix': '', - 'prefix': '', - 'placeholder': '', - 'key': 'lastName', - 'label': 'Last Name', - 'inputMask': '', - 'inputType': 'text', - 'tableView': true, - 'input': true - } - ], - 'nextPage': null, - 'theme': 'default', - 'title': 'User Information', - 'input': false, - 'key': 'panel1' + tags: [], + type: 'panel', + components: [ + { + tags: [], + type: 'textfield', + conditional: { + eq: '', + when: null, + show: '', + }, + validate: { + customPrivate: false, + custom: '', + pattern: '', + maxLength: '', + minLength: '', + required: false, + }, + persistent: true, + unique: false, + protected: false, + defaultValue: '', + multiple: false, + suffix: '', + prefix: '', + placeholder: '', + key: 'firstName', + label: 'First Name', + inputMask: '', + inputType: 'text', + tableView: true, + input: true, + }, + { + tags: [], + type: 'textfield', + conditional: { + eq: '', + when: null, + show: '', + }, + validate: { + customPrivate: false, + custom: '', + pattern: '', + maxLength: '', + minLength: '', + required: false, + }, + persistent: true, + unique: false, + protected: false, + defaultValue: '', + multiple: false, + suffix: '', + prefix: '', + placeholder: '', + key: 'lastName', + label: 'Last Name', + inputMask: '', + inputType: 'text', + tableView: true, + input: true, + }, + ], + nextPage: null, + theme: 'default', + title: 'User Information', + input: false, + key: 'panel1', }; diff --git a/src/components/panel/fixtures/comp2.js b/src/components/panel/fixtures/comp2.js index 63c99b898d..bc3a60df8f 100644 --- a/src/components/panel/fixtures/comp2.js +++ b/src/components/panel/fixtures/comp2.js @@ -1,67 +1,67 @@ export default { - title: 'test panel', - name: 'testPanel', - path: 'testpanel', - type: 'form', - display: 'form', - components: [ - { - label: 'Text Field', - tableView: true, - validate: { - minLength: 8, - }, - key: 'textField1', - type: 'textfield', - input: true, - }, - { - collapsible: true, - key: 'panel', - type: 'panel', - label: 'Panel', - collapsed: false, - input: false, - tableView: false, - components: [ + title: 'test panel', + name: 'testPanel', + path: 'testpanel', + type: 'form', + display: 'form', + components: [ { - label: 'Text Field', - tableView: true, - validate: { - required: true, - }, - key: 'textField', - type: 'textfield', - input: true, + label: 'Text Field', + tableView: true, + validate: { + minLength: 8, + }, + key: 'textField1', + type: 'textfield', + input: true, }, { - label: 'Number', - mask: false, - tableView: false, - delimiter: false, - requireDecimal: false, - inputFormat: 'plain', - truncateMultipleSpaces: false, - validate: { - min: 100, - }, - key: 'number', - type: 'number', - input: true, + collapsible: true, + key: 'panel', + type: 'panel', + label: 'Panel', + collapsed: false, + input: false, + tableView: false, + components: [ + { + label: 'Text Field', + tableView: true, + validate: { + required: true, + }, + key: 'textField', + type: 'textfield', + input: true, + }, + { + label: 'Number', + mask: false, + tableView: false, + delimiter: false, + requireDecimal: false, + inputFormat: 'plain', + truncateMultipleSpaces: false, + validate: { + min: 100, + }, + key: 'number', + type: 'number', + input: true, + }, + ], }, - ], - }, - { - label: 'Submit', - showValidations: false, - tableView: false, - key: 'submit', - type: 'button', - input: true, - saveOnEnter: false, - }, - ], - created: '2022-12-21T09:39:36.394Z', - modified: '2022-12-21T12:37:15.497Z', - machineName: 'uljpnhxgtzkilfa:testPanel', + { + label: 'Submit', + showValidations: false, + tableView: false, + key: 'submit', + type: 'button', + input: true, + saveOnEnter: false, + }, + ], + created: '2022-12-21T09:39:36.394Z', + modified: '2022-12-21T12:37:15.497Z', + machineName: 'uljpnhxgtzkilfa:testPanel', }; diff --git a/src/components/password/Password.form.js b/src/components/password/Password.form.js index 6c4e9bd123..444e551f51 100644 --- a/src/components/password/Password.form.js +++ b/src/components/password/Password.form.js @@ -4,19 +4,22 @@ import PasswordEditDisplay from './editForm/Password.edit.display'; import PasswordEditData from './editForm/Password.edit.data'; import PasswordEditValidation from './editForm/Password.edit.validation'; -export default function(...extend) { - return textEditForm([ - { - key: 'data', - components: PasswordEditData - }, - { - key: 'display', - components: PasswordEditDisplay - }, - { - key: 'validation', - components: PasswordEditValidation - }, - ], ...extend); +export default function (...extend) { + return textEditForm( + [ + { + key: 'data', + components: PasswordEditData, + }, + { + key: 'display', + components: PasswordEditDisplay, + }, + { + key: 'validation', + components: PasswordEditValidation, + }, + ], + ...extend, + ); } diff --git a/src/components/password/Password.js b/src/components/password/Password.js index 3f7fae6e2c..c4561ba293 100644 --- a/src/components/password/Password.js +++ b/src/components/password/Password.js @@ -2,38 +2,41 @@ import TextFieldComponent from '../textfield/TextField'; import _ from 'lodash'; export default class PasswordComponent extends TextFieldComponent { - static schema(...extend) { - return TextFieldComponent.schema({ - type: 'password', - label: 'Password', - key: 'password', - protected: true, - tableView: false, - }, ...extend); - } + static schema(...extend) { + return TextFieldComponent.schema( + { + type: 'password', + label: 'Password', + key: 'password', + protected: true, + tableView: false, + }, + ...extend, + ); + } - static get builderInfo() { - return { - title: 'Password', - icon: 'asterisk', - group: 'basic', - documentation: '/userguide/form-building/form-components#password', - weight: 40, - schema: PasswordComponent.schema() - }; - } + static get builderInfo() { + return { + title: 'Password', + icon: 'asterisk', + group: 'basic', + documentation: '/userguide/form-building/form-components#password', + weight: 40, + schema: PasswordComponent.schema(), + }; + } - get defaultSchema() { - return _.omit(PasswordComponent.schema(), ['protected', 'tableView']); - } + get defaultSchema() { + return _.omit(PasswordComponent.schema(), ['protected', 'tableView']); + } - get inputInfo() { - const info = super.inputInfo; - info.attr.type = 'password'; - return info; - } + get inputInfo() { + const info = super.inputInfo; + info.attr.type = 'password'; + return info; + } - get autocompleteDisableAttrName() { - return 'new-password'; - } + get autocompleteDisableAttrName() { + return 'new-password'; + } } diff --git a/src/components/password/Password.unit.js b/src/components/password/Password.unit.js index 6b826f449f..31bbd954a6 100644 --- a/src/components/password/Password.unit.js +++ b/src/components/password/Password.unit.js @@ -4,144 +4,189 @@ import { Formio } from './../../Formio'; import assert from 'power-assert'; import _ from 'lodash'; -import { - comp1, - comp2 -} from './fixtures'; - -describe('Password Component', function() { - it('Should build a password component', function() { - return Harness.testCreate(PasswordComponent, comp1).then((component) => { - Harness.testElements(component, 'input[type="password"]', 1); +import { comp1, comp2 } from './fixtures'; + +describe('Password Component', function () { + it('Should build a password component', function () { + return Harness.testCreate(PasswordComponent, comp1).then( + (component) => { + Harness.testElements(component, 'input[type="password"]', 1); + }, + ); + }); + + it('Should provide min/max length validation', function (done) { + const form = _.cloneDeep(comp2); + form.components[0].validate = { minLength: 5, maxLength: 10 }; + + const validValues = [ + '', + 'te_st', + 'test value', + ' ', + 'What?', + 'test: ', + 't ', + ' t ', + ]; + + const invalidMin = ['t', 'tt', 'ttt', 'tttt', ' t ', ' t', '_4_']; + + const invalidMax = [ + 'test__value', + 'test value ', + ' test value', + 'test: value', + ]; + + const testValidity = (values, valid, message, lastValue) => { + _.each(values, (value) => { + const element = document.createElement('div'); + + Formio.createForm(element, form) + .then((form) => { + form.setPristine(false); + + const component = form.getComponent('password'); + const changed = component.setValue(value); + const error = message; + + if (value) { + assert.equal(changed, true, 'Should set value'); + } + + setTimeout(() => { + if (valid) { + assert.equal( + !!component.error, + false, + 'Should not contain error', + ); + } else { + assert.equal( + !!component.error, + true, + 'Should contain error', + ); + assert.equal( + component.error.message, + error, + 'Should contain error message', + ); + assert.equal( + component.element.classList.contains( + 'has-error', + ), + true, + 'Should contain error class', + ); + assert.equal( + component.refs.messageContainer.textContent.trim(), + error, + 'Should show error', + ); + } + + if (_.isEqual(value, lastValue)) { + done(); + } + }, 300); + }) + .catch(done); + }); + }; + + testValidity(validValues, true); + testValidity( + invalidMin, + false, + 'Password must have at least 5 characters.', + ); + testValidity( + invalidMax, + false, + 'Password must have no more than 10 characters.', + invalidMax[invalidMax.length - 1], + ); + }); + + it('Should provide pattern validation', function (done) { + const form = _.cloneDeep(comp2); + form.components[0].validate = { pattern: '\\D+' }; + + const validValues = [ + '', + ' ', + 'test value', + '& "" (test) _ ,.*', + ' some - test - value ', + ]; + + const invalidValues = ['test(2)', '123', '0 waste', '"9"', ' 9']; + + const testValidity = (values, valid, message, lastValue) => { + _.each(values, (value) => { + const element = document.createElement('div'); + + Formio.createForm(element, form) + .then((form) => { + form.setPristine(false); + + const component = form.getComponent('password'); + const changed = component.setValue(value); + const error = message; + + if (value) { + assert.equal(changed, true, 'Should set value'); + } + + setTimeout(() => { + if (valid) { + assert.equal( + !!component.error, + false, + 'Should not contain error', + ); + } else { + assert.equal( + !!component.error, + true, + 'Should contain error', + ); + assert.equal( + component.error.message.trim(), + error, + 'Should contain error message', + ); + assert.equal( + component.element.classList.contains( + 'has-error', + ), + true, + 'Should contain error class', + ); + assert.equal( + component.refs.messageContainer.textContent.trim(), + error, + 'Should show error', + ); + } + + if (_.isEqual(value, lastValue)) { + done(); + } + }, 300); + }) + .catch(done); + }); + }; + + testValidity(validValues, true); + testValidity( + invalidValues, + false, + 'Password does not match the pattern \\D+', + invalidValues[invalidValues.length - 1], + ); }); - }); - - it('Should provide min/max length validation', function(done) { - const form = _.cloneDeep(comp2); - form.components[0].validate = { minLength: 5, maxLength: 10 }; - - const validValues = [ - '', - 'te_st', - 'test value', - ' ', - 'What?', - 'test: ', - 't ', - ' t ' - ]; - - const invalidMin = [ - 't', - 'tt', - 'ttt', - 'tttt', - ' t ', - ' t', - '_4_' - ]; - - const invalidMax = [ - 'test__value', - 'test value ', - ' test value', - 'test: value', - ]; - - const testValidity = (values, valid, message, lastValue) => { - _.each(values, (value) => { - const element = document.createElement('div'); - - Formio.createForm(element, form).then(form => { - form.setPristine(false); - - const component = form.getComponent('password'); - const changed = component.setValue(value); - const error = message; - - if (value) { - assert.equal(changed, true, 'Should set value'); - } - - setTimeout(() => { - if (valid) { - assert.equal(!!component.error, false, 'Should not contain error'); - } - else { - assert.equal(!!component.error, true, 'Should contain error'); - assert.equal(component.error.message, error, 'Should contain error message'); - assert.equal(component.element.classList.contains('has-error'), true, 'Should contain error class'); - assert.equal(component.refs.messageContainer.textContent.trim(), error, 'Should show error'); - } - - if (_.isEqual(value, lastValue)) { - done(); - } - }, 300); - }).catch(done); - }); - }; - - testValidity(validValues, true); - testValidity(invalidMin, false, 'Password must have at least 5 characters.'); - testValidity(invalidMax, false, 'Password must have no more than 10 characters.', invalidMax[invalidMax.length-1]); - }); - - it('Should provide pattern validation', function(done) { - const form = _.cloneDeep(comp2); - form.components[0].validate = { pattern: '\\D+' }; - - const validValues = [ - '', - ' ', - 'test value', - '& "" (test) _ ,.*', - ' some - test - value ', - ]; - - const invalidValues = [ - 'test(2)', - '123', - '0 waste', - '"9"', - ' 9', - ]; - - const testValidity = (values, valid, message, lastValue) => { - _.each(values, (value) => { - const element = document.createElement('div'); - - Formio.createForm(element, form).then(form => { - form.setPristine(false); - - const component = form.getComponent('password'); - const changed = component.setValue(value); - const error = message; - - if (value) { - assert.equal(changed, true, 'Should set value'); - } - - setTimeout(() => { - if (valid) { - assert.equal(!!component.error, false, 'Should not contain error'); - } - else { - assert.equal(!!component.error, true, 'Should contain error'); - assert.equal(component.error.message.trim(), error, 'Should contain error message'); - assert.equal(component.element.classList.contains('has-error'), true, 'Should contain error class'); - assert.equal(component.refs.messageContainer.textContent.trim(), error, 'Should show error'); - } - - if (_.isEqual(value, lastValue)) { - done(); - } - }, 300); - }).catch(done); - }); - }; - - testValidity(validValues, true); - testValidity(invalidValues, false, 'Password does not match the pattern \\D+', invalidValues[invalidValues.length-1]); - }); }); diff --git a/src/components/password/editForm/Password.edit.data.js b/src/components/password/editForm/Password.edit.data.js index acea1bd282..8a974f3658 100644 --- a/src/components/password/editForm/Password.edit.data.js +++ b/src/components/password/editForm/Password.edit.data.js @@ -1,46 +1,47 @@ export default [ - { - key: 'inputFormat', - ignore: true - }, - { - key: 'persistent', - ignore: true - }, - { - key: 'protected', - ignore: true - }, - { - key: 'dbIndex', - ignore: true - }, - { - key: 'encrypted', - ignore: true - }, - { - key: 'multiple', - ignore: true - }, - { - key: 'defaultValue', - ignore: true - }, - { - key: 'customDefaultValuePanel', - ignore: true - }, - { - key: 'calculateValuePanel', - ignore: true - }, - { - key: 'passwordInfo', - weight: 0, - type: 'htmlelement', - tag: 'div', - className: 'alert alert-info', - content: 'Password fields are automatically encrypted using 1-way salted bcrypt hashes. These hashes are also protected and not returned in the API.' - } + { + key: 'inputFormat', + ignore: true, + }, + { + key: 'persistent', + ignore: true, + }, + { + key: 'protected', + ignore: true, + }, + { + key: 'dbIndex', + ignore: true, + }, + { + key: 'encrypted', + ignore: true, + }, + { + key: 'multiple', + ignore: true, + }, + { + key: 'defaultValue', + ignore: true, + }, + { + key: 'customDefaultValuePanel', + ignore: true, + }, + { + key: 'calculateValuePanel', + ignore: true, + }, + { + key: 'passwordInfo', + weight: 0, + type: 'htmlelement', + tag: 'div', + className: 'alert alert-info', + content: + 'Password fields are automatically encrypted using 1-way salted bcrypt hashes. These hashes are also protected and not returned in the API.', + }, ]; diff --git a/src/components/password/editForm/Password.edit.display.js b/src/components/password/editForm/Password.edit.display.js index 73ddba3fa2..94fa7cf1a9 100644 --- a/src/components/password/editForm/Password.edit.display.js +++ b/src/components/password/editForm/Password.edit.display.js @@ -1,14 +1,14 @@ export default [ - { - ignore: true, - key: 'mask', - }, - { - key: 'inputMask', - ignore: true - }, - { - key: 'allowMultipleMasks', - ignore: true - } + { + ignore: true, + key: 'mask', + }, + { + key: 'inputMask', + ignore: true, + }, + { + key: 'allowMultipleMasks', + ignore: true, + }, ]; diff --git a/src/components/password/editForm/Password.edit.validation.js b/src/components/password/editForm/Password.edit.validation.js index 3943fe1686..f2263c0f96 100644 --- a/src/components/password/editForm/Password.edit.validation.js +++ b/src/components/password/editForm/Password.edit.validation.js @@ -1,14 +1,14 @@ export default [ - { - key: 'unique', - ignore: true - }, - { - key: 'validate.minWords', - ignore: true - }, - { - key: 'validate.maxWords', - ignore: true - }, + { + key: 'unique', + ignore: true, + }, + { + key: 'validate.minWords', + ignore: true, + }, + { + key: 'validate.maxWords', + ignore: true, + }, ]; diff --git a/src/components/password/fixtures/comp1.js b/src/components/password/fixtures/comp1.js index 3d3acd5200..74f5a58fcd 100644 --- a/src/components/password/fixtures/comp1.js +++ b/src/components/password/fixtures/comp1.js @@ -1,22 +1,20 @@ export default { - 'input': true, - 'tableView': false, - 'inputType': 'password', - 'label': 'Password', - 'key': 'password', - 'placeholder': '', - 'prefix': '', - 'suffix': '', - 'protected': true, - 'persistent': true, - 'type': 'password', - 'tags': [ - - ], - 'conditional': { - 'show': '', - 'when': null, - 'eq': '' - }, - 'isNew': false + input: true, + tableView: false, + inputType: 'password', + label: 'Password', + key: 'password', + placeholder: '', + prefix: '', + suffix: '', + protected: true, + persistent: true, + type: 'password', + tags: [], + conditional: { + show: '', + when: null, + eq: '', + }, + isNew: false, }; diff --git a/src/components/password/fixtures/comp2.js b/src/components/password/fixtures/comp2.js index 1f3cbbc489..8fd46f689d 100644 --- a/src/components/password/fixtures/comp2.js +++ b/src/components/password/fixtures/comp2.js @@ -1,27 +1,27 @@ export default { - type: 'form', - components: [ - { - label: 'Password', - tableView: false, - key: 'password', - type: 'password', - input: true, - protected: true - }, - { - label: 'Submit', - showValidations: false, - tableView: false, - key: 'submit', - type: 'button', - input: true - } - ], - revisions: '', - _vid: 0, - title: 'password tests', - display: 'form', - name: 'passwordTests', - path: 'passwordtests', + type: 'form', + components: [ + { + label: 'Password', + tableView: false, + key: 'password', + type: 'password', + input: true, + protected: true, + }, + { + label: 'Submit', + showValidations: false, + tableView: false, + key: 'submit', + type: 'button', + input: true, + }, + ], + revisions: '', + _vid: 0, + title: 'password tests', + display: 'form', + name: 'passwordTests', + path: 'passwordtests', }; diff --git a/src/components/password/fixtures/values.js b/src/components/password/fixtures/values.js index 078e72435d..bf7cb0e217 100644 --- a/src/components/password/fixtures/values.js +++ b/src/components/password/fixtures/values.js @@ -1,4 +1 @@ -export default [ - 'secret', - 'supersecret', -]; +export default ['secret', 'supersecret']; diff --git a/src/components/phonenumber/PhoneNumber.form.js b/src/components/phonenumber/PhoneNumber.form.js index 5a34323540..14fad0f9d8 100644 --- a/src/components/phonenumber/PhoneNumber.form.js +++ b/src/components/phonenumber/PhoneNumber.form.js @@ -2,33 +2,36 @@ import textEditForm from '../textfield/TextField.form'; import PhoneNumberEditValidation from './editForm/PhoneNumber.edit.validation'; -export default function(...extend) { - return textEditForm([ - { - key: 'display', - components: [ - { - key: 'showWordCount', - ignore: true - }, - { - key: 'showCharCount', - ignore: true - } - ] - }, - { - key: 'data', - components: [ - { - key: 'case', - ignore: true - } - ] - }, - { - key: 'validation', - components: PhoneNumberEditValidation - }, - ], ...extend); +export default function (...extend) { + return textEditForm( + [ + { + key: 'display', + components: [ + { + key: 'showWordCount', + ignore: true, + }, + { + key: 'showCharCount', + ignore: true, + }, + ], + }, + { + key: 'data', + components: [ + { + key: 'case', + ignore: true, + }, + ], + }, + { + key: 'validation', + components: PhoneNumberEditValidation, + }, + ], + ...extend, + ); } diff --git a/src/components/phonenumber/PhoneNumber.js b/src/components/phonenumber/PhoneNumber.js index c927b94596..ed8727c9c1 100644 --- a/src/components/phonenumber/PhoneNumber.js +++ b/src/components/phonenumber/PhoneNumber.js @@ -2,36 +2,45 @@ import TextFieldComponent from '../textfield/TextField'; import _ from 'lodash'; export default class PhoneNumberComponent extends TextFieldComponent { - static schema(...extend) { - return TextFieldComponent.schema({ - type: 'phoneNumber', - label: 'Phone Number', - key: 'phoneNumber', - inputType: 'tel', - inputMask: '(999) 999-9999', - inputMode: 'decimal', - displayMask: '', - }, ...extend); - } + static schema(...extend) { + return TextFieldComponent.schema( + { + type: 'phoneNumber', + label: 'Phone Number', + key: 'phoneNumber', + inputType: 'tel', + inputMask: '(999) 999-9999', + inputMode: 'decimal', + displayMask: '', + }, + ...extend, + ); + } - static get builderInfo() { - return { - title: 'Phone Number', - group: 'advanced', - icon: 'phone-square', - weight: 30, - documentation: '/userguide/form-building/advanced-components#phone-number', - schema: PhoneNumberComponent.schema() - }; - } + static get builderInfo() { + return { + title: 'Phone Number', + group: 'advanced', + icon: 'phone-square', + weight: 30, + documentation: + '/userguide/form-building/advanced-components#phone-number', + schema: PhoneNumberComponent.schema(), + }; + } - get defaultSchema() { - return PhoneNumberComponent.schema(); - } + get defaultSchema() { + return PhoneNumberComponent.schema(); + } - getValueAsString(value, options) { - if (options?.email && this.visible && !this.skipInEmail && _.isObject(value)) { - const result = (` + getValueAsString(value, options) { + if ( + options?.email && + this.visible && + !this.skipInEmail && + _.isObject(value) + ) { + const result = ` @@ -40,11 +49,11 @@ export default class PhoneNumberComponent extends TextFieldComponent {
    - `); + `; - return result; - } + return result; + } - return super.getValueAsString(value, options); - } + return super.getValueAsString(value, options); + } } diff --git a/src/components/phonenumber/PhoneNumber.unit.js b/src/components/phonenumber/PhoneNumber.unit.js index 966ea909d5..6cb9bc5823 100644 --- a/src/components/phonenumber/PhoneNumber.unit.js +++ b/src/components/phonenumber/PhoneNumber.unit.js @@ -3,56 +3,80 @@ import PhoneNumberComponent from './PhoneNumber'; import assert from 'power-assert'; import { Formio } from './../../Formio'; -import { - comp1, -} from './fixtures'; +import { comp1 } from './fixtures'; -describe('PhoneNumber Component', function() { - it('Should build a phone number component', function() { - return Harness.testCreate(PhoneNumberComponent, comp1).then((component) => { - Harness.testElements(component, 'input[type="text"]', 1); +describe('PhoneNumber Component', function () { + it('Should build a phone number component', function () { + return Harness.testCreate(PhoneNumberComponent, comp1).then( + (component) => { + Harness.testElements(component, 'input[type="text"]', 1); + }, + ); }); - }); - it('Should check mask and value in the phone component in the email template', function(done) { - const formJson = { - components: [{ - label: 'Phone Number', - tableView: true, - allowMultipleMasks: true, - inputMasks: [{ - label: 'mask1', - mask: 'mask1' - }], - key: 'phoneNumber', - type: 'phoneNumber', - input: true - }] - }; - const element = document.createElement('div'); - Formio.createForm(element, formJson) - .then(form => { - form.setSubmission({ - data: { - phoneNumber: { - value: 'mask1', - maskName: 'mask2' - } - }, - }); + it('Should check mask and value in the phone component in the email template', function (done) { + const formJson = { + components: [ + { + label: 'Phone Number', + tableView: true, + allowMultipleMasks: true, + inputMasks: [ + { + label: 'mask1', + mask: 'mask1', + }, + ], + key: 'phoneNumber', + type: 'phoneNumber', + input: true, + }, + ], + }; + const element = document.createElement('div'); + Formio.createForm(element, formJson) + .then((form) => { + form.setSubmission({ + data: { + phoneNumber: { + value: 'mask1', + maskName: 'mask2', + }, + }, + }); - const phoneNumber = form.getComponent('phoneNumber'); + const phoneNumber = form.getComponent('phoneNumber'); - setTimeout(() => { - assert.equal(phoneNumber.dataValue.value, 'mask1', 'Should check value'); - assert.equal(phoneNumber.dataValue.maskName, 'mask2', 'Should check maskName'); - const toString = phoneNumber.getValueAsString(phoneNumber.dataValue, { email: true }); - assert.ok(toString.includes('table'), 'Email template should render html table'); - assert.ok(toString.includes(phoneNumber.dataValue.maskName), 'Email template should have Phone Number mackName'); - assert.ok(toString.includes(phoneNumber.dataValue.value), 'Email template should have Phone Number value'); - done(); - }, 300); - }) - .catch(done); - }); + setTimeout(() => { + assert.equal( + phoneNumber.dataValue.value, + 'mask1', + 'Should check value', + ); + assert.equal( + phoneNumber.dataValue.maskName, + 'mask2', + 'Should check maskName', + ); + const toString = phoneNumber.getValueAsString( + phoneNumber.dataValue, + { email: true }, + ); + assert.ok( + toString.includes('table'), + 'Email template should render html table', + ); + assert.ok( + toString.includes(phoneNumber.dataValue.maskName), + 'Email template should have Phone Number mackName', + ); + assert.ok( + toString.includes(phoneNumber.dataValue.value), + 'Email template should have Phone Number value', + ); + done(); + }, 300); + }) + .catch(done); + }); }); diff --git a/src/components/phonenumber/editForm/PhoneNumber.edit.validation.js b/src/components/phonenumber/editForm/PhoneNumber.edit.validation.js index a455b2bbc5..7ca2625af9 100644 --- a/src/components/phonenumber/editForm/PhoneNumber.edit.validation.js +++ b/src/components/phonenumber/editForm/PhoneNumber.edit.validation.js @@ -1,22 +1,22 @@ export default [ - { - key: 'validate.minLength', - ignore: true - }, - { - key: 'validate.maxLength', - ignore: true - }, - { - key: 'validate.pattern', - ignore: true - }, - { - key: 'validate.minWords', - ignore: true - }, - { - key: 'validate.maxWords', - ignore: true - } + { + key: 'validate.minLength', + ignore: true, + }, + { + key: 'validate.maxLength', + ignore: true, + }, + { + key: 'validate.pattern', + ignore: true, + }, + { + key: 'validate.minWords', + ignore: true, + }, + { + key: 'validate.maxWords', + ignore: true, + }, ]; diff --git a/src/components/phonenumber/fixtures/comp1.js b/src/components/phonenumber/fixtures/comp1.js index e49825444c..6b922f554b 100644 --- a/src/components/phonenumber/fixtures/comp1.js +++ b/src/components/phonenumber/fixtures/comp1.js @@ -1,27 +1,25 @@ export default { - 'input': true, - 'tableView': true, - 'inputMask': '(999) 999-9999', - 'label': 'Phone Number', - 'key': 'phoneNumber', - 'placeholder': '', - 'prefix': '', - 'suffix': '', - 'multiple': false, - 'protected': false, - 'unique': false, - 'persistent': true, - 'defaultValue': '', - 'validate': { - 'required': false - }, - 'type': 'phoneNumber', - 'tags': [ - - ], - 'conditional': { - 'show': '', - 'when': null, - 'eq': '' - } + input: true, + tableView: true, + inputMask: '(999) 999-9999', + label: 'Phone Number', + key: 'phoneNumber', + placeholder: '', + prefix: '', + suffix: '', + multiple: false, + protected: false, + unique: false, + persistent: true, + defaultValue: '', + validate: { + required: false, + }, + type: 'phoneNumber', + tags: [], + conditional: { + show: '', + when: null, + eq: '', + }, }; diff --git a/src/components/phonenumber/fixtures/values.js b/src/components/phonenumber/fixtures/values.js index 1083075dc1..95bb52a54b 100644 --- a/src/components/phonenumber/fixtures/values.js +++ b/src/components/phonenumber/fixtures/values.js @@ -1,4 +1 @@ -export default [ - '(123) 456-7890', - '(098) 765-4321', -]; +export default ['(123) 456-7890', '(098) 765-4321']; diff --git a/src/components/radio/Radio.form.js b/src/components/radio/Radio.form.js index 165a41f278..f4ab915bd6 100644 --- a/src/components/radio/Radio.form.js +++ b/src/components/radio/Radio.form.js @@ -3,19 +3,22 @@ import RadioEditData from './editForm/Radio.edit.data'; import RadioEditDisplay from './editForm/Radio.edit.display'; import RadioEditValidation from './editForm/Radio.edit.validation'; -export default function(...extend) { - return listComponentForm([ - { - key: 'display', - components: RadioEditDisplay - }, - { - key: 'data', - components: RadioEditData - }, - { - key: 'validation', - components: RadioEditValidation - }, - ], ...extend); +export default function (...extend) { + return listComponentForm( + [ + { + key: 'display', + components: RadioEditDisplay, + }, + { + key: 'data', + components: RadioEditData, + }, + { + key: 'validation', + components: RadioEditValidation, + }, + ], + ...extend, + ); } diff --git a/src/components/radio/Radio.js b/src/components/radio/Radio.js index 5a97a65343..f1b1dcad0d 100644 --- a/src/components/radio/Radio.js +++ b/src/components/radio/Radio.js @@ -1,433 +1,513 @@ import _ from 'lodash'; import ListComponent from '../_classes/list/ListComponent'; import { Formio } from '../../Formio'; -import { boolValue, componentValueTypes, getComponentSavedTypes } from '../../utils/utils'; +import { + boolValue, + componentValueTypes, + getComponentSavedTypes, +} from '../../utils/utils'; export default class RadioComponent extends ListComponent { - static schema(...extend) { - return ListComponent.schema({ - type: 'radio', - inputType: 'radio', - label: 'Radio', - key: 'radio', - values: [{ label: '', value: '' }], - data: { - url: '', - }, - fieldSet: false - }, ...extend); - } - - static get builderInfo() { - return { - title: 'Radio', - group: 'basic', - icon: 'dot-circle-o', - weight: 80, - documentation: '/userguide/form-building/form-components#radio', - schema: RadioComponent.schema() - }; - } - - static get conditionOperatorsSettings() { - return { - ...super.conditionOperatorsSettings, - valueComponent(classComp) { + static schema(...extend) { + return ListComponent.schema( + { + type: 'radio', + inputType: 'radio', + label: 'Radio', + key: 'radio', + values: [{ label: '', value: '' }], + data: { + url: '', + }, + fieldSet: false, + }, + ...extend, + ); + } + + static get builderInfo() { return { - type: 'select', - dataSrc: 'custom', - valueProperty: 'value', - dataType: classComp.dataType || '', - data: { - custom() { - return classComp.values; - } - }, + title: 'Radio', + group: 'basic', + icon: 'dot-circle-o', + weight: 80, + documentation: '/userguide/form-building/form-components#radio', + schema: RadioComponent.schema(), }; - } - }; - } - - static savedValueTypes(schema) { - const { boolean, string, number, object, array } = componentValueTypes; - const { dataType } = schema; - const types = getComponentSavedTypes(schema); - - if (types) { - return types; } - if (dataType === 'object') { - return [object, array]; + static get conditionOperatorsSettings() { + return { + ...super.conditionOperatorsSettings, + valueComponent(classComp) { + return { + type: 'select', + dataSrc: 'custom', + valueProperty: 'value', + dataType: classComp.dataType || '', + data: { + custom() { + return classComp.values; + }, + }, + }; + }, + }; } - if (componentValueTypes[dataType]) { - return [componentValueTypes[dataType]]; - } + static savedValueTypes(schema) { + const { boolean, string, number, object, array } = componentValueTypes; + const { dataType } = schema; + const types = getComponentSavedTypes(schema); - return [boolean, string, number, object, array]; - } + if (types) { + return types; + } - constructor(component, options, data) { - super(component, options, data); - this.previousValue = this.dataValue || null; - } + if (dataType === 'object') { + return [object, array]; + } - get defaultSchema() { - return RadioComponent.schema(); - } + if (componentValueTypes[dataType]) { + return [componentValueTypes[dataType]]; + } - get defaultValue() { - let defaultValue = super.defaultValue; - if (!defaultValue && this.component.defaultValue === false) { - defaultValue = this.component.defaultValue; + return [boolean, string, number, object, array]; } - return defaultValue; - } - - get inputInfo() { - const info = super.elementInfo(); - info.type = 'input'; - info.changeEvent = 'click'; - info.attr.class = 'form-check-input'; - info.attr.name = info.attr.name += `[${this.root?.id}-${this.id}]`; - return info; - } - - get emptyValue() { - return ''; - } - - get isRadio() { - return this.component.inputType === 'radio'; - } - - get optionSelectedClass() { - return 'radio-selected'; - } - - get listData() { - const listData = _.get(this.root, 'submission.metadata.listData', {}); - return _.get(listData, this.path); - } - - init() { - super.init(); - this.templateData = {}; - this.validators = this.validators.concat(['select', 'onlyAvailableItems', 'availableValueProperty']); - - // Trigger an update.// - let updateArgs = []; - const triggerUpdate = _.debounce((...args) => { - updateArgs = []; - return this.updateItems.apply(this, args); - }, 100); - this.triggerUpdate = (...args) => { - // Make sure we always resolve the previous promise before reassign it - if (typeof this.itemsLoadedResolve === 'function') { - this.itemsLoadedResolve(); - } - this.itemsLoaded = new Promise((resolve) => { - this.itemsLoadedResolve = resolve; - }); - if (args.length) { - updateArgs = args; - } - return triggerUpdate(...updateArgs); - }; - - this.itemsLoaded = new Promise((resolve) => { - this.itemsLoadedResolve = resolve; - }); - this.optionsLoaded = false; - this.loadedOptions = []; - - // Get the template keys for this radio component. - this.getTemplateKeys(); - } - - render() { - return super.render(this.renderTemplate('radio', { - input: this.inputInfo, - inline: this.component.inline, - values: this.component.dataSrc === 'values' ? this.component.values : this.loadedOptions, - value: this.dataValue, - row: this.row, - })); - } - - attach(element) { - this.loadRefs(element, { input: 'multiple', wrapper: 'multiple' }); - this.refs.input.forEach((input, index) => { - this.addEventListener(input, this.inputInfo.changeEvent, () => { - this.updateValue(null, { - modified: true, - }); - }); - if (this.component.values[index]) { - this.addShortcut(input, this.component.values[index].shortcut); - } - if (this.isRadio) { - let dataValue = this.dataValue; + constructor(component, options, data) { + super(component, options, data); + this.previousValue = this.dataValue || null; + } - if (!_.isString(this.dataValue)) { - dataValue = _.toString(this.dataValue); - } + get defaultSchema() { + return RadioComponent.schema(); + } - if (this.isSelectURL && _.isObject(this.loadedOptions[index].value)) { - input.checked = _.isEqual(this.loadedOptions[index].value, this.dataValue); + get defaultValue() { + let defaultValue = super.defaultValue; + if (!defaultValue && this.component.defaultValue === false) { + defaultValue = this.component.defaultValue; } - else { - input.checked = (dataValue === input.value && (input.value || this.component.dataSrc !== 'url')); - } - this.addEventListener(input, 'keyup', (event) => { - if (event.key === ' ' && dataValue === input.value) { - event.preventDefault(); - - this.updateValue(null, { - modified: true, - }); - } - }); - } - }); - this.triggerUpdate(); - this.setSelectedClasses(); - return super.attach(element); - } - - detach(element) { - if (element && this.refs.input) { - this.refs.input.forEach((input, index) => { - if (this.component.values[index]) { - this.removeShortcut(input, this.component.values[index].shortcut); - } - }); + return defaultValue; } - super.detach(); - } - getValue() { - if (this.viewOnly || !this.refs.input || !this.refs.input.length) { - return this.dataValue; + get inputInfo() { + const info = super.elementInfo(); + info.type = 'input'; + info.changeEvent = 'click'; + info.attr.class = 'form-check-input'; + info.attr.name = info.attr.name += `[${this.root?.id}-${this.id}]`; + return info; } - let value = this.dataValue; - this.refs.input.forEach((input, index) => { - if (input.checked) { - value = (this.isSelectURL && _.isObject(this.loadedOptions[index].value)) ? - this.loadedOptions[index].value : - input.value; - } - }); - return value; - } - - validateValueProperty() { - if (this.component.dataSrc === 'values') { - return true; + + get emptyValue() { + return ''; } - return !_.some(this.refs.wrapper, (wrapper, index) => this.refs.input[index].checked && this.loadedOptions[index].invalid); - } + get isRadio() { + return this.component.inputType === 'radio'; + } - validateValueAvailability(setting, value) { - if (!boolValue(setting) || !value) { - return true; + get optionSelectedClass() { + return 'radio-selected'; } - const values = this.component.values; - if (values) { - return values.findIndex(({ value: optionValue }) => this.normalizeValue(optionValue) === value) !== -1; + get listData() { + const listData = _.get(this.root, 'submission.metadata.listData', {}); + return _.get(listData, this.path); } - return false; - } + init() { + super.init(); + this.templateData = {}; + this.validators = this.validators.concat([ + 'select', + 'onlyAvailableItems', + 'availableValueProperty', + ]); + + // Trigger an update.// + let updateArgs = []; + const triggerUpdate = _.debounce((...args) => { + updateArgs = []; + return this.updateItems.apply(this, args); + }, 100); + this.triggerUpdate = (...args) => { + // Make sure we always resolve the previous promise before reassign it + if (typeof this.itemsLoadedResolve === 'function') { + this.itemsLoadedResolve(); + } + this.itemsLoaded = new Promise((resolve) => { + this.itemsLoadedResolve = resolve; + }); + if (args.length) { + updateArgs = args; + } + return triggerUpdate(...updateArgs); + }; - getValueAsString(value) { - if (_.isObject(value)) { - value = JSON.stringify(value); - } - else if (!_.isString(value)) { - value = _.toString(value); - } - if (this.component.dataSrc !== 'values') { - return value; - } + this.itemsLoaded = new Promise((resolve) => { + this.itemsLoadedResolve = resolve; + }); + this.optionsLoaded = false; + this.loadedOptions = []; - const option = _.find(this.component.values, (v) => v.value === value); + // Get the template keys for this radio component. + this.getTemplateKeys(); + } - if (!value) { - return _.get(option, 'label', ''); + render() { + return super.render( + this.renderTemplate('radio', { + input: this.inputInfo, + inline: this.component.inline, + values: + this.component.dataSrc === 'values' + ? this.component.values + : this.loadedOptions, + value: this.dataValue, + row: this.row, + }), + ); } - return _.get(option, 'label', ''); - } + attach(element) { + this.loadRefs(element, { input: 'multiple', wrapper: 'multiple' }); + this.refs.input.forEach((input, index) => { + this.addEventListener(input, this.inputInfo.changeEvent, () => { + this.updateValue(null, { + modified: true, + }); + }); + if (this.component.values[index]) { + this.addShortcut(input, this.component.values[index].shortcut); + } - setValueAt(index, value) { - if (this.refs.input && this.refs.input[index] && value !== null && value !== undefined) { - const inputValue = this.refs.input[index].value; - this.refs.input[index].checked = (inputValue === value.toString()); + if (this.isRadio) { + let dataValue = this.dataValue; + + if (!_.isString(this.dataValue)) { + dataValue = _.toString(this.dataValue); + } + + if ( + this.isSelectURL && + _.isObject(this.loadedOptions[index].value) + ) { + input.checked = _.isEqual( + this.loadedOptions[index].value, + this.dataValue, + ); + } else { + input.checked = + dataValue === input.value && + (input.value || this.component.dataSrc !== 'url'); + } + this.addEventListener(input, 'keyup', (event) => { + if (event.key === ' ' && dataValue === input.value) { + event.preventDefault(); + + this.updateValue(null, { + modified: true, + }); + } + }); + } + }); + this.triggerUpdate(); + this.setSelectedClasses(); + return super.attach(element); } - } - loadItems(url, search, headers, options, method, body) { - if (this.optionsLoaded) { - return; + detach(element) { + if (element && this.refs.input) { + this.refs.input.forEach((input, index) => { + if (this.component.values[index]) { + this.removeShortcut( + input, + this.component.values[index].shortcut, + ); + } + }); + } + super.detach(); } - if (!this.shouldLoad && this.listData) { - this.loadItemsFromMetadata(); - return; + getValue() { + if (this.viewOnly || !this.refs.input || !this.refs.input.length) { + return this.dataValue; + } + let value = this.dataValue; + this.refs.input.forEach((input, index) => { + if (input.checked) { + value = + this.isSelectURL && + _.isObject(this.loadedOptions[index].value) + ? this.loadedOptions[index].value + : input.value; + } + }); + return value; } - // Ensure we have a method and remove any body if method is get - method = method || 'GET'; - if (method.toUpperCase() === 'GET') { - body = null; + validateValueProperty() { + if (this.component.dataSrc === 'values') { + return true; + } + + return !_.some( + this.refs.wrapper, + (wrapper, index) => + this.refs.input[index].checked && + this.loadedOptions[index].invalid, + ); } - // Set ignoreCache if it is - options.ignoreCache = this.component.ignoreCache; - // Make the request. - options.header = headers; - - this.loading = true; - Formio.makeRequest(this.options.formio, 'select', url, method, body, options) - .then((response) => { - this.loading = false; - this.error = null; - this.setItems(response); - this.optionsLoaded = true; - this.redraw(); - }) - .catch((err) => { - this.handleLoadingError(err); - }); - } - - loadItemsFromMetadata() { - this.listData.forEach((item, i) => { - this.loadedOptions[i] = { - label: this.itemTemplate(item) - }; - if (_.isEqual(item, this.selectData || _.pick(this.dataValue, _.keys(item)))) { - this.loadedOptions[i].value = this.dataValue; - } - }); - this.optionsLoaded = true; - this.redraw(); - } - - setItems(items) { - const listData = []; - items?.forEach((item, i) => { - this.loadedOptions[i] = { - value: this.component.valueProperty ? item[this.component.valueProperty] : item, - label: this.component.valueProperty ? this.itemTemplate(item, item[this.component.valueProperty]) : this.itemTemplate(item, item, i) - }; - listData.push(this.templateData[this.component.valueProperty ? item[this.component.valueProperty] : i]); - - if ((this.component.valueProperty || !this.isRadio) && ( - _.isUndefined(item[this.component.valueProperty]) || - (!this.isRadio && _.isObject(item[this.component.valueProperty])) || - (!this.isRadio && _.isBoolean(item[this.component.valueProperty])) - )) { - this.loadedOptions[i].invalid = true; - } - }); - - if (this.isSelectURL) { - const submission = this.root.submission; - if (!submission.metadata) { - submission.metadata = {}; - } - if (!submission.metadata.listData) { - submission.metadata.listData = {}; - } - _.set(submission.metadata.listData, this.path, listData); + validateValueAvailability(setting, value) { + if (!boolValue(setting) || !value) { + return true; + } + + const values = this.component.values; + if (values) { + return ( + values.findIndex( + ({ value: optionValue }) => + this.normalizeValue(optionValue) === value, + ) !== -1 + ); + } + + return false; } - } - - setSelectedClasses() { - if (this.refs.wrapper) { - //add/remove selected option class - const value = this.dataValue; - this.refs.wrapper.forEach((wrapper, index) => { - const input = this.refs.input[index]; - const checked = (input.type === 'checkbox') ? value[input.value] : (input.value.toString() === value.toString()); - if (checked) { - //add class to container when selected - this.addClass(wrapper, this.optionSelectedClass); - //change "checked" attribute - input.setAttribute('checked', 'true'); + + getValueAsString(value) { + if (_.isObject(value)) { + value = JSON.stringify(value); + } else if (!_.isString(value)) { + value = _.toString(value); + } + if (this.component.dataSrc !== 'values') { + return value; } - else { - this.removeClass(wrapper, this.optionSelectedClass); - input.removeAttribute('checked'); + + const option = _.find(this.component.values, (v) => v.value === value); + + if (!value) { + return _.get(option, 'label', ''); } - }); + + return _.get(option, 'label', ''); } - } - updateValue(value, flags) { - const changed = super.updateValue(value, flags); - if (changed) { - this.setSelectedClasses(); + setValueAt(index, value) { + if ( + this.refs.input && + this.refs.input[index] && + value !== null && + value !== undefined + ) { + const inputValue = this.refs.input[index].value; + this.refs.input[index].checked = inputValue === value.toString(); + } } - if (!flags || !flags.modified || !this.isRadio) { - if (changed) { - this.previousValue = this.dataValue; - } + loadItems(url, search, headers, options, method, body) { + if (this.optionsLoaded) { + return; + } - return changed; - } + if (!this.shouldLoad && this.listData) { + this.loadItemsFromMetadata(); + return; + } - // If they clicked on the radio that is currently selected, it needs to reset the value. - this.currentValue = this.dataValue; - const shouldResetValue = flags && flags.modified && !flags.noUpdateEvent && this.previousValue === this.currentValue; - if (shouldResetValue) { - this.resetValue(); - this.triggerChange(flags); - this.setSelectedClasses(); + // Ensure we have a method and remove any body if method is get + method = method || 'GET'; + if (method.toUpperCase() === 'GET') { + body = null; + } + + // Set ignoreCache if it is + options.ignoreCache = this.component.ignoreCache; + // Make the request. + options.header = headers; + + this.loading = true; + Formio.makeRequest( + this.options.formio, + 'select', + url, + method, + body, + options, + ) + .then((response) => { + this.loading = false; + this.error = null; + this.setItems(response); + this.optionsLoaded = true; + this.redraw(); + }) + .catch((err) => { + this.handleLoadingError(err); + }); } - this.previousValue = this.dataValue; - return changed; - } - - /** - * Normalize values coming into updateValue. - * - * @param value - * @return {*} - */ - normalizeValue(value) { - if (value === this.emptyValue) { - return value; + + loadItemsFromMetadata() { + this.listData.forEach((item, i) => { + this.loadedOptions[i] = { + label: this.itemTemplate(item), + }; + if ( + _.isEqual( + item, + this.selectData || _.pick(this.dataValue, _.keys(item)), + ) + ) { + this.loadedOptions[i].value = this.dataValue; + } + }); + this.optionsLoaded = true; + this.redraw(); } - const isEquivalent = _.toString(value) === Number(value).toString(); + setItems(items) { + const listData = []; + items?.forEach((item, i) => { + this.loadedOptions[i] = { + value: this.component.valueProperty + ? item[this.component.valueProperty] + : item, + label: this.component.valueProperty + ? this.itemTemplate( + item, + item[this.component.valueProperty], + ) + : this.itemTemplate(item, item, i), + }; + listData.push( + this.templateData[ + this.component.valueProperty + ? item[this.component.valueProperty] + : i + ], + ); + + if ( + (this.component.valueProperty || !this.isRadio) && + (_.isUndefined(item[this.component.valueProperty]) || + (!this.isRadio && + _.isObject(item[this.component.valueProperty])) || + (!this.isRadio && + _.isBoolean(item[this.component.valueProperty]))) + ) { + this.loadedOptions[i].invalid = true; + } + }); - if (!isNaN(parseFloat(value)) && isFinite(value) && isEquivalent) { - value = +value; - } - if (value === 'true') { - value = true; + if (this.isSelectURL) { + const submission = this.root.submission; + if (!submission.metadata) { + submission.metadata = {}; + } + if (!submission.metadata.listData) { + submission.metadata.listData = {}; + } + _.set(submission.metadata.listData, this.path, listData); + } } - if (value === 'false') { - value = false; + + setSelectedClasses() { + if (this.refs.wrapper) { + //add/remove selected option class + const value = this.dataValue; + this.refs.wrapper.forEach((wrapper, index) => { + const input = this.refs.input[index]; + const checked = + input.type === 'checkbox' + ? value[input.value] + : input.value.toString() === value.toString(); + if (checked) { + //add class to container when selected + this.addClass(wrapper, this.optionSelectedClass); + //change "checked" attribute + input.setAttribute('checked', 'true'); + } else { + this.removeClass(wrapper, this.optionSelectedClass); + input.removeAttribute('checked'); + } + }); + } } - if (this.isSelectURL && this.templateData && this.templateData[value]) { - const submission = this.root.submission; - if (!submission.metadata.selectData) { - submission.metadata.selectData = {}; - } + updateValue(value, flags) { + const changed = super.updateValue(value, flags); + if (changed) { + this.setSelectedClasses(); + } + + if (!flags || !flags.modified || !this.isRadio) { + if (changed) { + this.previousValue = this.dataValue; + } - _.set(submission.metadata.selectData, this.path, this.templateData[value]); + return changed; + } + + // If they clicked on the radio that is currently selected, it needs to reset the value. + this.currentValue = this.dataValue; + const shouldResetValue = + flags && + flags.modified && + !flags.noUpdateEvent && + this.previousValue === this.currentValue; + if (shouldResetValue) { + this.resetValue(); + this.triggerChange(flags); + this.setSelectedClasses(); + } + this.previousValue = this.dataValue; + return changed; } - return super.normalizeValue(value); - } + /** + * Normalize values coming into updateValue. + * + * @param value + * @return {*} + */ + normalizeValue(value) { + if (value === this.emptyValue) { + return value; + } + + const isEquivalent = _.toString(value) === Number(value).toString(); + + if (!isNaN(parseFloat(value)) && isFinite(value) && isEquivalent) { + value = +value; + } + if (value === 'true') { + value = true; + } + if (value === 'false') { + value = false; + } + + if (this.isSelectURL && this.templateData && this.templateData[value]) { + const submission = this.root.submission; + if (!submission.metadata.selectData) { + submission.metadata.selectData = {}; + } + + _.set( + submission.metadata.selectData, + this.path, + this.templateData[value], + ); + } + + return super.normalizeValue(value); + } } diff --git a/src/components/radio/Radio.unit.js b/src/components/radio/Radio.unit.js index 4e1089264b..e0f592782a 100644 --- a/src/components/radio/Radio.unit.js +++ b/src/components/radio/Radio.unit.js @@ -5,361 +5,410 @@ import Harness from '../../../test/harness'; import RadioComponent from './Radio'; import { - comp1, - comp2, - comp3, - comp4, - comp5, - comp6, - comp7, - comp8, - comp9, - comp10 + comp1, + comp2, + comp3, + comp4, + comp5, + comp6, + comp7, + comp8, + comp9, + comp10, } from './fixtures'; -describe('Radio Component', function() { - it('Should build a radio component', function() { - return Harness.testCreate(RadioComponent, comp1).then((component) => { - Harness.testElements(component, 'input[type="radio"]', 4); - Harness.testElements(component, 'span', 4); +describe('Radio Component', function () { + it('Should build a radio component', function () { + return Harness.testCreate(RadioComponent, comp1).then((component) => { + Harness.testElements(component, 'input[type="radio"]', 4); + Harness.testElements(component, 'span', 4); + }); }); - }); - it('Should return correct string values if storage type is Number', function() { - return Harness.testCreate(RadioComponent, comp2).then((component) => { - assert.equal(component.getValueAsString(1), 'one'); - assert.equal(component.getValueAsString(2), 'two'); + it('Should return correct string values if storage type is Number', function () { + return Harness.testCreate(RadioComponent, comp2).then((component) => { + assert.equal(component.getValueAsString(1), 'one'); + assert.equal(component.getValueAsString(2), 'two'); + }); }); - }); - - it('Should build a radio component with URL DataSrc', function(done) { - const form = _.cloneDeep(comp9); - const element = document.createElement('div'); - const originalMakeRequest = Formio.makeRequest; - Formio.makeRequest = function() { - return new Promise(resolve => { - const values = [ - { name : 'Alabama', abbreviation : 'AL' }, - { name : 'Alaska', abbreviation: 'AK' }, - { name: 'American Samoa', abbreviation: 'AS' } - ]; - resolve(values); - }); - }; + it('Should build a radio component with URL DataSrc', function (done) { + const form = _.cloneDeep(comp9); + const element = document.createElement('div'); + const originalMakeRequest = Formio.makeRequest; + + Formio.makeRequest = function () { + return new Promise((resolve) => { + const values = [ + { name: 'Alabama', abbreviation: 'AL' }, + { name: 'Alaska', abbreviation: 'AK' }, + { name: 'American Samoa', abbreviation: 'AS' }, + ]; + resolve(values); + }); + }; - Formio.createForm(element, form).then(form => { - const radio = form.getComponent('radio'); + Formio.createForm(element, form) + .then((form) => { + const radio = form.getComponent('radio'); - setTimeout(()=>{ - assert.equal(radio.loadedOptions.length, 3); + setTimeout(() => { + assert.equal(radio.loadedOptions.length, 3); - Formio.makeRequest = originalMakeRequest; - done(); - }, 200); - }).catch(done); - }); + Formio.makeRequest = originalMakeRequest; + done(); + }, 200); + }) + .catch(done); + }); - it('Should provide metadata.selectData for radio component with URL DataSrc', function(done) { - const form = _.cloneDeep(comp9); - const element = document.createElement('div'); - const originalMakeRequest = Formio.makeRequest; + it('Should provide metadata.selectData for radio component with URL DataSrc', function (done) { + const form = _.cloneDeep(comp9); + const element = document.createElement('div'); + const originalMakeRequest = Formio.makeRequest; + + Formio.makeRequest = function () { + return new Promise((resolve) => { + const values = [ + { name: 'Alabama', abbreviation: 'AL' }, + { name: 'Alaska', abbreviation: 'AK' }, + { name: 'American Samoa', abbreviation: 'AS' }, + ]; + resolve(values); + }); + }; - Formio.makeRequest = function() { - return new Promise(resolve => { - const values = [ - { name : 'Alabama', abbreviation : 'AL' }, - { name : 'Alaska', abbreviation: 'AK' }, - { name: 'American Samoa', abbreviation: 'AS' } - ]; - resolve(values); - }); - }; - - Formio.createForm(element, form).then(form => { - const radio = form.getComponent('radio'); - - setTimeout(()=>{ - const value = 'AK'; - radio.setValue(value); - setTimeout(() => { - assert.equal(radio.dataValue, value); - const submit = form.getComponent('submit'); - const clickEvent = new Event('click'); - const submitBtn = submit.refs.button; - submitBtn.dispatchEvent(clickEvent); - setTimeout(() => { - assert.equal(_.isEqual(form.submission.metadata.selectData.radio, { name : 'Alaska' }), true); - assert.equal(form.submission.metadata.listData.radio.length, 3); - Formio.makeRequest = originalMakeRequest; - done(); - },200); - },200); - }, 200); - }).catch(done); - }); - - it('Should save checked value after redrawing if storage type is Number', function(done) { - Harness.testCreate(RadioComponent, comp3).then((component) => { - component.setValue(22); - component.redraw(); - - setTimeout(()=>{ - assert.equal(component.refs.input[0].checked, false); - assert.equal(component.refs.input[1].value, '22'); - assert.equal(component.refs.input[1].checked, true); - assert.equal(component.refs.input[2].checked, false); - done(); - }, 700); + Formio.createForm(element, form) + .then((form) => { + const radio = form.getComponent('radio'); + + setTimeout(() => { + const value = 'AK'; + radio.setValue(value); + setTimeout(() => { + assert.equal(radio.dataValue, value); + const submit = form.getComponent('submit'); + const clickEvent = new Event('click'); + const submitBtn = submit.refs.button; + submitBtn.dispatchEvent(clickEvent); + setTimeout(() => { + assert.equal( + _.isEqual( + form.submission.metadata.selectData.radio, + { name: 'Alaska' }, + ), + true, + ); + assert.equal( + form.submission.metadata.listData.radio.length, + 3, + ); + Formio.makeRequest = originalMakeRequest; + done(); + }, 200); + }, 200); + }, 200); + }) + .catch(done); }); - }); - - it('Should set correct data for 0s values', function(done) { - Harness.testCreate(RadioComponent, comp10).then((component) => { - component.setValue('01'); - component.redraw(); - - setTimeout(()=>{ - assert.equal(component._data.radio, '01'); - component.setValue(1); - component.redraw(); - setTimeout(()=>{ - assert.equal(component._data.radio, 1); - done(); - }, 200); - }, 200); + + it('Should save checked value after redrawing if storage type is Number', function (done) { + Harness.testCreate(RadioComponent, comp3).then((component) => { + component.setValue(22); + component.redraw(); + + setTimeout(() => { + assert.equal(component.refs.input[0].checked, false); + assert.equal(component.refs.input[1].value, '22'); + assert.equal(component.refs.input[1].checked, true); + assert.equal(component.refs.input[2].checked, false); + done(); + }, 700); + }); }); - }); - - it('Span should have correct text label', function() { - return Harness.testCreate(RadioComponent, comp1).then((component) => { - component.element.querySelectorAll('input').forEach((input) => { - assert(input.getAttribute('class').indexOf('form-check-input') !== -1, 'No form-check-input on radios.'); - }); - const spans = component.element.querySelectorAll('span'); - assert.equal(spans[0].innerHTML, 'Red'); - assert.equal(spans[1].innerHTML, 'Green'); - assert.equal(spans[2].innerHTML, 'Blue'); - assert.equal(spans[3].innerHTML, 'Yellow'); + + it('Should set correct data for 0s values', function (done) { + Harness.testCreate(RadioComponent, comp10).then((component) => { + component.setValue('01'); + component.redraw(); + + setTimeout(() => { + assert.equal(component._data.radio, '01'); + component.setValue(1); + component.redraw(); + setTimeout(() => { + assert.equal(component._data.radio, 1); + done(); + }, 200); + }, 200); + }); }); - }); - - it('Should set false as defaultValue correctly', function(done) { - Harness.testCreate(RadioComponent, comp4).then((component) => { - assert.equal(component.dataValue, false, 'Should be equal to false'); - const input = component.element.querySelector('input[value="false"]'); - assert.equal(input.getAttribute('checked'), 'true', 'Should be checked'); - done(); + + it('Span should have correct text label', function () { + return Harness.testCreate(RadioComponent, comp1).then((component) => { + component.element.querySelectorAll('input').forEach((input) => { + assert( + input.getAttribute('class').indexOf('form-check-input') !== + -1, + 'No form-check-input on radios.', + ); + }); + const spans = component.element.querySelectorAll('span'); + assert.equal(spans[0].innerHTML, 'Red'); + assert.equal(spans[1].innerHTML, 'Green'); + assert.equal(spans[2].innerHTML, 'Blue'); + assert.equal(spans[3].innerHTML, 'Yellow'); + }); }); - }); - - it('Should provide "Allow only available values" validation', function(done) { - const form = _.cloneDeep(comp5); - const element = document.createElement('div'); - - Formio.createForm(element, form).then(form => { - const radio = form.getComponent('radio'); - let value = 'five'; - radio.setValue(value); - - setTimeout(() => { - assert.equal(radio.getValue(), value); - assert.equal(radio.dataValue, value); - const submit = form.getComponent('submit'); - const clickEvent = new Event('click'); - const submitBtn = submit.refs.button; - submitBtn.dispatchEvent(clickEvent); - - setTimeout(() => { - assert.equal(form.errors.length, 1); - assert.equal(radio.error.message, 'Radio is an invalid value.'); - value = 'one'; - radio.setValue(value); - - setTimeout(() => { - assert.equal(radio.getValue(), value); - assert.equal(radio.dataValue, value); - assert.equal(form.errors.length, 0); - assert.equal(!!radio.error, false); - - document.innerHTML = ''; + + it('Should set false as defaultValue correctly', function (done) { + Harness.testCreate(RadioComponent, comp4).then((component) => { + assert.equal( + component.dataValue, + false, + 'Should be equal to false', + ); + const input = component.element.querySelector( + 'input[value="false"]', + ); + assert.equal( + input.getAttribute('checked'), + 'true', + 'Should be checked', + ); done(); - }, 300); - }, 300); - }, 200); - }).catch(done); - }); - - it('Should use whole Object as value if URL DataSrc and ValueProperty is not set', function(done) { - const form = _.cloneDeep(comp9); - delete form.components[0].valueProperty; - const element = document.createElement('div'); - const originalMakeRequest = Formio.makeRequest; - const values = [ - { name : 'Alabama', abbreviation : 'AL' }, - { name : 'Alaska', abbreviation: 'AK' } - ]; - - Formio.makeRequest = function() { - return new Promise(resolve => { - resolve(values); - }); - }; - - Formio.createForm(element, form).then(form => { - const radio = form.getComponent('radio'); - - setTimeout(()=>{ - values.forEach((value, i) => { - assert.equal(_.isEqual(value, radio.loadedOptions[i].value), true); }); - radio.setValue(values[1]); - - setTimeout(() => { - const submit = form.getComponent('submit'); - const clickEvent = new Event('click'); - const submitBtn = submit.refs.button; - submitBtn.dispatchEvent(clickEvent); - - setTimeout(() => { - assert.equal(form.errors.length, 0); - assert.equal(!!radio.error, false); - assert.equal(radio.getValue(), values[1]); - assert.equal(radio.dataValue, values[1]); - document.innerHTML = ''; - Formio.makeRequest = originalMakeRequest; - done(); - }, 300); - }, 300); - }, 200); - }).catch(done); - }); - - it('Should not have default values in schema', function(done) { - const form = _.cloneDeep(comp6); - const element = document.createElement('div'); - - const requiredSchema = { - label: 'Radio', - optionsLabelPosition: 'right', - inline: true, - tableView: false, - key: 'radio', - type: 'radio', - input: true - }; - - Formio.createForm(element, form).then(form => { - const radio = form.getComponent('radio'); - assert.deepEqual(requiredSchema, radio.schema); - done(); - }).catch(done); - }); + }); + + it('Should provide "Allow only available values" validation', function (done) { + const form = _.cloneDeep(comp5); + const element = document.createElement('div'); + + Formio.createForm(element, form) + .then((form) => { + const radio = form.getComponent('radio'); + let value = 'five'; + radio.setValue(value); + + setTimeout(() => { + assert.equal(radio.getValue(), value); + assert.equal(radio.dataValue, value); + const submit = form.getComponent('submit'); + const clickEvent = new Event('click'); + const submitBtn = submit.refs.button; + submitBtn.dispatchEvent(clickEvent); + + setTimeout(() => { + assert.equal(form.errors.length, 1); + assert.equal( + radio.error.message, + 'Radio is an invalid value.', + ); + value = 'one'; + radio.setValue(value); + + setTimeout(() => { + assert.equal(radio.getValue(), value); + assert.equal(radio.dataValue, value); + assert.equal(form.errors.length, 0); + assert.equal(!!radio.error, false); + + document.innerHTML = ''; + done(); + }, 300); + }, 300); + }, 200); + }) + .catch(done); + }); + + it('Should use whole Object as value if URL DataSrc and ValueProperty is not set', function (done) { + const form = _.cloneDeep(comp9); + delete form.components[0].valueProperty; + const element = document.createElement('div'); + const originalMakeRequest = Formio.makeRequest; + const values = [ + { name: 'Alabama', abbreviation: 'AL' }, + { name: 'Alaska', abbreviation: 'AK' }, + ]; + + Formio.makeRequest = function () { + return new Promise((resolve) => { + resolve(values); + }); + }; + + Formio.createForm(element, form) + .then((form) => { + const radio = form.getComponent('radio'); + + setTimeout(() => { + values.forEach((value, i) => { + assert.equal( + _.isEqual(value, radio.loadedOptions[i].value), + true, + ); + }); + radio.setValue(values[1]); + + setTimeout(() => { + const submit = form.getComponent('submit'); + const clickEvent = new Event('click'); + const submitBtn = submit.refs.button; + submitBtn.dispatchEvent(clickEvent); + + setTimeout(() => { + assert.equal(form.errors.length, 0); + assert.equal(!!radio.error, false); + assert.equal(radio.getValue(), values[1]); + assert.equal(radio.dataValue, values[1]); + document.innerHTML = ''; + Formio.makeRequest = originalMakeRequest; + done(); + }, 300); + }, 300); + }, 200); + }) + .catch(done); + }); + + it('Should not have default values in schema', function (done) { + const form = _.cloneDeep(comp6); + const element = document.createElement('div'); + + const requiredSchema = { + label: 'Radio', + optionsLabelPosition: 'right', + inline: true, + tableView: false, + key: 'radio', + type: 'radio', + input: true, + }; + + Formio.createForm(element, form) + .then((form) => { + const radio = form.getComponent('radio'); + assert.deepEqual(requiredSchema, radio.schema); + done(); + }) + .catch(done); + }); }); -describe('Radio Component Part Two', function() { - it('should have red asterisk left hand side to the options labels if component is required and label is hidden', function() { - return Harness.testCreate(RadioComponent, comp7).then(component => { - const options = component.element.querySelectorAll('.form-check-label'); - options.forEach(i => { - assert.deepEqual(!!getComputedStyle(i, ':before'), true); - }); +describe('Radio Component Part Two', function () { + it('should have red asterisk left hand side to the options labels if component is required and label is hidden', function () { + return Harness.testCreate(RadioComponent, comp7).then((component) => { + const options = + component.element.querySelectorAll('.form-check-label'); + options.forEach((i) => { + assert.deepEqual(!!getComputedStyle(i, ':before'), true); + }); + }); }); - }); - - it('Should not provide empty error message when hidden radio has storage type as string', function(done) { - const form = _.cloneDeep(comp8); - const element = document.createElement('div'); - - Formio.createForm(element, form) - .then(form => { - form.submission = { - data: { - radio: 'no' - } + + it('Should not provide empty error message when hidden radio has storage type as string', function (done) { + const form = _.cloneDeep(comp8); + const element = document.createElement('div'); + + Formio.createForm(element, form) + .then((form) => { + form.submission = { + data: { + radio: 'no', + }, + }; + const alerts = document.querySelectorAll('.alert-danger'); + setTimeout(() => { + assert.equal(alerts.length, 0); + done(); + }, 100); + }) + .catch(done); + }); + + it('Should show correct attributes during performance', function (done) { + const formElement = document.createElement('div'); + + const JSON = { + components: [ + { + key: 'whichOneIsYourFavoriteFruit', + type: 'radio', + input: true, + label: 'Which one is your favorite fruit?', + inline: false, + values: [ + { + label: 'Apple ', + value: 'apple', + shortcut: '', + }, + { + label: 'Orange', + value: 'orange', + shortcut: '', + }, + { + label: 'Banana', + value: 'banana', + shortcut: '', + }, + ], + tableView: false, + optionsLabelPosition: 'right', + }, + ], }; - const alerts = document.querySelectorAll('.alert-danger'); - setTimeout(() => { - assert.equal(alerts.length, 0); - done(); - }, 100); - }) - .catch(done); - }); - - it('Should show correct attributes during performance', function(done) { - const formElement = document.createElement('div'); - - const JSON = { - components: [ - { - key: 'whichOneIsYourFavoriteFruit', - type: 'radio', - input: true, - label: 'Which one is your favorite fruit?', - inline: false, - values: [ - { - label: 'Apple ', - value: 'apple', - shortcut: '', - }, - { - label: 'Orange', - value: 'orange', - shortcut: '', - }, - { - label: 'Banana', - value: 'banana', - shortcut: '', - }, - ], - tableView: false, - optionsLabelPosition: 'right', - }, - ], - }; - - Formio.createForm(formElement, JSON) - .then((form) => { - const component = form.getComponent('whichOneIsYourFavoriteFruit'); - - const appleRadioInput = component.refs.input[0]; - const appleComponentWrapper = formElement.querySelector('.form-check'); - const isContainClass = - appleComponentWrapper.classList.contains('radio-selected'); - - assert.equal( - appleRadioInput.checked, - false, - 'should be false by default' - ); - assert.equal(isContainClass, false, 'should be false by default'); - - appleRadioInput.click(); - - setTimeout(() => { - assert.equal(appleRadioInput.checked, true); - - const elementWrapper = formElement.querySelector('.form-check'); - const isContainClass = - elementWrapper.classList.contains('radio-selected'); - assert.equal(isContainClass, true); - - appleRadioInput.click(); - - setTimeout(() => { - assert.equal(appleRadioInput.checked, false); - const elementWrapper = formElement.querySelector('.form-check'); - const isContainClass = - elementWrapper.classList.contains('radio-selected'); - assert.equal(isContainClass, false); - done(); - }, 200); - }, 200); - }) - .catch(done); - }); + Formio.createForm(formElement, JSON) + .then((form) => { + const component = form.getComponent( + 'whichOneIsYourFavoriteFruit', + ); + + const appleRadioInput = component.refs.input[0]; + const appleComponentWrapper = + formElement.querySelector('.form-check'); + const isContainClass = + appleComponentWrapper.classList.contains('radio-selected'); + + assert.equal( + appleRadioInput.checked, + false, + 'should be false by default', + ); + assert.equal( + isContainClass, + false, + 'should be false by default', + ); + + appleRadioInput.click(); + + setTimeout(() => { + assert.equal(appleRadioInput.checked, true); + + const elementWrapper = + formElement.querySelector('.form-check'); + const isContainClass = + elementWrapper.classList.contains('radio-selected'); + assert.equal(isContainClass, true); + + appleRadioInput.click(); + + setTimeout(() => { + assert.equal(appleRadioInput.checked, false); + const elementWrapper = + formElement.querySelector('.form-check'); + const isContainClass = + elementWrapper.classList.contains('radio-selected'); + assert.equal(isContainClass, false); + + done(); + }, 200); + }, 200); + }) + .catch(done); + }); }); diff --git a/src/components/radio/editForm/Radio.edit.data.js b/src/components/radio/editForm/Radio.edit.data.js index f46f60aa22..98319b182b 100644 --- a/src/components/radio/editForm/Radio.edit.data.js +++ b/src/components/radio/editForm/Radio.edit.data.js @@ -2,83 +2,93 @@ import BuilderUtils from '../../../utils/builder'; import _ from 'lodash'; export default [ - { - key: 'multiple', - ignore: true, - }, - { - key: 'dataSrc', - data: { - values: [ - { label: 'Values', value: 'values' }, - { label: 'URL', value: 'url' }, - ], + { + key: 'multiple', + ignore: true, }, - 'validate': { - 'required': true - }, - onChange(context) { - if (context && context.flags && context.flags && context.flags.modified) { - context.data.values = [{ label: '', value: '' }]; - } - }, - }, - { - type: 'datagrid', - input: true, - label: 'Values', - key: 'values', - tooltip: 'The radio button values that can be picked for this field. Values are text submitted with the form data. Labels are text that appears next to the radio buttons on the form.', - weight: 10, - reorder: true, - defaultValue: [{ label: '', value: '' }], - components: [ - { - label: 'Label', - key: 'label', - input: true, - type: 'textfield', - }, - { - label: 'Value', - key: 'value', - input: true, - type: 'textfield', - allowCalculateOverride: true, - calculateValue: 'value = _.camelCase(row.label);', + { + key: 'dataSrc', + data: { + values: [ + { label: 'Values', value: 'values' }, + { label: 'URL', value: 'url' }, + ], + }, validate: { - required: true - } - }, - { - type: 'select', + required: true, + }, + onChange(context) { + if ( + context && + context.flags && + context.flags && + context.flags.modified + ) { + context.data.values = [{ label: '', value: '' }]; + } + }, + }, + { + type: 'datagrid', input: true, - weight: 180, - label: 'Shortcut', - key: 'shortcut', - tooltip: 'The shortcut key for this option.', - dataSrc: 'custom', - valueProperty: 'value', - customDefaultValue: () => '', - template: '{{ item.label }}', - data: { - custom(context) { - return BuilderUtils.getAvailableShortcuts( - _.get(context, 'instance.options.editForm', {}), - _.get(context, 'instance.options.editComponent', {}) - ); - }, + label: 'Values', + key: 'values', + tooltip: + 'The radio button values that can be picked for this field. Values are text submitted with the form data. Labels are text that appears next to the radio buttons on the form.', + weight: 10, + reorder: true, + defaultValue: [{ label: '', value: '' }], + components: [ + { + label: 'Label', + key: 'label', + input: true, + type: 'textfield', + }, + { + label: 'Value', + key: 'value', + input: true, + type: 'textfield', + allowCalculateOverride: true, + calculateValue: 'value = _.camelCase(row.label);', + validate: { + required: true, + }, + }, + { + type: 'select', + input: true, + weight: 180, + label: 'Shortcut', + key: 'shortcut', + tooltip: 'The shortcut key for this option.', + dataSrc: 'custom', + valueProperty: 'value', + customDefaultValue: () => '', + template: '{{ item.label }}', + data: { + custom(context) { + return BuilderUtils.getAvailableShortcuts( + _.get(context, 'instance.options.editForm', {}), + _.get( + context, + 'instance.options.editComponent', + {}, + ), + ); + }, + }, + }, + ], + conditional: { + json: { '===': [{ var: 'data.dataSrc' }, 'values'] }, }, - }, - ], - conditional: { - json: { '===': [{ var: 'data.dataSrc' }, 'values'] }, }, - }, - { - key: 'template', - conditional: { - json: { '===': [{ var: 'data.dataSrc' }, 'url'] }, + { + key: 'template', + conditional: { + json: { '===': [{ var: 'data.dataSrc' }, 'url'] }, + }, }, - } ]; diff --git a/src/components/radio/editForm/Radio.edit.display.js b/src/components/radio/editForm/Radio.edit.display.js index f540db7a49..08fa72dd5e 100644 --- a/src/components/radio/editForm/Radio.edit.display.js +++ b/src/components/radio/editForm/Radio.edit.display.js @@ -1,32 +1,32 @@ export default [ - { - key: 'placeholder', - ignore: true - }, - { - type: 'select', - input: true, - label: 'Options Label Position', - key: 'optionsLabelPosition', - tooltip: 'Position for the label for options for this field.', - dataSrc: 'values', - weight: 32, - defaultValue: 'right', - data: { - values: [ - { label: 'Top', value: 'top' }, - { label: 'Left', value: 'left' }, - { label: 'Right', value: 'right' }, - { label: 'Bottom', value: 'bottom' } - ] - } - }, - { - type: 'checkbox', - input: true, - key: 'inline', - label: 'Inline Layout', - tooltip: 'Displays the checkboxes/radios horizontally.', - weight: 650 - } + { + key: 'placeholder', + ignore: true, + }, + { + type: 'select', + input: true, + label: 'Options Label Position', + key: 'optionsLabelPosition', + tooltip: 'Position for the label for options for this field.', + dataSrc: 'values', + weight: 32, + defaultValue: 'right', + data: { + values: [ + { label: 'Top', value: 'top' }, + { label: 'Left', value: 'left' }, + { label: 'Right', value: 'right' }, + { label: 'Bottom', value: 'bottom' }, + ], + }, + }, + { + type: 'checkbox', + input: true, + key: 'inline', + label: 'Inline Layout', + tooltip: 'Displays the checkboxes/radios horizontally.', + weight: 650, + }, ]; diff --git a/src/components/radio/editForm/Radio.edit.validation.js b/src/components/radio/editForm/Radio.edit.validation.js index 64be4ae030..acd5c786a0 100644 --- a/src/components/radio/editForm/Radio.edit.validation.js +++ b/src/components/radio/editForm/Radio.edit.validation.js @@ -1,18 +1,19 @@ export default [ - { - key: 'validateOn', - ignore: true - }, - { - key: 'unique', - ignore: true - }, - { - weight: 52, - type: 'checkbox', - label: 'Allow only available values', - tooltip: 'Check this if you would like to perform a validation check to ensure the selected value is an available option.', - key: 'validate.onlyAvailableItems', - input: true, - }, + { + key: 'validateOn', + ignore: true, + }, + { + key: 'unique', + ignore: true, + }, + { + weight: 52, + type: 'checkbox', + label: 'Allow only available values', + tooltip: + 'Check this if you would like to perform a validation check to ensure the selected value is an available option.', + key: 'validate.onlyAvailableItems', + input: true, + }, ]; diff --git a/src/components/radio/fixtures/comp1.js b/src/components/radio/fixtures/comp1.js index dbdfd924c6..300ff5c15f 100644 --- a/src/components/radio/fixtures/comp1.js +++ b/src/components/radio/fixtures/comp1.js @@ -1,42 +1,40 @@ export default { - 'conditional': { - 'eq': '', - 'when': null, - 'show': '' - }, - 'tags': [ - - ], - 'type': 'radio', - 'validate': { - 'customPrivate': false, - 'custom': '', - 'required': false - }, - 'persistent': true, - 'protected': false, - 'defaultValue': '', - 'values': [ - { - 'label': 'Red', - 'value': 'red' + conditional: { + eq: '', + when: null, + show: '', }, - { - 'label': 'Green', - 'value': 'green' + tags: [], + type: 'radio', + validate: { + customPrivate: false, + custom: '', + required: false, }, - { - 'label': 'Blue', - 'value': 'blue' - }, - { - 'label': 'Yellow', - 'value': 'yellow' - } - ], - 'key': 'favoriteColor', - 'label': 'Favorite Color', - 'inputType': 'radio', - 'tableView': true, - 'input': true + persistent: true, + protected: false, + defaultValue: '', + values: [ + { + label: 'Red', + value: 'red', + }, + { + label: 'Green', + value: 'green', + }, + { + label: 'Blue', + value: 'blue', + }, + { + label: 'Yellow', + value: 'yellow', + }, + ], + key: 'favoriteColor', + label: 'Favorite Color', + inputType: 'radio', + tableView: true, + input: true, }; diff --git a/src/components/radio/fixtures/comp10.js b/src/components/radio/fixtures/comp10.js index 8ff3642242..545f81d329 100644 --- a/src/components/radio/fixtures/comp10.js +++ b/src/components/radio/fixtures/comp10.js @@ -1,21 +1,21 @@ export default { - 'label': 'Radio', - 'optionsLabelPosition': 'right', - 'inline': false, - 'tableView': false, - 'values': [ + label: 'Radio', + optionsLabelPosition: 'right', + inline: false, + tableView: false, + values: [ { - 'label': '01', - 'value': '01', - 'shortcut': '' + label: '01', + value: '01', + shortcut: '', }, { - 'label': '1', - 'value': '1', - 'shortcut': '' - } + label: '1', + value: '1', + shortcut: '', + }, ], - 'key': 'radio', - 'type': 'radio', - 'input': true + key: 'radio', + type: 'radio', + input: true, }; diff --git a/src/components/radio/fixtures/comp2.js b/src/components/radio/fixtures/comp2.js index 582bf20161..e3eca50867 100644 --- a/src/components/radio/fixtures/comp2.js +++ b/src/components/radio/fixtures/comp2.js @@ -1,84 +1,87 @@ export default { - 'label': 'Radio Test', - 'labelPosition': 'top', - 'optionsLabelPosition': 'right', - 'description': '', - 'tooltip': '', - 'customClass': '', - 'tabindex': '', - 'inline': false, - 'hidden': false, - 'hideLabel': false, - 'autofocus': false, - 'disabled': false, - 'tableView': true, - 'modalEdit': false, - 'values': [{ - 'label': 'one', - 'value': '1', - 'shortcut': '' - }, { - 'label': 'two', - 'value': '2', - 'shortcut': '' - }], - 'dataType': 'number', - 'persistent': true, - 'protected': false, - 'dbIndex': false, - 'encrypted': false, - 'redrawOn': '', - 'clearOnHide': true, - 'customDefaultValue': '', - 'calculateValue': '', - 'calculateServer': false, - 'allowCalculateOverride': false, - 'validate': { - 'required': false, - 'customMessage': '', - 'custom': '', - 'customPrivate': false, - 'json': '', - 'strictDateValidation': false, - 'multiple': false, - 'unique': false + label: 'Radio Test', + labelPosition: 'top', + optionsLabelPosition: 'right', + description: '', + tooltip: '', + customClass: '', + tabindex: '', + inline: false, + hidden: false, + hideLabel: false, + autofocus: false, + disabled: false, + tableView: true, + modalEdit: false, + values: [ + { + label: 'one', + value: '1', + shortcut: '', + }, + { + label: 'two', + value: '2', + shortcut: '', + }, + ], + dataType: 'number', + persistent: true, + protected: false, + dbIndex: false, + encrypted: false, + redrawOn: '', + clearOnHide: true, + customDefaultValue: '', + calculateValue: '', + calculateServer: false, + allowCalculateOverride: false, + validate: { + required: false, + customMessage: '', + custom: '', + customPrivate: false, + json: '', + strictDateValidation: false, + multiple: false, + unique: false, }, - 'errorLabel': '', - 'key': 'radioTest', - 'tags': [], - 'properties': {}, - 'conditional': { - 'show': null, - 'when': null, - 'eq': '', - 'json': '' + errorLabel: '', + key: 'radioTest', + tags: [], + properties: {}, + conditional: { + show: null, + when: null, + eq: '', + json: '', }, - 'customConditional': '', - 'logic': [], - 'attributes': {}, - 'overlay': { - 'style': '', - 'page': '', - 'left': '', - 'top': '', - 'width': '', - 'height': '' + customConditional: '', + logic: [], + attributes: {}, + overlay: { + style: '', + page: '', + left: '', + top: '', + width: '', + height: '', }, - 'type': 'radio', - 'input': true, - 'placeholder': '', - 'prefix': '', - 'suffix': '', - 'multiple': false, - 'unique': false, - 'refreshOn': '', - 'widget': null, - 'validateOn': 'change', - 'showCharCount': false, - 'showWordCount': false, - 'allowMultipleMasks': false, - 'inputType': 'radio', - 'fieldSet': false, - 'id': 'ed0srhn', - 'defaultValue': '' - }; + type: 'radio', + input: true, + placeholder: '', + prefix: '', + suffix: '', + multiple: false, + unique: false, + refreshOn: '', + widget: null, + validateOn: 'change', + showCharCount: false, + showWordCount: false, + allowMultipleMasks: false, + inputType: 'radio', + fieldSet: false, + id: 'ed0srhn', + defaultValue: '', +}; diff --git a/src/components/radio/fixtures/comp3.js b/src/components/radio/fixtures/comp3.js index 59f4692d60..4027826351 100644 --- a/src/components/radio/fixtures/comp3.js +++ b/src/components/radio/fixtures/comp3.js @@ -1,88 +1,92 @@ export default { - 'label': 'Number ST Radio', - 'labelPosition': 'top', - 'optionsLabelPosition': 'right', - 'description': '', - 'tooltip': '', - 'customClass': '', - 'tabindex': '', - 'inline': false, - 'hidden': false, - 'hideLabel': false, - 'autofocus': false, - 'disabled': false, - 'tableView': false, - 'modalEdit': false, - 'values': [{ - 'label': '11', - 'value': '11', - 'shortcut': '' - }, { - 'label': '22', - 'value': '22', - 'shortcut': '' - }, { - 'label': '33', - 'value': '33', - 'shortcut': '' - }], - 'dataType': 'number', - 'persistent': true, - 'protected': false, - 'dbIndex': false, - 'encrypted': false, - 'redrawOn': '', - 'clearOnHide': true, - 'customDefaultValue': '', - 'calculateValue': '', - 'calculateServer': false, - 'allowCalculateOverride': false, - 'validate': { - 'required': false, - 'customMessage': '', - 'custom': '', - 'customPrivate': false, - 'json': '', - 'strictDateValidation': false, - 'multiple': false, - 'unique': false - }, - 'errorLabel': '', - 'key': 'numberStRadio', - 'tags': [], - 'properties': {}, - 'conditional': { - 'show': null, - 'when': null, - 'eq': '', - 'json': '' - }, - 'customConditional': '', - 'logic': [], - 'attributes': {}, - 'overlay': { - 'style': '', - 'page': '', - 'left': '', - 'top': '', - 'width': '', - 'height': '' - }, - 'type': 'radio', - 'input': true, - 'placeholder': '', - 'prefix': '', - 'suffix': '', - 'multiple': false, - 'unique': false, - 'refreshOn': '', - 'widget': null, - 'validateOn': 'change', - 'showCharCount': false, - 'showWordCount': false, - 'allowMultipleMasks': false, - 'inputType': 'radio', - 'fieldSet': false, - 'id': 'er5lmse', - 'defaultValue': '' + label: 'Number ST Radio', + labelPosition: 'top', + optionsLabelPosition: 'right', + description: '', + tooltip: '', + customClass: '', + tabindex: '', + inline: false, + hidden: false, + hideLabel: false, + autofocus: false, + disabled: false, + tableView: false, + modalEdit: false, + values: [ + { + label: '11', + value: '11', + shortcut: '', + }, + { + label: '22', + value: '22', + shortcut: '', + }, + { + label: '33', + value: '33', + shortcut: '', + }, + ], + dataType: 'number', + persistent: true, + protected: false, + dbIndex: false, + encrypted: false, + redrawOn: '', + clearOnHide: true, + customDefaultValue: '', + calculateValue: '', + calculateServer: false, + allowCalculateOverride: false, + validate: { + required: false, + customMessage: '', + custom: '', + customPrivate: false, + json: '', + strictDateValidation: false, + multiple: false, + unique: false, + }, + errorLabel: '', + key: 'numberStRadio', + tags: [], + properties: {}, + conditional: { + show: null, + when: null, + eq: '', + json: '', + }, + customConditional: '', + logic: [], + attributes: {}, + overlay: { + style: '', + page: '', + left: '', + top: '', + width: '', + height: '', + }, + type: 'radio', + input: true, + placeholder: '', + prefix: '', + suffix: '', + multiple: false, + unique: false, + refreshOn: '', + widget: null, + validateOn: 'change', + showCharCount: false, + showWordCount: false, + allowMultipleMasks: false, + inputType: 'radio', + fieldSet: false, + id: 'er5lmse', + defaultValue: '', }; diff --git a/src/components/radio/fixtures/comp4.js b/src/components/radio/fixtures/comp4.js index dc9d1cdd68..c4fa01244b 100644 --- a/src/components/radio/fixtures/comp4.js +++ b/src/components/radio/fixtures/comp4.js @@ -1,25 +1,25 @@ export default { - label: 'Radio', - optionsLabelPosition: 'right', - inline: false, - tableView: false, - defaultValue: false, - values: [ - { - label: 'Yes', - value: 'true', - shortcut: '' + label: 'Radio', + optionsLabelPosition: 'right', + inline: false, + tableView: false, + defaultValue: false, + values: [ + { + label: 'Yes', + value: 'true', + shortcut: '', + }, + { + label: 'No', + value: 'false', + shortcut: '', + }, + ], + validate: { + onlyAvailableItems: false, }, - { - label: 'No', - value: 'false', - shortcut: '' - } - ], - validate: { - onlyAvailableItems: false - }, - key: 'radio1', - type: 'radio', - input: true + key: 'radio1', + type: 'radio', + input: true, }; diff --git a/src/components/radio/fixtures/comp5.js b/src/components/radio/fixtures/comp5.js index a725f2d698..ad5d8d3e77 100644 --- a/src/components/radio/fixtures/comp5.js +++ b/src/components/radio/fixtures/comp5.js @@ -1,33 +1,33 @@ export default { - type: 'form', - components: [ - { - label: 'Radio', - optionsLabelPosition: 'right', - inline: true, - tableView: false, - values: [ - { label: 'one', value: 'one', shortcut: '' }, - { label: 'two', value: 'two', shortcut: '' }, - { label: 'three', value: 'three', shortcut: '' } - ], - validate: { onlyAvailableItems: true }, - key: 'radio', - type: 'radio', - input: true - }, - { - label: 'Submit', - showValidations: false, - alwaysEnabled: false, - tableView: false, - key: 'submit', - type: 'button', - input: true - } - ], - title: 'test testrer', - display: 'form', - name: 'testTestrer', - path: 'testtestrer', + type: 'form', + components: [ + { + label: 'Radio', + optionsLabelPosition: 'right', + inline: true, + tableView: false, + values: [ + { label: 'one', value: 'one', shortcut: '' }, + { label: 'two', value: 'two', shortcut: '' }, + { label: 'three', value: 'three', shortcut: '' }, + ], + validate: { onlyAvailableItems: true }, + key: 'radio', + type: 'radio', + input: true, + }, + { + label: 'Submit', + showValidations: false, + alwaysEnabled: false, + tableView: false, + key: 'submit', + type: 'button', + input: true, + }, + ], + title: 'test testrer', + display: 'form', + name: 'testTestrer', + path: 'testtestrer', }; diff --git a/src/components/radio/fixtures/comp6.js b/src/components/radio/fixtures/comp6.js index 50df750dda..ab96a55cf9 100644 --- a/src/components/radio/fixtures/comp6.js +++ b/src/components/radio/fixtures/comp6.js @@ -1,31 +1,29 @@ export default { - type: 'form', - components: [ - { - label: 'Radio', - optionsLabelPosition: 'right', - inline: true, - tableView: false, - values: [ - { label: '', value: '' }, - ], - validate: { onlyAvailableItems: false }, - key: 'radio', - type: 'radio', - input: true - }, - { - label: 'Submit', - showValidations: false, - alwaysEnabled: false, - tableView: false, - key: 'submit', - type: 'button', - input: true - } - ], - title: 'test testrer', - display: 'form', - name: 'testTestrer', - path: 'testtestrer', + type: 'form', + components: [ + { + label: 'Radio', + optionsLabelPosition: 'right', + inline: true, + tableView: false, + values: [{ label: '', value: '' }], + validate: { onlyAvailableItems: false }, + key: 'radio', + type: 'radio', + input: true, + }, + { + label: 'Submit', + showValidations: false, + alwaysEnabled: false, + tableView: false, + key: 'submit', + type: 'button', + input: true, + }, + ], + title: 'test testrer', + display: 'form', + name: 'testTestrer', + path: 'testtestrer', }; diff --git a/src/components/radio/fixtures/comp7.js b/src/components/radio/fixtures/comp7.js index 1e79240326..6d18c95ae1 100644 --- a/src/components/radio/fixtures/comp7.js +++ b/src/components/radio/fixtures/comp7.js @@ -1,19 +1,19 @@ export default { - _id: '61135ec3e4188f021c7ad390', - label: 'Label hidden', - optionsLabelPosition: 'right', - inline: false, - hideLabel: true, - tableView: false, - values: [ - { label: '3', value: '3', shortcut: '' }, - { label: '4', value: '4', shortcut: '' }, - ], - validate: { required: true }, - key: 'labelHidden', - type: 'radio', - input: true, - title: 'FIO-2959', - name: 'fio2959', - path: 'fio2959', + _id: '61135ec3e4188f021c7ad390', + label: 'Label hidden', + optionsLabelPosition: 'right', + inline: false, + hideLabel: true, + tableView: false, + values: [ + { label: '3', value: '3', shortcut: '' }, + { label: '4', value: '4', shortcut: '' }, + ], + validate: { required: true }, + key: 'labelHidden', + type: 'radio', + input: true, + title: 'FIO-2959', + name: 'fio2959', + path: 'fio2959', }; diff --git a/src/components/radio/fixtures/comp8.js b/src/components/radio/fixtures/comp8.js index 9740ad9784..037564b149 100644 --- a/src/components/radio/fixtures/comp8.js +++ b/src/components/radio/fixtures/comp8.js @@ -1,111 +1,111 @@ export default { - title: '4405', - name: '4405', - type: 'form', - display: 'form', - components: [ - { - label: 'HTML', - attrs: [{ attr: '', value: '' }], - content: '

    Assessment Checklist

    ', - refreshOnChange: false, - type: 'htmlelement', - input: false, - key: 'html', - tableView: false, - }, - { - type: 'button', - label: 'Submit', - key: 'submit', - disableOnInvalid: true, - input: true, - tableView: false, - }, - { - title: 'Panel1', - theme: 'primary', - collapsible: false, - type: 'panel', - label: 'Panel', - input: false, - key: 'panel', - tableView: false, - components: [ + title: '4405', + name: '4405', + type: 'form', + display: 'form', + components: [ { - label: 'Radio', - optionsLabelPosition: 'right', - inline: true, - dataType: 'string', - values: [ - { label: 'Yes', value: 'yes', shortcut: '' }, - { label: 'No', value: 'no', shortcut: '' }, - ], - type: 'radio', - input: true, - key: 'radio', - tableView: false, + label: 'HTML', + attrs: [{ attr: '', value: '' }], + content: '

    Assessment Checklist

    ', + refreshOnChange: false, + type: 'htmlelement', + input: false, + key: 'html', + tableView: false, }, - ], - }, - { - title: 'Panel2', - theme: 'primary', - collapsible: false, - conditional: { show: false, when: 'radio', eq: 'no' }, - type: 'panel', - label: 'Panel2', - input: false, - key: 'panel1', - tableView: false, - components: [ { - label: 'Text Area', - isEditor: false, - autoExpand: false, - type: 'textarea', - input: true, - key: 'textArea', - tableView: true, + type: 'button', + label: 'Submit', + key: 'submit', + disableOnInvalid: true, + input: true, + tableView: false, }, - ], - }, - { - title: 'Panel3', - theme: 'primary', - collapsible: false, - conditional: { show: false, when: 'radio', eq: 'no' }, - type: 'panel', - label: 'Panel3', - input: false, - key: 'panel2', - tableView: false, - components: [ { - label: 'Radio', - optionsLabelPosition: 'right', - inline: false, - tableView: false, - values: [ - { label: 'Yes', value: 'yes', shortcut: '' }, - { label: 'No', value: 'no', shortcut: '' }, - ], - dataType: 'string', - key: 'radio2', - type: 'radio', - input: true, + title: 'Panel1', + theme: 'primary', + collapsible: false, + type: 'panel', + label: 'Panel', + input: false, + key: 'panel', + tableView: false, + components: [ + { + label: 'Radio', + optionsLabelPosition: 'right', + inline: true, + dataType: 'string', + values: [ + { label: 'Yes', value: 'yes', shortcut: '' }, + { label: 'No', value: 'no', shortcut: '' }, + ], + type: 'radio', + input: true, + key: 'radio', + tableView: false, + }, + ], }, { - label: 'Text Area', - isEditor: false, - autoExpand: false, - type: 'textarea', - input: true, - key: 'textArea1', - tableView: true, + title: 'Panel2', + theme: 'primary', + collapsible: false, + conditional: { show: false, when: 'radio', eq: 'no' }, + type: 'panel', + label: 'Panel2', + input: false, + key: 'panel1', + tableView: false, + components: [ + { + label: 'Text Area', + isEditor: false, + autoExpand: false, + type: 'textarea', + input: true, + key: 'textArea', + tableView: true, + }, + ], }, - ], - }, - ], - project: '61547bb9b027c7a861d855b6', + { + title: 'Panel3', + theme: 'primary', + collapsible: false, + conditional: { show: false, when: 'radio', eq: 'no' }, + type: 'panel', + label: 'Panel3', + input: false, + key: 'panel2', + tableView: false, + components: [ + { + label: 'Radio', + optionsLabelPosition: 'right', + inline: false, + tableView: false, + values: [ + { label: 'Yes', value: 'yes', shortcut: '' }, + { label: 'No', value: 'no', shortcut: '' }, + ], + dataType: 'string', + key: 'radio2', + type: 'radio', + input: true, + }, + { + label: 'Text Area', + isEditor: false, + autoExpand: false, + type: 'textarea', + input: true, + key: 'textArea1', + tableView: true, + }, + ], + }, + ], + project: '61547bb9b027c7a861d855b6', }; diff --git a/src/components/radio/fixtures/comp9.js b/src/components/radio/fixtures/comp9.js index 4c215868e5..0b11436c8c 100644 --- a/src/components/radio/fixtures/comp9.js +++ b/src/components/radio/fixtures/comp9.js @@ -7,24 +7,24 @@ export default { key: 'radio', dataSrc: 'url', data: { - url: 'https://cdn.rawgit.com/mshafrir/2646763/raw/states_titlecase.json' + url: 'https://cdn.rawgit.com/mshafrir/2646763/raw/states_titlecase.json', }, valueProperty: 'abbreviation', template: '{{ item.name }}', - input: true + input: true, }, { - label: 'Submit', - showValidations: false, - alwaysEnabled: false, - tableView: false, - key: 'submit', - type: 'button', - input: true - } + label: 'Submit', + showValidations: false, + alwaysEnabled: false, + tableView: false, + key: 'submit', + type: 'button', + input: true, + }, ], title: 'test radio Url', - display: 'form', - name: 'testRadioUrl', - path: 'testRadioUrl', + display: 'form', + name: 'testRadioUrl', + path: 'testRadioUrl', }; diff --git a/src/components/radio/fixtures/index.js b/src/components/radio/fixtures/index.js index 9db3870977..138ce09398 100644 --- a/src/components/radio/fixtures/index.js +++ b/src/components/radio/fixtures/index.js @@ -8,4 +8,15 @@ import comp7 from './comp7'; import comp8 from './comp8'; import comp9 from './comp9'; import comp10 from './comp10'; -export { comp1, comp2, comp3, comp4, comp5, comp6, comp7, comp8, comp9, comp10 }; +export { + comp1, + comp2, + comp3, + comp4, + comp5, + comp6, + comp7, + comp8, + comp9, + comp10, +}; diff --git a/src/components/radio/fixtures/values.js b/src/components/radio/fixtures/values.js index cd49a6103c..4289713807 100644 --- a/src/components/radio/fixtures/values.js +++ b/src/components/radio/fixtures/values.js @@ -1,6 +1 @@ -export default [ - 'one', - 'two', - false, -]; - +export default ['one', 'two', false]; diff --git a/src/components/recaptcha/ReCaptcha.form.js b/src/components/recaptcha/ReCaptcha.form.js index 6171a71043..d7e318bc82 100644 --- a/src/components/recaptcha/ReCaptcha.form.js +++ b/src/components/recaptcha/ReCaptcha.form.js @@ -1,27 +1,27 @@ import Components from '../Components'; import ReCaptchaEditDisplay from './editForm/ReCaptcha.edit.display'; -export default function() { - return Components.baseEditForm([ - { - key: 'display', - components: ReCaptchaEditDisplay - }, - { - key: 'data', - ignore: true - }, - { - key: 'validation', - ignore: true - }, - { - key: 'conditional', - ignore: true - }, - { - key: 'logic', - ignore: true - }, - ]); +export default function () { + return Components.baseEditForm([ + { + key: 'display', + components: ReCaptchaEditDisplay, + }, + { + key: 'data', + ignore: true, + }, + { + key: 'validation', + ignore: true, + }, + { + key: 'conditional', + ignore: true, + }, + { + key: 'logic', + ignore: true, + }, + ]); } diff --git a/src/components/recaptcha/ReCaptcha.js b/src/components/recaptcha/ReCaptcha.js index bf862d68bc..4c9036389c 100644 --- a/src/components/recaptcha/ReCaptcha.js +++ b/src/components/recaptcha/ReCaptcha.js @@ -5,143 +5,168 @@ import _get from 'lodash/get'; import _debounce from 'lodash/debounce'; export default class ReCaptchaComponent extends Component { - static schema(...extend) { - return Component.schema({ - type: 'recaptcha', - key: 'recaptcha', - label: 'reCAPTCHA' - }, ...extend); - } - - static get builderInfo() { - return { - title: 'reCAPTCHA', - group: 'premium', - icon: 'refresh', - documentation: '/userguide/form-building/premium-components#recaptcha', - weight: 40, - schema: ReCaptchaComponent.schema() - }; - } - - static savedValueTypes() { - return []; - } - - render() { - this.recaptchaResult = null; - if (this.builderMode) { - return super.render('reCAPTCHA'); + static schema(...extend) { + return Component.schema( + { + type: 'recaptcha', + key: 'recaptcha', + label: 'reCAPTCHA', + }, + ...extend, + ); } - else { - return super.render('', true); - } - } - createInput() { - if (this.builderMode) { - // We need to see it in builder mode. - this.append(this.text(this.name)); + static get builderInfo() { + return { + title: 'reCAPTCHA', + group: 'premium', + icon: 'refresh', + documentation: + '/userguide/form-building/premium-components#recaptcha', + weight: 40, + schema: ReCaptchaComponent.schema(), + }; } - else { - const siteKey = _get(this.root.form, 'settings.recaptcha.siteKey'); - if (siteKey) { - const recaptchaApiScriptUrl = `https://www.google.com/recaptcha/api.js?render=${siteKey}`; - this.recaptchaApiReady = Formio.requireLibrary('googleRecaptcha', 'grecaptcha', recaptchaApiScriptUrl, true); - } - else { - console.warn('There is no Site Key specified in settings in form JSON'); - } + + static savedValueTypes() { + return []; } - } - createLabel() { - return; - } + render() { + this.recaptchaResult = null; + if (this.builderMode) { + return super.render('reCAPTCHA'); + } else { + return super.render('', true); + } + } - get skipInEmail() { - return true; - } + createInput() { + if (this.builderMode) { + // We need to see it in builder mode. + this.append(this.text(this.name)); + } else { + const siteKey = _get(this.root.form, 'settings.recaptcha.siteKey'); + if (siteKey) { + const recaptchaApiScriptUrl = `https://www.google.com/recaptcha/api.js?render=${siteKey}`; + this.recaptchaApiReady = Formio.requireLibrary( + 'googleRecaptcha', + 'grecaptcha', + recaptchaApiScriptUrl, + true, + ); + } else { + console.warn( + 'There is no Site Key specified in settings in form JSON', + ); + } + } + } - async verify(actionName) { - const siteKey = _get(this.root.form, 'settings.recaptcha.siteKey'); - if (!siteKey) { - console.warn('There is no Site Key specified in settings in form JSON'); - return; + createLabel() { + return; } - if (!this.recaptchaApiReady) { - const recaptchaApiScriptUrl = `https://www.google.com/recaptcha/api.js?render=${_get(this.root.form, 'settings.recaptcha.siteKey')}`; - this.recaptchaApiReady = Formio.requireLibrary('googleRecaptcha', 'grecaptcha', recaptchaApiScriptUrl, true); + + get skipInEmail() { + return true; } - try { - await this.recaptchaApiReady; - this.recaptchaVerifiedPromise = new Promise((resolve, reject) => { - if (!this.isLoading) { - this.isLoading= true; - grecaptcha.ready(_debounce(async() => { - try { - const token = await grecaptcha.execute(siteKey, { action: actionName }); - const verificationResult = await this.sendVerificationRequest(token); - this.recaptchaResult = { - ...verificationResult, - token, - }; - this.updateValue(this.recaptchaResult); - this.isLoading = false; - return resolve(verificationResult); - } - catch (err) { - this.isLoading = false; - reject(err); - } - }, 1000)); + + async verify(actionName) { + const siteKey = _get(this.root.form, 'settings.recaptcha.siteKey'); + if (!siteKey) { + console.warn( + 'There is no Site Key specified in settings in form JSON', + ); + return; + } + if (!this.recaptchaApiReady) { + const recaptchaApiScriptUrl = `https://www.google.com/recaptcha/api.js?render=${_get( + this.root.form, + 'settings.recaptcha.siteKey', + )}`; + this.recaptchaApiReady = Formio.requireLibrary( + 'googleRecaptcha', + 'grecaptcha', + recaptchaApiScriptUrl, + true, + ); + } + try { + await this.recaptchaApiReady; + this.recaptchaVerifiedPromise = new Promise((resolve, reject) => { + if (!this.isLoading) { + this.isLoading = true; + grecaptcha.ready( + _debounce(async () => { + try { + const token = await grecaptcha.execute( + siteKey, + { action: actionName }, + ); + const verificationResult = + await this.sendVerificationRequest(token); + this.recaptchaResult = { + ...verificationResult, + token, + }; + this.updateValue(this.recaptchaResult); + this.isLoading = false; + return resolve(verificationResult); + } catch (err) { + this.isLoading = false; + reject(err); + } + }, 1000), + ); + } + }); + } catch (err) { + this.loading = false; } - }); } - catch (err) { - this.loading = false; + + beforeSubmit() { + if (this.recaptchaVerifiedPromise) { + return this.recaptchaVerifiedPromise.then(() => + super.beforeSubmit(), + ); + } + return super.beforeSubmit(); } - } - beforeSubmit() { - if (this.recaptchaVerifiedPromise) { - return this.recaptchaVerifiedPromise - .then(() => super.beforeSubmit()); + sendVerificationRequest(token) { + return Formio.makeStaticRequest( + `${Formio.projectUrl}/recaptcha?recaptchaToken=${token}`, + ); } - return super.beforeSubmit(); - } - sendVerificationRequest(token) { - return Formio.makeStaticRequest(`${Formio.projectUrl}/recaptcha?recaptchaToken=${token}`); - } + checkComponentValidity(data, dirty, row, options = {}) { + data = data || this.rootValue; + row = row || this.data; + const { async = false } = options; - checkComponentValidity(data, dirty, row, options = {}) { - data = data || this.rootValue; - row = row || this.data; - const { async = false } = options; + // Verification could be async only (which for now is only the case for server-side validation) + if (!async) { + return super.checkComponentValidity(data, dirty, row, options); + } - // Verification could be async only (which for now is only the case for server-side validation) - if (!async) { - return super.checkComponentValidity(data, dirty, row, options); - } + const componentData = row[this.component.key]; + if (!componentData || !componentData.token) { + this.setCustomValidity(this.t('reCaptchaTokenNotSpecifiedError')); + return Promise.resolve(false); + } - const componentData = row[this.component.key]; - if (!componentData || !componentData.token) { - this.setCustomValidity(this.t('reCaptchaTokenNotSpecifiedError')); - return Promise.resolve(false); - } + if (!componentData.success) { + this.setCustomValidity(this.t('reCaptchaTokenValidationError')); + return Promise.resolve(false); + } - if (!componentData.success) { - this.setCustomValidity(this.t('reCaptchaTokenValidationError')); - return Promise.resolve(false); + // Any further validation will 100% not run on the client + return Promise.resolve(true); } - // Any further validation will 100% not run on the client - return Promise.resolve(true); - } - - normalizeValue(newValue) { - // If a recaptcha result has already been established, then do not allow it to be reset. - return this.recaptchaResult ? this.recaptchaResult : newValue; - } + normalizeValue(newValue) { + // If a recaptcha result has already been established, then do not allow it to be reset. + return this.recaptchaResult ? this.recaptchaResult : newValue; + } } diff --git a/src/components/recaptcha/ReCaptcha.unit.js b/src/components/recaptcha/ReCaptcha.unit.js index 8ca5d6bc64..c9197e2130 100644 --- a/src/components/recaptcha/ReCaptcha.unit.js +++ b/src/components/recaptcha/ReCaptcha.unit.js @@ -1,14 +1,12 @@ import ReCaptchaComponent from './ReCaptcha'; -import { - comp1 -} from './fixtures'; +import { comp1 } from './fixtures'; -describe('reCAPTCHA Component', function() { - it('Should build a reCAPTCHA component in builder mode', function(done) { - new ReCaptchaComponent(comp1, { - builder: true +describe('reCAPTCHA Component', function () { + it('Should build a reCAPTCHA component in builder mode', function (done) { + new ReCaptchaComponent(comp1, { + builder: true, + }); + done(); }); - done(); - }); }); diff --git a/src/components/recaptcha/editForm/ReCaptcha.edit.display.js b/src/components/recaptcha/editForm/ReCaptcha.edit.display.js index 37cedf27f5..9f8ac6db38 100644 --- a/src/components/recaptcha/editForm/ReCaptcha.edit.display.js +++ b/src/components/recaptcha/editForm/ReCaptcha.edit.display.js @@ -1,106 +1,107 @@ import { getContextButtons } from '../../../utils/utils'; export default [ - { - key: 'eventType', - label: 'Type of event', - tooltip: 'Specify type of event that this reCAPTCHA would react to', - type: 'radio', - values: [ - { - label: 'Form Load', - value: 'formLoad' - }, - { - label: 'Button Click', - value: 'buttonClick' - } - ], - weight: 650 - }, - { - type: 'select', - input: true, - label: 'Button Key', - key: 'buttonKey', - dataSrc: 'custom', - valueProperty: 'value', - tooltip: 'Specify key of button on this form that this reCAPTCHA should react to', - weight: 660, - customConditional(context) { - return context.data.eventType === 'buttonClick'; - }, - data: { - custom(context) { - return getContextButtons(context); - } - } - }, - { - key: 'label', - ignore: true - }, - { - key: 'hideLabel', - ignore: true - }, - { - key: 'labelPosition', - ignore: true - }, - { - key: 'placeholder', - ignore: true - }, - { - key: 'description', - ignore: true - }, - { - key: 'tooltip', - ignore: true - }, - { - key: 'errorLabel', - ignore: true - }, - { - key: 'customClass', - ignore: true - }, - { - key: 'tabindex', - ignore: true - }, - { - key: 'multiple', - ignore: true - }, - { - key: 'clearOnHide', - ignore: true - }, - { - key: 'hidden', - ignore: true - }, - { - key: 'mask', - ignore: true - }, - { - key: 'dataGridLabel', - ignore: true - }, - { - key: 'disabled', - ignore: true - }, - { - key: 'autofocus', - ignore: true - }, - { - key: 'tableView', - ignore: true - }, + { + key: 'eventType', + label: 'Type of event', + tooltip: 'Specify type of event that this reCAPTCHA would react to', + type: 'radio', + values: [ + { + label: 'Form Load', + value: 'formLoad', + }, + { + label: 'Button Click', + value: 'buttonClick', + }, + ], + weight: 650, + }, + { + type: 'select', + input: true, + label: 'Button Key', + key: 'buttonKey', + dataSrc: 'custom', + valueProperty: 'value', + tooltip: + 'Specify key of button on this form that this reCAPTCHA should react to', + weight: 660, + customConditional(context) { + return context.data.eventType === 'buttonClick'; + }, + data: { + custom(context) { + return getContextButtons(context); + }, + }, + }, + { + key: 'label', + ignore: true, + }, + { + key: 'hideLabel', + ignore: true, + }, + { + key: 'labelPosition', + ignore: true, + }, + { + key: 'placeholder', + ignore: true, + }, + { + key: 'description', + ignore: true, + }, + { + key: 'tooltip', + ignore: true, + }, + { + key: 'errorLabel', + ignore: true, + }, + { + key: 'customClass', + ignore: true, + }, + { + key: 'tabindex', + ignore: true, + }, + { + key: 'multiple', + ignore: true, + }, + { + key: 'clearOnHide', + ignore: true, + }, + { + key: 'hidden', + ignore: true, + }, + { + key: 'mask', + ignore: true, + }, + { + key: 'dataGridLabel', + ignore: true, + }, + { + key: 'disabled', + ignore: true, + }, + { + key: 'autofocus', + ignore: true, + }, + { + key: 'tableView', + ignore: true, + }, ]; diff --git a/src/components/recaptcha/fixtures/comp1.js b/src/components/recaptcha/fixtures/comp1.js index b2b59e797b..a90e45a119 100644 --- a/src/components/recaptcha/fixtures/comp1.js +++ b/src/components/recaptcha/fixtures/comp1.js @@ -1,7 +1,7 @@ export default { - 'eventType': 'buttonClick', - 'type': 'recaptcha', - 'key': 'reCaptcha', - 'label': 'reCAPTCHA', - 'buttonKey': 'test' + eventType: 'buttonClick', + type: 'recaptcha', + key: 'reCaptcha', + label: 'reCAPTCHA', + buttonKey: 'test', }; diff --git a/src/components/select/Select.form.js b/src/components/select/Select.form.js index 93e6d01cf6..5711a407f7 100644 --- a/src/components/select/Select.form.js +++ b/src/components/select/Select.form.js @@ -3,19 +3,22 @@ import SelectEditData from './editForm/Select.edit.data'; import SelectEditDisplay from './editForm/Select.edit.display'; import SelectEditValidation from './editForm/Select.edit.validation'; -export default function(...extend) { - return listComponentForm([ - { - key: 'display', - components: SelectEditDisplay - }, - { - key: 'data', - components: SelectEditData - }, - { - key: 'validation', - components: SelectEditValidation - }, - ], ...extend); +export default function (...extend) { + return listComponentForm( + [ + { + key: 'display', + components: SelectEditDisplay, + }, + { + key: 'data', + components: SelectEditData, + }, + { + key: 'validation', + components: SelectEditValidation, + }, + ], + ...extend, + ); } diff --git a/src/components/select/Select.js b/src/components/select/Select.js index ad0c476a29..f5fe9ae197 100644 --- a/src/components/select/Select.js +++ b/src/components/select/Select.js @@ -3,74 +3,85 @@ import { Formio } from '../../Formio'; import ListComponent from '../_classes/list/ListComponent'; import Input from '../_classes/input/Input'; import Form from '../../Form'; -import { getRandomComponentId, boolValue, isPromise, componentValueTypes, getComponentSavedTypes, unescapeHTML, isSelectResourceWithObjectValue } from '../../utils/utils'; +import { + getRandomComponentId, + boolValue, + isPromise, + componentValueTypes, + getComponentSavedTypes, + unescapeHTML, + isSelectResourceWithObjectValue, +} from '../../utils/utils'; import Choices from '../../utils/ChoicesWrapper'; export default class SelectComponent extends ListComponent { - static schema(...extend) { - return ListComponent.schema({ - type: 'select', - label: 'Select', - key: 'select', - idPath: 'id', - data: { - values: [{ label: '', value: '' }], - json: '', - url: '', - resource: '', - custom: '' - }, - clearOnRefresh: false, - limit: 100, - valueProperty: '', - lazyLoad: true, - filter: '', - searchEnabled: true, - searchDebounce: 0.3, - searchField: '', - minSearch: 0, - readOnlyValue: false, - selectFields: '', - selectThreshold: 0.3, - uniqueOptions: false, - tableView: true, - fuseOptions: { - include: 'score', - threshold: 0.3, - }, - indexeddb: { - filter: {} - }, - customOptions: {}, - useExactSearch: false, - }, ...extend); - } - - static get builderInfo() { - return { - title: 'Select', - group: 'basic', - icon: 'th-list', - weight: 70, - documentation: '/userguide/form-building/form-components#select', - schema: SelectComponent.schema() - }; - } - - static get serverConditionSettings() { - return SelectComponent.conditionOperatorsSettings; - } - - static get conditionOperatorsSettings() { - return { - ...super.conditionOperatorsSettings, - valueComponent(classComp) { - const valueComp = { ... classComp, type: 'select' }; - - if (isSelectResourceWithObjectValue(classComp)) { - valueComp.reference = false; - valueComp.onSetItems = ` + static schema(...extend) { + return ListComponent.schema( + { + type: 'select', + label: 'Select', + key: 'select', + idPath: 'id', + data: { + values: [{ label: '', value: '' }], + json: '', + url: '', + resource: '', + custom: '', + }, + clearOnRefresh: false, + limit: 100, + valueProperty: '', + lazyLoad: true, + filter: '', + searchEnabled: true, + searchDebounce: 0.3, + searchField: '', + minSearch: 0, + readOnlyValue: false, + selectFields: '', + selectThreshold: 0.3, + uniqueOptions: false, + tableView: true, + fuseOptions: { + include: 'score', + threshold: 0.3, + }, + indexeddb: { + filter: {}, + }, + customOptions: {}, + useExactSearch: false, + }, + ...extend, + ); + } + + static get builderInfo() { + return { + title: 'Select', + group: 'basic', + icon: 'th-list', + weight: 70, + documentation: '/userguide/form-building/form-components#select', + schema: SelectComponent.schema(), + }; + } + + static get serverConditionSettings() { + return SelectComponent.conditionOperatorsSettings; + } + + static get conditionOperatorsSettings() { + return { + ...super.conditionOperatorsSettings, + valueComponent(classComp) { + const valueComp = { ...classComp, type: 'select' }; + + if (isSelectResourceWithObjectValue(classComp)) { + valueComp.reference = false; + valueComp.onSetItems = ` var templateKeys = utils.getItemTemplateKeys(component.template) || []; items = _.map(items || [], i => { var item = {}; @@ -78,1766 +89,2076 @@ export default class SelectComponent extends ListComponent { return item; }) `; + } + + return valueComp; + }, + }; + } + + static savedValueTypes(schema) { + const { boolean, string, number, object, array } = componentValueTypes; + const { dataType, reference } = schema; + const types = getComponentSavedTypes(schema); + + if (types) { + return types; } - return valueComp; - } - }; - } + if (reference) { + return [object]; + } - static savedValueTypes(schema) { - const { boolean, string, number, object, array } = componentValueTypes; - const { dataType, reference } = schema; - const types = getComponentSavedTypes(schema); + if (dataType === 'object') { + return [object, array]; + } - if (types) { - return types; - } + if (componentValueTypes[dataType]) { + return [componentValueTypes[dataType]]; + } - if (reference) { - return [object]; - } + return [boolean, string, number, object, array]; + } + + init() { + super.init(); + this.templateData = {}; + this.validators = this.validators.concat([ + 'select', + 'onlyAvailableItems', + ]); + + // Trigger an update. + let updateArgs = []; + const triggerUpdate = _.debounce((...args) => { + updateArgs = []; + return this.updateItems.apply(this, args); + }, 100); + this.triggerUpdate = (...args) => { + // Make sure we always resolve the previous promise before reassign it + if (typeof this.itemsLoadedResolve === 'function') { + this.itemsLoadedResolve(); + } + this.itemsLoaded = new Promise((resolve) => { + this.itemsLoadedResolve = resolve; + }); + if (args.length) { + updateArgs = args; + } + return triggerUpdate(...updateArgs); + }; - if (dataType === 'object') { - return [object, array]; - } + // Keep track of the select options. + this.selectOptions = []; - if (componentValueTypes[dataType]) { - return [componentValueTypes[dataType]]; - } + if (this.itemsFromUrl) { + this.isFromSearch = false; - return [boolean, string, number, object, array]; - } + this.searchServerCount = null; + this.defaultServerCount = null; - init() { - super.init(); - this.templateData = {}; - this.validators = this.validators.concat(['select', 'onlyAvailableItems']); + this.isScrollLoading = false; - // Trigger an update. - let updateArgs = []; - const triggerUpdate = _.debounce((...args) => { - updateArgs = []; - return this.updateItems.apply(this, args); - }, 100); - this.triggerUpdate = (...args) => { - // Make sure we always resolve the previous promise before reassign it - if (typeof this.itemsLoadedResolve === 'function') { - this.itemsLoadedResolve(); - } - this.itemsLoaded = new Promise((resolve) => { - this.itemsLoadedResolve = resolve; - }); - if (args.length) { - updateArgs = args; - } - return triggerUpdate(...updateArgs); - }; + this.searchDownloadedResources = []; + this.defaultDownloadedResources = []; + } - // Keep track of the select options. - this.selectOptions = []; + // If this component has been activated.// + this.activated = false; + this.itemsLoaded = new Promise((resolve) => { + this.itemsLoadedResolve = resolve; + }); - if (this.itemsFromUrl) { - this.isFromSearch = false; + this.shouldPositionDropdown = this.hasDataGridAncestor(); - this.searchServerCount = null; - this.defaultServerCount = null; + if (this.isHtmlRenderMode()) { + this.activate(); + } - this.isScrollLoading = false; + // Get the template keys for this select component. + this.getTemplateKeys(); + } - this.searchDownloadedResources = []; - this.defaultDownloadedResources = []; + get dataReady() { + // If the root submission has been set, and we are still not attached, then assume + // that our data is ready. + if (this.root && this.root.submissionSet && !this.attached) { + return Promise.resolve(); + } + return this.itemsLoaded; } - // If this component has been activated.// - this.activated = false; - this.itemsLoaded = new Promise((resolve) => { - this.itemsLoadedResolve = resolve; - }); + get defaultSchema() { + return SelectComponent.schema(); + } - this.shouldPositionDropdown = this.hasDataGridAncestor(); + get emptyValue() { + if (this.component.multiple) { + return []; + } + // if select has JSON data source type, we are defining if empty value would be an object or a string by checking JSON's first item + if (this.component.dataSrc === 'json' && this.component.data.json) { + const firstItem = this.component.data.json[0]; + let firstValue; + if (this.valueProperty) { + firstValue = _.get(firstItem, this.valueProperty); + } else { + firstValue = firstItem; + } + if (firstValue && typeof firstValue === 'string') { + return ''; + } else { + return {}; + } + } + if (this.valueProperty) { + return ''; + } + return {}; + } - if (this.isHtmlRenderMode()) { - this.activate(); + get valueProperty() { + if (this.component.valueProperty) { + return this.component.valueProperty; + } + // Force values datasource to use values without actually setting it on the component settings. + if (this.component.dataSrc === 'values') { + return 'value'; + } + return ''; } - // Get the template keys for this select component. - this.getTemplateKeys(); - } + get inputInfo() { + const info = super.elementInfo(); + info.type = 'select'; + info.changeEvent = 'change'; + return info; + } - get dataReady() { - // If the root submission has been set, and we are still not attached, then assume - // that our data is ready. - if ( - this.root && - this.root.submissionSet && - !this.attached - ) { - return Promise.resolve(); - } - return this.itemsLoaded; - } - - get defaultSchema() { - return SelectComponent.schema(); - } - - get emptyValue() { - if (this.component.multiple) { - return []; - } - // if select has JSON data source type, we are defining if empty value would be an object or a string by checking JSON's first item - if (this.component.dataSrc === 'json' && this.component.data.json) { - const firstItem = this.component.data.json[0]; - let firstValue; - if (this.valueProperty) { - firstValue = _.get(firstItem, this.valueProperty); - } - else { - firstValue = firstItem; - } - if (firstValue && typeof firstValue === 'string') { - return ''; - } - else { - return {}; - } + get isSelectResource() { + return this.component.dataSrc === 'resource'; } - if (this.valueProperty) { - return ''; + + get itemsFromUrl() { + return this.isSelectResource || this.isSelectURL; } - return {}; - } - get valueProperty() { - if (this.component.valueProperty) { - return this.component.valueProperty; + get isInfiniteScrollProvided() { + return this.itemsFromUrl; } - // Force values datasource to use values without actually setting it on the component settings. - if (this.component.dataSrc === 'values') { - return 'value'; + + get shouldDisabled() { + return super.shouldDisabled || this.parentDisabled; } - return ''; - } - get inputInfo() { - const info = super.elementInfo(); - info.type = 'select'; - info.changeEvent = 'change'; - return info; - } + get shouldInitialLoad() { + if ( + this.component.widget === 'html5' && + this.isEntireObjectDisplay() && + this.component.searchField && + this.dataValue + ) { + return false; + } - get isSelectResource() { - return this.component.dataSrc === 'resource'; - } + return super.shouldLoad; + } + + isEntireObjectDisplay() { + return ( + this.component.dataSrc === 'resource' && + this.valueProperty === 'data' + ); + } + + selectValueAndLabel(data) { + const value = this.getOptionValue( + this.isEntireObjectDisplay() && !this.itemValue(data) + ? data + : this.itemValue(data), + ); + return { + value, + label: this.itemTemplate( + this.isEntireObjectDisplay() && !_.isObject(data.data) + ? { data: data } + : data, + value, + ), + }; + } - get itemsFromUrl() { - return this.isSelectResource || this.isSelectURL; - } + itemTemplate(data, value) { + if (!_.isNumber(data) && _.isEmpty(data)) { + return ''; + } - get isInfiniteScrollProvided() { - return this.itemsFromUrl; - } + // If they wish to show the value in read only mode, then just return the itemValue here. + if (this.options.readOnly && this.component.readOnlyValue) { + return this.itemValue(data); + } + // Perform a fast interpretation if we should not use the template. + if (data && !this.component.template) { + const itemLabel = data.label || data; + const value = + typeof itemLabel === 'string' + ? this.t(itemLabel, { _userInput: true }) + : itemLabel; + return this.sanitize(value, this.shouldSanitizeValue); + } - get shouldDisabled() { - return super.shouldDisabled || this.parentDisabled; - } + if ( + this.component.multiple && _.isArray(this.dataValue) + ? this.dataValue.find((val) => value === val) + : this.dataValue === value + ) { + const selectData = this.selectData; + if (selectData) { + const templateValue = + this.component.reference && value?._id + ? value._id.toString() + : value; + if (!this.templateData || !this.templateData[templateValue]) { + this.getOptionTemplate(data, value); + } + if (this.component.multiple) { + if (selectData[templateValue]) { + data = selectData[templateValue]; + } + } else { + data = selectData; + } + } + } - get shouldInitialLoad() { - if (this.component.widget === 'html5' && - this.isEntireObjectDisplay() && - this.component.searchField && - this.dataValue) { - return false; - } + if (typeof data === 'string' || typeof data === 'number') { + return this.sanitize( + this.t(data, { _userInput: true }), + this.shouldSanitizeValue, + ); + } + if (Array.isArray(data)) { + return data.map((val) => { + if (typeof val === 'string' || typeof val === 'number') { + return this.sanitize( + this.t(val, { _userInput: true }), + this.shouldSanitizeValue, + ); + } + return val; + }); + } - return super.shouldLoad; - } + if (data.data) { + // checking additional fields in the template for the selected Entire Object option + const hasNestedFields = /item\.data\.\w*/g.test( + this.component.template, + ); + data.data = + this.isEntireObjectDisplay() && + _.isObject(data.data) && + !hasNestedFields + ? JSON.stringify(data.data) + : data.data; + } + return super.itemTemplate(data, value); + } + + /** + * Adds an option to the select dropdown. + * + * @param value + * @param label + */ + addOption(value, label, attrs = {}, id = getRandomComponentId()) { + if (_.isNil(label)) return; + const idPath = this.component.idPath + ? this.component.idPath + .split('.') + .reduceRight((obj, key) => ({ [key]: obj }), id) + : {}; + const option = { + value: this.getOptionValue(value), + label, + ...idPath, + }; - isEntireObjectDisplay() { - return this.component.dataSrc === 'resource' && this.valueProperty === 'data'; - } + const skipOption = this.component.uniqueOptions + ? !!this.selectOptions.find((selectOption) => + _.isEqual(selectOption.value, option.value), + ) + : false; - selectValueAndLabel(data) { - const value = this.getOptionValue((this.isEntireObjectDisplay() && !this.itemValue(data)) ? data : this.itemValue(data)); - return { - value, - label: this.itemTemplate((this.isEntireObjectDisplay() && !_.isObject(data.data)) ? { data: data } : data, value) - }; - } + if (skipOption) { + return; + } - itemTemplate(data, value) { - if (!_.isNumber(data) && _.isEmpty(data)) { - return ''; - } + if (value) { + this.selectOptions.push(option); + } - // If they wish to show the value in read only mode, then just return the itemValue here. - if (this.options.readOnly && this.component.readOnlyValue) { - return this.itemValue(data); - } - // Perform a fast interpretation if we should not use the template. - if (data && !this.component.template) { - const itemLabel = data.label || data; - const value = (typeof itemLabel === 'string') ? this.t(itemLabel, { _userInput: true }) : itemLabel; - return this.sanitize(value, this.shouldSanitizeValue); + if (this.refs.selectContainer && this.component.widget === 'html5') { + // Replace an empty Object value to an empty String. + if ( + option.value && + _.isObject(option.value) && + _.isEmpty(option.value) + ) { + option.value = ''; + } + // Add element to option so we can reference it later. + const div = document.createElement('div'); + div.innerHTML = this.sanitize( + this.renderTemplate('selectOption', { + selected: _.isEqual( + this.getOptionValue(this.dataValue), + option.value, + ), + option, + attrs, + id, + useId: + (this.valueProperty === '' || + this.isEntireObjectDisplay()) && + _.isObject(value) && + id, + }), + this.shouldSanitizeValue, + ).trim(); + + option.element = div.firstChild; + this.refs.selectContainer.appendChild(option.element); + } } - if (this.component.multiple && _.isArray(this.dataValue) ? this.dataValue.find((val) => value === val) : (this.dataValue === value)) { - const selectData = this.selectData; - if (selectData) { - const templateValue = this.component.reference && value?._id ? value._id.toString() : value; - if (!this.templateData || !this.templateData[templateValue]) { - this.getOptionTemplate(data, value); + addValueOptions(items) { + items = items || []; + let added = false; + let data = this.dataValue; + + // preset submission value with value property before request. + if ( + this.options.pdf && + !items.length && + this.component.dataSrc === 'url' && + this.valueProperty + ) { + data = Array.isArray(data) + ? data.map((item) => _.set({}, this.valueProperty, item)) + : _.set({}, this.valueProperty, data); } - if (this.component.multiple) { - if (selectData[templateValue]) { - data = selectData[templateValue]; - } - } - else { - data = selectData; - } - } - } - - if (typeof data === 'string' || typeof data === 'number') { - return this.sanitize(this.t(data, { _userInput: true }), this.shouldSanitizeValue); - } - if (Array.isArray(data)) { - return data.map((val) => { - if (typeof val === 'string' || typeof val === 'number') { - return this.sanitize(this.t(val, { _userInput: true }), this.shouldSanitizeValue); - } - return val; - }); - } - - if (data.data) { - // checking additional fields in the template for the selected Entire Object option - const hasNestedFields = /item\.data\.\w*/g.test(this.component.template); - data.data = this.isEntireObjectDisplay() && _.isObject(data.data) && !hasNestedFields - ? JSON.stringify(data.data) - : data.data; - } - return super.itemTemplate(data, value); - } - - /** - * Adds an option to the select dropdown. - * - * @param value - * @param label - */ - addOption(value, label, attrs = {}, id = getRandomComponentId()) { - if (_.isNil(label)) return; - const idPath = this.component.idPath - ? this.component.idPath.split('.').reduceRight((obj, key) => ({ [key]: obj }), id) - : {}; - const option = { - value: this.getOptionValue(value), - label, - ...idPath - }; - - const skipOption = this.component.uniqueOptions - ? !!this.selectOptions.find((selectOption) => _.isEqual(selectOption.value, option.value)) - : false; - - if (skipOption) { - return; - } - - if (value) { - this.selectOptions.push(option); - } - - if (this.refs.selectContainer && (this.component.widget === 'html5')) { - // Replace an empty Object value to an empty String. - if (option.value && _.isObject(option.value) && _.isEmpty(option.value)) { - option.value = ''; - } - // Add element to option so we can reference it later. - const div = document.createElement('div'); - div.innerHTML = this.sanitize(this.renderTemplate('selectOption', { - selected: _.isEqual(this.getOptionValue(this.dataValue), option.value), - option, - attrs, - id, - useId: (this.valueProperty === '' || this.isEntireObjectDisplay()) && _.isObject(value) && id, - }), this.shouldSanitizeValue).trim(); - - option.element = div.firstChild; - this.refs.selectContainer.appendChild(option.element); - } - } - - addValueOptions(items) { - items = items || []; - let added = false; - let data = this.dataValue; - - // preset submission value with value property before request. - if (this.options.pdf && !items.length && this.component.dataSrc === 'url' && this.valueProperty) { - data = Array.isArray(data) - ? data.map(item => _.set({}, this.valueProperty, item)) - : _.set({}, this.valueProperty, data); - } - - if (!this.selectOptions.length) { - // Add the currently selected choices if they don't already exist. - const currentChoices = Array.isArray(data) && this.component.multiple ? data : [data]; - added = this.addCurrentChoices(currentChoices, items); - if (!added && !this.component.multiple) { - this.addPlaceholder(); - } - } - return added; - } - disableInfiniteScroll() { - if (!this.downloadedResources) { - return; + if (!this.selectOptions.length) { + // Add the currently selected choices if they don't already exist. + const currentChoices = + Array.isArray(data) && this.component.multiple ? data : [data]; + added = this.addCurrentChoices(currentChoices, items); + if (!added && !this.component.multiple) { + this.addPlaceholder(); + } + } + return added; } - this.downloadedResources.serverCount = this.downloadedResources.length; - this.serverCount = this.downloadedResources.length; - } + disableInfiniteScroll() { + if (!this.downloadedResources) { + return; + } - /* eslint-disable max-statements */ - setItems(items, fromSearch) { - this.selectItems = items; - // If the items is a string, then parse as JSON. - if (typeof items == 'string') { - try { - items = JSON.parse(items); - } - catch (err) { - console.warn(err.message); - items = []; - } + this.downloadedResources.serverCount = this.downloadedResources.length; + this.serverCount = this.downloadedResources.length; } - // Allow js processing (needed for form builder) - if (this.component.onSetItems) { - const newItems = typeof this.component.onSetItems === 'function' - ? this.component.onSetItems(this, items) - : this.evaluate(this.component.onSetItems, { items: items }, 'items'); - if (newItems) { - items = newItems; - } - } + /* eslint-disable max-statements */ + setItems(items, fromSearch) { + this.selectItems = items; + // If the items is a string, then parse as JSON. + if (typeof items == 'string') { + try { + items = JSON.parse(items); + } catch (err) { + console.warn(err.message); + items = []; + } + } - if (!this.choices && this.refs.selectContainer) { - this.empty(this.refs.selectContainer); - } + // Allow js processing (needed for form builder) + if (this.component.onSetItems) { + const newItems = + typeof this.component.onSetItems === 'function' + ? this.component.onSetItems(this, items) + : this.evaluate( + this.component.onSetItems, + { items: items }, + 'items', + ); + if (newItems) { + items = newItems; + } + } - // If they provided select values, then we need to get them instead. - if (this.component.selectValues) { - items = _.get(items, this.component.selectValues, items) || []; - } + if (!this.choices && this.refs.selectContainer) { + this.empty(this.refs.selectContainer); + } - let areItemsEqual; + // If they provided select values, then we need to get them instead. + if (this.component.selectValues) { + items = _.get(items, this.component.selectValues, items) || []; + } - if (this.itemsFromUrl) { - areItemsEqual = this.isSelectURL ? _.isEqual(items, this.downloadedResources) : false; + let areItemsEqual; - const areItemsEnded = this.component.limit > items.length; - const areItemsDownloaded = areItemsEqual - && this.downloadedResources - && this.downloadedResources.length === items.length; + if (this.itemsFromUrl) { + areItemsEqual = this.isSelectURL + ? _.isEqual(items, this.downloadedResources) + : false; + + const areItemsEnded = this.component.limit > items.length; + const areItemsDownloaded = + areItemsEqual && + this.downloadedResources && + this.downloadedResources.length === items.length; + + if (areItemsEnded) { + this.disableInfiniteScroll(); + } else if (areItemsDownloaded) { + this.selectOptions = []; + } else { + this.serverCount = items.serverCount; + } + } - if (areItemsEnded) { - this.disableInfiniteScroll(); - } - else if (areItemsDownloaded) { - this.selectOptions = []; - } - else { - this.serverCount = items.serverCount; - } - } - - if (this.isScrollLoading && items) { - if (!areItemsEqual) { - this.downloadedResources = this.downloadedResources - ? this.downloadedResources.concat(items) - : items; - } - - this.downloadedResources.serverCount = items.serverCount || this.downloadedResources.serverCount; - } - else { - this.downloadedResources = items || []; - this.selectOptions = []; - // If there is new select option with same id as already selected, set the new one - if (!_.isEmpty(this.dataValue) && this.component.idPath) { - const selectedOptionId = _.get(this.dataValue, this.component.idPath, null); - const newOptionWithSameId = !_.isNil(selectedOptionId) && items.find(item => { - const itemId = _.get(item, this.component.idPath); - - return itemId === selectedOptionId; + if (this.isScrollLoading && items) { + if (!areItemsEqual) { + this.downloadedResources = this.downloadedResources + ? this.downloadedResources.concat(items) + : items; + } + + this.downloadedResources.serverCount = + items.serverCount || this.downloadedResources.serverCount; + } else { + this.downloadedResources = items || []; + this.selectOptions = []; + // If there is new select option with same id as already selected, set the new one + if (!_.isEmpty(this.dataValue) && this.component.idPath) { + const selectedOptionId = _.get( + this.dataValue, + this.component.idPath, + null, + ); + const newOptionWithSameId = + !_.isNil(selectedOptionId) && + items.find((item) => { + const itemId = _.get(item, this.component.idPath); + + return itemId === selectedOptionId; + }); + + if (newOptionWithSameId) { + this.setValue(newOptionWithSameId); + } + } + } + + // Add the value options. + if (!fromSearch) { + this.addValueOptions(items); + } + + if (this.component.widget === 'html5' && !this.component.placeholder) { + this.addOption(null, ''); + } + + // Iterate through each of the items. + _.each(items, (item, index) => { + // preventing references of the components inside the form to the parent form when building forms + if ( + this.root && + this.root.options.editForm && + this.root.options.editForm._id && + this.root.options.editForm._id === item._id + ) + return; + const itemValueAndLabel = this.selectValueAndLabel(item); + this.addOption( + itemValueAndLabel.value, + itemValueAndLabel.label, + {}, + _.get(item, this.component.idPath, String(index)), + ); }); - if (newOptionWithSameId) { - this.setValue(newOptionWithSameId); + if (this.choices) { + this.choices.setChoices(this.selectOptions, 'value', 'label', true); + } else if (this.loading) { + // Re-attach select input. + // this.appendTo(this.refs.input[0], this.selectContainer); } - } + + // We are no longer loading. + this.isScrollLoading = false; + this.loading = false; + + const searching = fromSearch && this.choices?.input?.isFocussed; + + if (!searching) { + // If a value is provided, then select it. + if (!this.isEmpty() || this.isRemoveButtonPressed) { + this.setValue(this.dataValue, { + noUpdateEvent: true, + }); + } else if (this.shouldAddDefaultValue && !this.options.readOnly) { + // If a default value is provided then select it. + const defaultValue = this.defaultValue; + if (!this.isEmpty(defaultValue)) { + this.setValue(defaultValue); + } + } + } + + // Say we are done loading the items. + this.itemsLoadedResolve(); } - // Add the value options. - if (!fromSearch) { - this.addValueOptions(items); + getSingleItemValueForHTMLMode(data) { + const option = this.selectOptions?.find(({ value }) => + _.isEqual(value, data), + ); + if (option) { + return option.label || data; + } + + return data; } - if (this.component.widget === 'html5' && !this.component.placeholder) { - this.addOption(null, ''); + itemValueForHTMLMode(value) { + if (!this.isHtmlRenderMode()) { + return super.itemValueForHTMLMode(value); + } + + if (Array.isArray(value)) { + const values = value.map((item) => + Array.isArray(item) + ? this.itemValueForHTMLMode(item) + : this.getSingleItemValueForHTMLMode(item), + ); + + return values.join(', '); + } + + return this.getSingleItemValueForHTMLMode(value); } - // Iterate through each of the items. - _.each(items, (item, index) => { - // preventing references of the components inside the form to the parent form when building forms - if (this.root && this.root.options.editForm && this.root.options.editForm._id && this.root.options.editForm._id === item._id) return; - const itemValueAndLabel = this.selectValueAndLabel(item); - this.addOption(itemValueAndLabel.value, itemValueAndLabel.label, {}, _.get(item, this.component.idPath, String(index))); - }); + /* eslint-enable max-statements */ - if (this.choices) { - this.choices.setChoices(this.selectOptions, 'value', 'label', true); + get defaultValue() { + let defaultValue = super.defaultValue; + if ( + !defaultValue && + (this.component.defaultValue === false || + this.component.defaultValue === 0) + ) { + defaultValue = this.component.defaultValue; + } + return defaultValue; } - else if (this.loading) { - // Re-attach select input. - // this.appendTo(this.refs.input[0], this.selectContainer); + + get loadingError() { + return ( + !this.component.refreshOn && + !this.component.refreshOnBlur && + this.networkError + ); } - // We are no longer loading. - this.isScrollLoading = false; - this.loading = false; + loadItems(url, search, headers, options, method, body) { + options = options || {}; + + // See if we should load items or not. + if (!this.shouldLoad || (!this.itemsFromUrl && this.options.readOnly)) { + this.isScrollLoading = false; + this.loading = false; + this.itemsLoadedResolve(); + return; + } + + // See if they have not met the minimum search requirements. + const minSearch = parseInt(this.component.minSearch, 10); + if ( + this.component.searchField && + minSearch > 0 && + (!search || search.length < minSearch) + ) { + // Set empty items. + return this.setItems([]); + } - const searching = fromSearch && this.choices?.input?.isFocussed; + // Ensure we have a method and remove any body if method is get + method = method || 'GET'; + if (method.toUpperCase() === 'GET') { + body = null; + } - if (!searching) { - // If a value is provided, then select it. - if (!this.isEmpty() || this.isRemoveButtonPressed) { - this.setValue(this.dataValue, { - noUpdateEvent: true + const limit = this.component.limit || 100; + const skip = this.isScrollLoading ? this.selectOptions.length : 0; + const query = this.component.disableLimit + ? {} + : { + limit, + skip, + }; + + // Allow for url interpolation. + url = this.interpolate(url, { + formioBase: Formio.getBaseUrl(), + search, + limit, + skip, + page: Math.abs(Math.floor(skip / limit)), }); - } - else if (this.shouldAddDefaultValue && !this.options.readOnly) { - // If a default value is provided then select it. - const defaultValue = this.defaultValue; - if (!this.isEmpty(defaultValue)) { - this.setValue(defaultValue); + + // Add search capability. + if (this.component.searchField && search) { + const searchValue = Array.isArray(search) + ? search.join(',') + : typeof search === 'object' + ? JSON.stringify(search) + : search; + + query[this.component.searchField] = + this.component.searchField.endsWith('__regex') + ? _.escapeRegExp(searchValue) + : searchValue; } - } - } - // Say we are done loading the items. - this.itemsLoadedResolve(); - } + // If they wish to return only some fields. + if (this.component.selectFields) { + query.select = this.component.selectFields; + } - getSingleItemValueForHTMLMode(data) { - const option = this.selectOptions?.find(({ value }) => _.isEqual(value, data)); - if (option) { - return option.label || data; - } + // Add sort capability + if (this.component.sort) { + query.sort = this.component.sort; + } - return data; - } + if (!_.isEmpty(query)) { + // Add the query string. + url += + (!url.includes('?') ? '?' : '&') + + Formio.serialize(query, (item) => this.interpolate(item)); + } + + // Add filter capability + if (this.component.filter) { + url += + (!url.includes('?') ? '?' : '&') + + this.interpolate(this.component.filter); + } - itemValueForHTMLMode(value) { - if (!this.isHtmlRenderMode()) { - return super.itemValueForHTMLMode(value); + // Set ignoreCache if it is + options.ignoreCache = this.component.ignoreCache; + + // Make the request. + options.header = headers; + this.loading = true; + + Formio.makeRequest( + this.options.formio, + 'select', + url, + method, + body, + options, + ) + .then((response) => { + this.loading = false; + this.error = null; + this.setItems(response, !!search); + }) + .catch((err) => { + if (this.itemsFromUrl) { + this.setItems([]); + this.disableInfiniteScroll(); + } + + this.isScrollLoading = false; + this.handleLoadingError(err); + }); } - if (Array.isArray(value)) { - const values = value.map(item => Array.isArray(item) - ? this.itemValueForHTMLMode(item) - : this.getSingleItemValueForHTMLMode(item)); + handleLoadingError(err) { + this.loading = false; + if (err.networkError) { + this.networkError = true; + } + this.itemsLoadedResolve(); + this.emit('componentError', { + component: this.component, + message: err.toString(), + }); + console.warn(`Unable to load resources for ${this.key}`); + } + /** + * Get the request headers for this select dropdown. + */ + get requestHeaders() { + // Create the headers object. + const headers = new Formio.Headers(); + + // Add custom headers to the url. + if (this.component.data && this.component.data.headers) { + try { + _.each(this.component.data.headers, (header) => { + if (header.key) { + headers.set(header.key, this.interpolate(header.value)); + } + }); + } catch (err) { + console.warn(err.message); + } + } - return values.join(', '); + return headers; } - return this.getSingleItemValueForHTMLMode(value); - } + getCustomItems() { + const customItems = this.evaluate( + this.component.data.custom, + { + values: [], + }, + 'values', + ); - /* eslint-enable max-statements */ + this.asyncValues = isPromise(customItems); - get defaultValue() { - let defaultValue = super.defaultValue; - if (!defaultValue && (this.component.defaultValue === false || this.component.defaultValue === 0)) { - defaultValue = this.component.defaultValue; + return customItems; } - return defaultValue; - } - - get loadingError() { - return !this.component.refreshOn && !this.component.refreshOnBlur && this.networkError; - } - loadItems(url, search, headers, options, method, body) { - options = options || {}; + asyncCustomValues() { + if (!_.isBoolean(this.asyncValues)) { + this.getCustomItems(); + } - // See if we should load items or not. - if (!this.shouldLoad || (!this.itemsFromUrl && this.options.readOnly)) { - this.isScrollLoading = false; - this.loading = false; - this.itemsLoadedResolve(); - return; + return this.asyncValues; } - // See if they have not met the minimum search requirements. - const minSearch = parseInt(this.component.minSearch, 10); - if ( - this.component.searchField && - (minSearch > 0) && - (!search || (search.length < minSearch)) - ) { - // Set empty items. - return this.setItems([]); - } + updateCustomItems(forceUpdate) { + if (this.asyncCustomValues()) { + if (!forceUpdate && !this.active) { + this.itemsLoadedResolve(); + return; + } - // Ensure we have a method and remove any body if method is get - method = method || 'GET'; - if (method.toUpperCase() === 'GET') { - body = null; + this.loading = true; + this.getCustomItems() + .then((items) => { + this.loading = false; + this.setItems(items || []); + }) + .catch((err) => { + this.handleLoadingError(err); + }); + } else { + this.setItems(this.getCustomItems() || []); + } } - const limit = this.component.limit || 100; - const skip = this.isScrollLoading ? this.selectOptions.length : 0; - const query = this.component.disableLimit ? {} : { - limit, - skip, - }; + isEmpty(value = this.dataValue) { + return super.isEmpty(value) || value === undefined; + } - // Allow for url interpolation. - url = this.interpolate(url, { - formioBase: Formio.getBaseUrl(), - search, - limit, - skip, - page: Math.abs(Math.floor(skip / limit)) - }); + refresh(value, { instance }) { + if (this.component.clearOnRefresh && instance && !instance.pristine) { + this.setValue(this.emptyValue); + } - // Add search capability. - if (this.component.searchField && search) { - const searchValue = Array.isArray(search) - ? search.join(',') - : typeof search === 'object' - ? JSON.stringify(search) - : search; + this.updateItems(null, true); + } - query[this.component.searchField] = this.component.searchField.endsWith('__regex') - ? _.escapeRegExp(searchValue) - : searchValue; + get additionalResourcesAvailable() { + return ( + _.isNil(this.serverCount) || + this.serverCount > this.downloadedResources.length + ); } - // If they wish to return only some fields. - if (this.component.selectFields) { - query.select = this.component.selectFields; + get serverCount() { + if (this.isFromSearch) { + return this.searchServerCount; + } + + return this.defaultServerCount; } - // Add sort capability - if (this.component.sort) { - query.sort = this.component.sort; + set serverCount(value) { + if (this.isFromSearch) { + this.searchServerCount = value; + } else { + this.defaultServerCount = value; + } } - if (!_.isEmpty(query)) { - // Add the query string. - url += (!url.includes('?') ? '?' : '&') + Formio.serialize(query, (item) => this.interpolate(item)); + get downloadedResources() { + if (this.isFromSearch) { + return this.searchDownloadedResources; + } + + return this.defaultDownloadedResources; } - // Add filter capability - if (this.component.filter) { - url += (!url.includes('?') ? '?' : '&') + this.interpolate(this.component.filter); + set downloadedResources(value) { + if (this.isFromSearch) { + this.searchDownloadedResources = value; + } else { + this.defaultDownloadedResources = value; + } } - // Set ignoreCache if it is - options.ignoreCache = this.component.ignoreCache; + addPlaceholder() { + if (!this.component.placeholder) { + return; + } - // Make the request. - options.header = headers; - this.loading = true; + this.addOption('', this.component.placeholder, { placeholder: true }); + } - Formio.makeRequest(this.options.formio, 'select', url, method, body, options) - .then((response) => { - this.loading = false; - this.error = null; - this.setItems(response, !!search); - }) - .catch((err) => { - if (this.itemsFromUrl) { - this.setItems([]); - this.disableInfiniteScroll(); + /** + * Activate this select control. + */ + activate() { + if (this.loading || !this.active) { + this.setLoadingItem(); + } + if (this.active) { + return; + } + this.activated = true; + this.triggerUpdate(); + } + + setLoadingItem(addToCurrentList = false) { + if (this.choices) { + if (addToCurrentList) { + this.choices.setChoices( + [ + { + value: `${this.id}-loading`, + label: 'Loading...', + disabled: true, + }, + ], + 'value', + 'label', + ); + } else { + this.choices.setChoices( + [ + { + value: '', + label: ``, + disabled: true, + }, + ], + 'value', + 'label', + true, + ); + } + } else if ( + this.component.dataSrc === 'url' || + this.component.dataSrc === 'resource' + ) { + this.addOption('', this.t('loading...')); } + } - this.isScrollLoading = false; - this.handleLoadingError(err); - }); - } - - handleLoadingError(err) { - this.loading = false; - if (err.networkError) { - this.networkError = true; - } - this.itemsLoadedResolve(); - this.emit('componentError', { - component: this.component, - message: err.toString(), - }); - console.warn(`Unable to load resources for ${this.key}`); - } - /** - * Get the request headers for this select dropdown. - */ - get requestHeaders() { - // Create the headers object. - const headers = new Formio.Headers(); - - // Add custom headers to the url. - if (this.component.data && this.component.data.headers) { - try { - _.each(this.component.data.headers, (header) => { - if (header.key) { - headers.set(header.key, this.interpolate(header.value)); - } + get active() { + return !this.component.lazyLoad || this.activated; + } + + render() { + const info = this.inputInfo; + info.attr = info.attr || {}; + info.multiple = this.component.multiple; + return super.render( + this.wrapElement( + this.renderTemplate('select', { + input: info, + selectOptions: '', + index: null, + }), + ), + ); + } + + wrapElement(element) { + return this.component.addResource && !this.options.readOnly + ? this.renderTemplate('resourceAdd', { + element, + }) + : element; + } + + choicesOptions() { + const useSearch = Object.prototype.hasOwnProperty.call( + this.component, + 'searchEnabled', + ) + ? this.component.searchEnabled + : true; + const placeholderValue = this.t(this.component.placeholder, { + _userInput: true, }); - } - catch (err) { - console.warn(err.message); - } - } + let customOptions = this.component.customOptions || {}; + if (typeof customOptions == 'string') { + try { + customOptions = JSON.parse(customOptions); + } catch (err) { + console.warn(err.message); + customOptions = {}; + } + } - return headers; - } + const commonFuseOptions = { + maxPatternLength: 1000, + distance: 1000, + }; - getCustomItems() { - const customItems = this.evaluate(this.component.data.custom, { - values: [] - }, 'values'); + return { + removeItemButton: this.component.disabled + ? false + : _.get(this.component, 'removeItemButton', true), + itemSelectText: '', + classNames: { + containerOuter: 'choices form-group formio-choices', + containerInner: this.transform( + 'class', + 'form-control ui fluid selection dropdown', + ), + }, + addItemText: false, + allowHTML: true, + placeholder: !!this.component.placeholder, + placeholderValue: placeholderValue, + noResultsText: this.t('No results found'), + noChoicesText: this.t('No choices to choose from'), + searchPlaceholderValue: this.t('Type to search'), + shouldSort: false, + position: this.component.dropdown || 'auto', + searchEnabled: useSearch, + searchChoices: !this.component.searchField, + searchFields: _.get(this, 'component.searchFields', ['label']), + shadowRoot: this.root ? this.root.shadowRoot : null, + fuseOptions: this.component.useExactSearch + ? { + tokenize: true, + matchAllTokens: true, + ...commonFuseOptions, + } + : Object.assign({}, _.get(this, 'component.fuseOptions', {}), { + include: 'score', + threshold: _.get(this, 'component.selectThreshold', 0.3), + ...commonFuseOptions, + }), + valueComparer: _.isEqual, + resetScrollPosition: false, + ...customOptions, + }; + } - this.asyncValues = isPromise(customItems); + /* eslint-disable max-statements */ + attach(element) { + const superAttach = super.attach(element); + this.loadRefs(element, { + selectContainer: 'single', + addResource: 'single', + autocompleteInput: 'single', + }); + //enable autocomplete for select + const autocompleteInput = this.refs.autocompleteInput; + if (autocompleteInput) { + this.addEventListener(autocompleteInput, 'change', (event) => { + this.setValue(event.target.value); + }); + } + const input = this.refs.selectContainer; + if (!input) { + return; + } + this.addEventListener(input, this.inputInfo.changeEvent, () => + this.updateValue(null, { + modified: true, + }), + ); + this.attachRefreshOnBlur(); + + if (this.component.widget === 'html5') { + this.addFocusBlurEvents(input); + this.triggerUpdate(null, true); + + if (this.visible) { + this.setItems(this.selectItems || []); + } - return customItems; - } + this.focusableElement = input; - asyncCustomValues() { - if (!_.isBoolean(this.asyncValues)) { - this.getCustomItems(); - } + if (this.component.dataSrc === 'custom') { + this.addEventListener(input, 'focus', () => + this.updateCustomItems(), + ); + } - return this.asyncValues; - } + this.addEventListener(input, 'keydown', (event) => { + const { key } = event; - updateCustomItems(forceUpdate) { - if (this.asyncCustomValues()) { - if (!forceUpdate && !this.active) { - this.itemsLoadedResolve(); - return; - } - - this.loading = true; - this.getCustomItems() - .then(items => { - this.loading = false; - this.setItems(items || []); - }) - .catch(err => { - this.handleLoadingError(err); - }); - } - else { - this.setItems(this.getCustomItems() || []); - } - } + if (['Backspace', 'Delete'].includes(key)) { + this.setValue(this.emptyValue); + } + }); - isEmpty(value = this.dataValue) { - return super.isEmpty(value) || value === undefined; - } + return; + } - refresh(value, { instance }) { - if (this.component.clearOnRefresh && (instance && !instance.pristine)) { - this.setValue(this.emptyValue); - } + const tabIndex = input.tabIndex; + this.addPlaceholder(); + if (this.i18next) { + input.setAttribute('dir', this.i18next.dir()); + } + if (this.choices?.containerOuter?.element?.parentNode) { + this.choices.destroy(); + } - this.updateItems(null, true); - } + const choicesOptions = this.choicesOptions(); - get additionalResourcesAvailable() { - return _.isNil(this.serverCount) || (this.serverCount > this.downloadedResources.length); - } + if (Choices) { + this.choices = new Choices(input, choicesOptions); - get serverCount() { - if (this.isFromSearch) { - return this.searchServerCount; - } + if (this.selectOptions && this.selectOptions.length) { + this.choices.setChoices( + this.selectOptions, + 'value', + 'label', + true, + ); + } - return this.defaultServerCount; - } + if (this.component.multiple) { + this.focusableElement = this.choices.input.element; + } else { + this.focusableElement = this.choices.containerInner.element; + this.choices.containerOuter.element.setAttribute( + 'tabIndex', + '-1', + ); + this.addEventListener( + this.choices.containerOuter.element, + 'focus', + () => this.focusableElement.focus(), + ); + } - set serverCount(value) { - if (this.isFromSearch) { - this.searchServerCount = value; - } - else { - this.defaultServerCount = value; - } - } + Input.prototype.addFocusBlurEvents.call( + this, + this.focusableElement, + ); - get downloadedResources() { - if (this.isFromSearch) { - return this.searchDownloadedResources; - } - - return this.defaultDownloadedResources; - } - - set downloadedResources(value) { - if (this.isFromSearch) { - this.searchDownloadedResources = value; - } - else { - this.defaultDownloadedResources = value; - } - } - - addPlaceholder() { - if (!this.component.placeholder) { - return; - } - - this.addOption('', this.component.placeholder, { placeholder: true }); - } - - /** - * Activate this select control. - */ - activate() { - if (this.loading || !this.active) { - this.setLoadingItem(); - } - if (this.active) { - return; - } - this.activated = true; - this.triggerUpdate(); - } - - setLoadingItem(addToCurrentList = false) { - if (this.choices) { - if (addToCurrentList) { - this.choices.setChoices([{ - value: `${this.id}-loading`, - label: 'Loading...', - disabled: true, - }], 'value', 'label'); - } - else { - this.choices.setChoices([{ - value: '', - label: ``, - disabled: true, - }], 'value', 'label', true); - } - } - else if (this.component.dataSrc === 'url' || this.component.dataSrc === 'resource') { - this.addOption('', this.t('loading...')); - } - } - - get active() { - return !this.component.lazyLoad || this.activated; - } - - render() { - const info = this.inputInfo; - info.attr = info.attr || {}; - info.multiple = this.component.multiple; - return super.render(this.wrapElement(this.renderTemplate('select', { - input: info, - selectOptions: '', - index: null, - }))); - } - - wrapElement(element) { - return this.component.addResource && !this.options.readOnly - ? ( - this.renderTemplate('resourceAdd', { - element - }) - ) - : element; - } - - choicesOptions() { - const useSearch = Object.prototype.hasOwnProperty.call(this.component, 'searchEnabled') ? this.component.searchEnabled : true; - const placeholderValue = this.t(this.component.placeholder, { _userInput: true }); - let customOptions = this.component.customOptions || {}; - if (typeof customOptions == 'string') { - try { - customOptions = JSON.parse(customOptions); - } - catch (err) { - console.warn(err.message); - customOptions = {}; - } - } - - const commonFuseOptions = { - maxPatternLength: 1000, - distance: 1000, - }; - - return { - removeItemButton: this.component.disabled ? false : _.get(this.component, 'removeItemButton', true), - itemSelectText: '', - classNames: { - containerOuter: 'choices form-group formio-choices', - containerInner: this.transform('class', 'form-control ui fluid selection dropdown') - }, - addItemText: false, - allowHTML: true, - placeholder: !!this.component.placeholder, - placeholderValue: placeholderValue, - noResultsText: this.t('No results found'), - noChoicesText: this.t('No choices to choose from'), - searchPlaceholderValue: this.t('Type to search'), - shouldSort: false, - position: (this.component.dropdown || 'auto'), - searchEnabled: useSearch, - searchChoices: !this.component.searchField, - searchFields: _.get(this, 'component.searchFields', ['label']), - shadowRoot: this.root ? this.root.shadowRoot : null, - fuseOptions: this.component.useExactSearch - ? { - tokenize: true, - matchAllTokens: true, - ...commonFuseOptions - } - : Object.assign( - {}, - _.get(this, 'component.fuseOptions', {}), - { - include: 'score', - threshold: _.get(this, 'component.selectThreshold', 0.3), - ...commonFuseOptions - } - ), - valueComparer: _.isEqual, - resetScrollPosition: false, - ...customOptions, - }; - } - - /* eslint-disable max-statements */ - attach(element) { - const superAttach = super.attach(element); - this.loadRefs(element, { - selectContainer: 'single', - addResource: 'single', - autocompleteInput: 'single' - }); - //enable autocomplete for select - const autocompleteInput = this.refs.autocompleteInput; - if (autocompleteInput) { - this.addEventListener(autocompleteInput, 'change', (event) => { - this.setValue(event.target.value); - }); - } - const input = this.refs.selectContainer; - if (!input) { - return; - } - this.addEventListener(input, this.inputInfo.changeEvent, () => this.updateValue(null, { - modified: true - })); - this.attachRefreshOnBlur(); - - if (this.component.widget === 'html5') { - this.addFocusBlurEvents(input); - this.triggerUpdate(null, true); - - if (this.visible) { - this.setItems(this.selectItems || []); - } - - this.focusableElement = input; - - if (this.component.dataSrc === 'custom') { - this.addEventListener(input, 'focus', () => this.updateCustomItems()); - } - - this.addEventListener(input, 'keydown', (event) => { - const { key } = event; - - if (['Backspace', 'Delete'].includes(key)) { - this.setValue(this.emptyValue); - } - }); - - return; - } - - const tabIndex = input.tabIndex; - this.addPlaceholder(); - if (this.i18next) { - input.setAttribute('dir', this.i18next.dir()); - } - if (this.choices?.containerOuter?.element?.parentNode) { - this.choices.destroy(); - } - - const choicesOptions = this.choicesOptions(); - - if (Choices) { - this.choices = new Choices(input, choicesOptions); - - if (this.selectOptions && this.selectOptions.length) { - this.choices.setChoices(this.selectOptions, 'value', 'label', true); - } - - if (this.component.multiple) { - this.focusableElement = this.choices.input.element; - } - else { - this.focusableElement = this.choices.containerInner.element; - this.choices.containerOuter.element.setAttribute('tabIndex', '-1'); - this.addEventListener(this.choices.containerOuter.element, 'focus', () => this.focusableElement.focus()); - } - - Input.prototype.addFocusBlurEvents.call(this, this.focusableElement); - - if (this.itemsFromUrl && !this.component.noRefreshOnScroll) { - this.scrollList = this.choices.choiceList.element; - this.addEventListener(this.scrollList, 'scroll', () => this.onScroll()); - } - - if (choicesOptions.removeItemButton) { - this.addEventListener(input, 'removeItem', () => { - this.isRemoveButtonPressed = true; - }); - } - } + if (this.itemsFromUrl && !this.component.noRefreshOnScroll) { + this.scrollList = this.choices.choiceList.element; + this.addEventListener(this.scrollList, 'scroll', () => + this.onScroll(), + ); + } - if (window && this.choices && this.shouldPositionDropdown) { - this.addEventListener(window.document, 'scroll', () => { - this.positionDropdown(true); - }, false, true); - } + if (choicesOptions.removeItemButton) { + this.addEventListener(input, 'removeItem', () => { + this.isRemoveButtonPressed = true; + }); + } + } - this.focusableElement.setAttribute('tabIndex', tabIndex); + if (window && this.choices && this.shouldPositionDropdown) { + this.addEventListener( + window.document, + 'scroll', + () => { + this.positionDropdown(true); + }, + false, + true, + ); + } - // If a search field is provided, then add an event listener to update items on search. - if (this.component.searchField) { - // Make sure to clear the search when no value is provided. - if (this.choices && this.choices.input && this.choices.input.element) { - this.addEventListener(this.choices.input.element, 'input', (event) => { - this.isFromSearch = !!event.target.value; + this.focusableElement.setAttribute('tabIndex', tabIndex); + + // If a search field is provided, then add an event listener to update items on search. + if (this.component.searchField) { + // Make sure to clear the search when no value is provided. + if ( + this.choices && + this.choices.input && + this.choices.input.element + ) { + this.addEventListener( + this.choices.input.element, + 'input', + (event) => { + this.isFromSearch = !!event.target.value; + + if (!event.target.value) { + this.triggerUpdate(); + } else { + this.serverCount = null; + this.downloadedResources = []; + } + }, + ); + } - if (!event.target.value) { - this.triggerUpdate(); - } - else { - this.serverCount = null; - this.downloadedResources = []; - } + this.addEventListener(input, 'choice', () => { + if ( + this.component.multiple && + this.component.dataSrc === 'resource' && + this.isFromSearch + ) { + this.triggerUpdate(); + } + this.isFromSearch = false; + }); + // avoid spamming the resource/url endpoint when we have server side filtering enabled. + const debounceTimeout = + this.component.searchField && + (this.isSelectResource || this.isSelectURL) + ? (this.component.searchDebounce === 0 + ? 0 + : this.component.searchDebounce || + this.defaultSchema.searchDebounce) * 1000 + : 0; + const updateComponent = (evt) => { + this.triggerUpdate(evt.detail.value); + }; + + this.addEventListener( + input, + 'search', + _.debounce((e) => { + updateComponent(e); + this.positionDropdown(); + }, debounceTimeout), + ); + + this.addEventListener(input, 'stopSearch', () => + this.triggerUpdate(), + ); + this.addEventListener(input, 'hideDropdown', () => { + if ( + this.choices && + this.choices.input && + this.choices.input.element + ) { + this.choices.input.element.value = ''; + } + + this.updateItems(null, true); + }); + } + + this.addEventListener(input, 'showDropdown', () => { + this.update(); + this.positionDropdown(); }); - } - this.addEventListener(input, 'choice', () => { - if (this.component.multiple && this.component.dataSrc === 'resource' && this.isFromSearch) { - this.triggerUpdate(); + if (this.shouldPositionDropdown) { + this.addEventListener(input, 'highlightChoice', () => { + this.positionDropdown(); + }); } - this.isFromSearch = false; - }); - // avoid spamming the resource/url endpoint when we have server side filtering enabled. - const debounceTimeout = this.component.searchField && (this.isSelectResource || this.isSelectURL) ? - (this.component.searchDebounce === 0 ? 0 : this.component.searchDebounce || this.defaultSchema.searchDebounce) * 1000 - : 0; - const updateComponent = (evt) => { - this.triggerUpdate(evt.detail.value); - }; - this.addEventListener(input, 'search', _.debounce((e) => { - updateComponent(e); - this.positionDropdown(); - }, debounceTimeout)); + if ( + this.choices && + choicesOptions.placeholderValue && + this.choices._isSelectOneElement + ) { + this.addPlaceholderItem(choicesOptions.placeholderValue); + + this.addEventListener(input, 'removeItem', () => { + this.addPlaceholderItem(choicesOptions.placeholderValue); + }); + } - this.addEventListener(input, 'stopSearch', () => this.triggerUpdate()); - this.addEventListener(input, 'hideDropdown', () => { - if (this.choices && this.choices.input && this.choices.input.element) { - this.choices.input.element.value = ''; + // Add value options. + this.addValueOptions(); + this.setChoicesValue(this.dataValue); + + if (this.isSelectResource && this.refs.addResource) { + this.addEventListener(this.refs.addResource, 'click', (event) => { + event.preventDefault(); + + const formioForm = this.ce('div'); + const dialog = this.createModal(formioForm); + + const projectUrl = _.get( + this.root, + 'formio.projectUrl', + Formio.getProjectUrl(), + ); + const formUrl = `${projectUrl}/form/${this.component.data.resource}`; + new Form(formioForm, formUrl, {}).ready.then((form) => { + form.on('submit', (submission) => { + // If valueProperty is set, replace the submission with the corresponding value + let value = this.valueProperty + ? _.get(submission, this.valueProperty) + : submission; + + if (this.component.multiple) { + value = [...this.dataValue, value]; + } + this.setValue(value); + this.triggerUpdate(); + dialog.close(); + }); + }); + }); } - this.updateItems(null, true); - }); + // Force the disabled state with getters and setters. + this.disabled = this.shouldDisabled; + this.triggerUpdate(); + return superAttach; } - this.addEventListener(input, 'showDropdown', () => { - this.update(); - this.positionDropdown(); - }); + setDropdownPosition() { + const dropdown = this.choices?.dropdown?.element; + const container = this.choices?.containerOuter?.element; - if (this.shouldPositionDropdown) { - this.addEventListener(input, 'highlightChoice', () => { - this.positionDropdown(); - }); + if (!dropdown || !container) { + return; + } + + const containerPosition = container.getBoundingClientRect(); + const isFlipped = container.classList.contains('is-flipped'); + + _.assign(dropdown.style, { + top: `${ + isFlipped + ? containerPosition.top - dropdown.offsetHeight + : containerPosition.top + containerPosition.height + }px`, + left: `${containerPosition.left}px`, + width: `${containerPosition.width}px`, + position: 'fixed', + bottom: 'unset', + right: 'unset', + }); } - if (this.choices && choicesOptions.placeholderValue && this.choices._isSelectOneElement) { - this.addPlaceholderItem(choicesOptions.placeholderValue); + hasDataGridAncestor(comp) { + comp = comp || this; - this.addEventListener(input, 'removeItem', () => { - this.addPlaceholderItem(choicesOptions.placeholderValue); - }); + if (comp.inDataGrid || comp.type === 'datagrid') { + return true; + } else if (comp.parent) { + return this.hasDataGridAncestor(comp.parent); + } else { + return false; + } } - // Add value options. - this.addValueOptions(); - this.setChoicesValue(this.dataValue); - - if (this.isSelectResource && this.refs.addResource) { - this.addEventListener(this.refs.addResource, 'click', (event) => { - event.preventDefault(); + positionDropdown(scroll) { + if ( + !this.shouldPositionDropdown || + !this.choices || + (!this.choices.dropdown?.isActive && scroll) + ) { + return; + } - const formioForm = this.ce('div'); - const dialog = this.createModal(formioForm); + this.setDropdownPosition(); - const projectUrl = _.get(this.root, 'formio.projectUrl', Formio.getProjectUrl()); - const formUrl = `${projectUrl}/form/${this.component.data.resource}`; - new Form(formioForm, formUrl, {}).ready - .then((form) => { - form.on('submit', (submission) => { - // If valueProperty is set, replace the submission with the corresponding value - let value = this.valueProperty ? _.get(submission, this.valueProperty) : submission; + this.itemsLoaded.then(() => { + this.setDropdownPosition(); + }); + } - if (this.component.multiple) { - value = [...this.dataValue, value]; - } - this.setValue(value); - this.triggerUpdate(); - dialog.close(); - }); - }); - }); + get isLoadingAvailable() { + return !this.isScrollLoading && this.additionalResourcesAvailable; } - // Force the disabled state with getters and setters. - this.disabled = this.shouldDisabled; - this.triggerUpdate(); - return superAttach; - } + onScroll() { + if (this.isLoadingAvailable) { + this.isScrollLoading = true; + this.setLoadingItem(true); + this.triggerUpdate(this.choices.input.element.value); + } + } - setDropdownPosition() { - const dropdown = this.choices?.dropdown?.element; - const container = this.choices?.containerOuter?.element; + attachRefreshOnBlur() { + if (this.component.refreshOnBlur) { + this.on('blur', (instance) => { + this.checkRefreshOn([{ instance, value: instance.dataValue }], { + fromBlur: true, + }); + }); + } + } - if (!dropdown || !container) { - return; + addPlaceholderItem(placeholderValue) { + const items = this.choices._store.activeItems; + if (!items.length) { + this.choices._addItem({ + value: '', + label: placeholderValue, + choiceId: 0, + groupId: -1, + customProperties: null, + placeholder: true, + keyCode: null, + }); + } } - const containerPosition = container.getBoundingClientRect(); - const isFlipped = container.classList.contains('is-flipped'); + /* eslint-enable max-statements */ + update() { + if (this.component.dataSrc === 'custom') { + this.updateCustomItems(); + } + // Activate the control. + this.activate(); + } - _.assign(dropdown.style, { - top: `${isFlipped ? containerPosition.top - dropdown.offsetHeight : containerPosition.top + containerPosition.height}px`, - left: `${containerPosition.left}px`, - width: `${containerPosition.width}px`, - position: 'fixed', - bottom: 'unset', - right: 'unset', - }); - } + set disabled(disabled) { + super.disabled = disabled; + if (!this.choices) { + return; + } + if (disabled) { + this.setDisabled(this.choices.containerInner.element, true); + this.focusableElement.removeAttribute('tabIndex'); + this.choices.disable(); + } else { + this.setDisabled(this.choices.containerInner.element, false); + this.focusableElement.setAttribute( + 'tabIndex', + this.component.tabindex || 0, + ); + this.choices.enable(); + } + } - hasDataGridAncestor(comp) { - comp = comp || this; + get disabled() { + return super.disabled; + } - if (comp.inDataGrid || comp.type === 'datagrid') { - return true; + set visible(value) { + // If we go from hidden to visible, trigger a refresh. + if (value && !this._visible !== !value) { + this.triggerUpdate(); + } + super.visible = value; } - else if (comp.parent) { - return this.hasDataGridAncestor(comp.parent); - } - else { - return false; + + get visible() { + return super.visible; } - } - - positionDropdown(scroll) { - if (!this.shouldPositionDropdown || !this.choices || (!this.choices.dropdown?.isActive && scroll)) { - return; - } - - this.setDropdownPosition(); - - this.itemsLoaded.then(() => { - this.setDropdownPosition(); - }); - } - - get isLoadingAvailable() { - return !this.isScrollLoading && this.additionalResourcesAvailable; - } - - onScroll() { - if (this.isLoadingAvailable) { - this.isScrollLoading = true; - this.setLoadingItem(true); - this.triggerUpdate(this.choices.input.element.value); - } - } - - attachRefreshOnBlur() { - if (this.component.refreshOnBlur) { - this.on('blur', (instance) => { - this.checkRefreshOn([{ instance, value: instance.dataValue }], { fromBlur: true }); - }); - } - } - - addPlaceholderItem(placeholderValue) { - const items = this.choices._store.activeItems; - if (!items.length) { - this.choices._addItem({ - value: '', - label: placeholderValue, - choiceId: 0, - groupId: -1, - customProperties: null, - placeholder: true, - keyCode: null - }); - } - } - - /* eslint-enable max-statements */ - update() { - if (this.component.dataSrc === 'custom') { - this.updateCustomItems(); - } - // Activate the control. - this.activate(); - } - - set disabled(disabled) { - super.disabled = disabled; - if (!this.choices) { - return; - } - if (disabled) { - this.setDisabled(this.choices.containerInner.element, true); - this.focusableElement.removeAttribute('tabIndex'); - this.choices.disable(); - } - else { - this.setDisabled(this.choices.containerInner.element, false); - this.focusableElement.setAttribute('tabIndex', this.component.tabindex || 0); - this.choices.enable(); - } - } - - get disabled() { - return super.disabled; - } - - set visible(value) { - // If we go from hidden to visible, trigger a refresh. - if (value && (!this._visible !== !value)) { - this.triggerUpdate(); - } - super.visible = value; - } - - get visible() { - return super.visible; - } - - /** - * @param {*} value - * @param {Array} items - */ - addCurrentChoices(values, items, keyValue) { - if (!values) { - return false; - } - const notFoundValuesToAdd = []; - const added = values.reduce((defaultAdded, value) => { - if (!value || _.isEmpty(value)) { - return defaultAdded; - } - let found = false; - - // Make sure that `items` and `this.selectOptions` points - // to the same reference. Because `this.selectOptions` is - // internal property and all items are populated by - // `this.addOption` method, we assume that items has - // 'label' and 'value' properties. This assumption allows - // us to read correct value from the item. - const isSelectOptions = items === this.selectOptions; - if (items && items.length) { - _.each(items, (choice) => { - if (choice._id && value._id && (choice._id === value._id)) { - found = true; + + /** + * @param {*} value + * @param {Array} items + */ + addCurrentChoices(values, items, keyValue) { + if (!values) { return false; - } - const itemValue = keyValue ? choice.value : this.itemValue(choice, isSelectOptions); - found |= _.isEqual(itemValue, value); - return found ? false : true; - }); - } - - // Add the default option if no item is found. - if (!found) { - notFoundValuesToAdd.push(this.selectValueAndLabel(value)); - return true; - } - return found || defaultAdded; - }, false); - - if (notFoundValuesToAdd.length) { - if (this.choices) { - this.choices.setChoices(notFoundValuesToAdd, 'value', 'label'); - } - notFoundValuesToAdd.map(notFoundValue => { - this.addOption(notFoundValue.value, notFoundValue.label); - }); - } - return added; - } - - getValueAsString(data, options) { - return (this.component.multiple && Array.isArray(data)) - ? data.map((v) => this.asString(v, options)).join(', ') - : this.asString(data, options); - } - - getValue() { - // If the widget isn't active. - if ( - this.viewOnly || this.loading - || (!this.component.lazyLoad && !this.selectOptions.length) - || !this.element - ) { - return this.dataValue; - } + } + const notFoundValuesToAdd = []; + const added = values.reduce((defaultAdded, value) => { + if (!value || _.isEmpty(value)) { + return defaultAdded; + } + let found = false; + + // Make sure that `items` and `this.selectOptions` points + // to the same reference. Because `this.selectOptions` is + // internal property and all items are populated by + // `this.addOption` method, we assume that items has + // 'label' and 'value' properties. This assumption allows + // us to read correct value from the item. + const isSelectOptions = items === this.selectOptions; + if (items && items.length) { + _.each(items, (choice) => { + if (choice._id && value._id && choice._id === value._id) { + found = true; + return false; + } + const itemValue = keyValue + ? choice.value + : this.itemValue(choice, isSelectOptions); + found |= _.isEqual(itemValue, value); + return found ? false : true; + }); + } + + // Add the default option if no item is found. + if (!found) { + notFoundValuesToAdd.push(this.selectValueAndLabel(value)); + return true; + } + return found || defaultAdded; + }, false); - let value = this.emptyValue; - if (this.choices) { - value = this.choices.getValue(true); + if (notFoundValuesToAdd.length) { + if (this.choices) { + this.choices.setChoices(notFoundValuesToAdd, 'value', 'label'); + } + notFoundValuesToAdd.map((notFoundValue) => { + this.addOption(notFoundValue.value, notFoundValue.label); + }); + } + return added; + } - // Make sure we don't get the placeholder - if ( - !this.component.multiple && - this.component.placeholder && - (value === this.t(this.component.placeholder, { _userInput: true })) - ) { - value = this.emptyValue; - } + getValueAsString(data, options) { + return this.component.multiple && Array.isArray(data) + ? data.map((v) => this.asString(v, options)).join(', ') + : this.asString(data, options); } - else if (this.refs.selectContainer) { - value = this.refs.selectContainer.value; - if (this.valueProperty === '' || this.isEntireObjectDisplay()) { - if (value === '') { - return {}; + getValue() { + // If the widget isn't active. + if ( + this.viewOnly || + this.loading || + (!this.component.lazyLoad && !this.selectOptions.length) || + !this.element + ) { + return this.dataValue; } - const option = this.selectOptions[value] || - this.selectOptions.find(option => option.id === value); - if (option && _.isObject(option.value)) { - value = option.value; + let value = this.emptyValue; + if (this.choices) { + value = this.choices.getValue(true); + + // Make sure we don't get the placeholder + if ( + !this.component.multiple && + this.component.placeholder && + value === + this.t(this.component.placeholder, { _userInput: true }) + ) { + value = this.emptyValue; + } + } else if (this.refs.selectContainer) { + value = this.refs.selectContainer.value; + + if (this.valueProperty === '' || this.isEntireObjectDisplay()) { + if (value === '') { + return {}; + } + + const option = + this.selectOptions[value] || + this.selectOptions.find((option) => option.id === value); + if (option && _.isObject(option.value)) { + value = option.value; + } + } + } else { + value = this.dataValue; } - } - } - else { - value = this.dataValue; + // Choices will return undefined if nothing is selected. We really want '' to be empty. + if (value === undefined || value === null) { + value = ''; + } + return value; } - // Choices will return undefined if nothing is selected. We really want '' to be empty. - if (value === undefined || value === null) { - value = ''; + + redraw() { + const done = super.redraw(); + this.triggerUpdate(); + return done; } - return value; - } - redraw() { - const done = super.redraw(); - this.triggerUpdate(); - return done; - } + normalizeSingleValue(value, retainObject) { + if (_.isNil(value)) { + return; + } + const valueIsObject = _.isObject(value); + //check if value equals to default emptyValue + if (valueIsObject && Object.keys(value).length === 0) { + return value; + } + // Check to see if we need to save off the template data into our metadata. + if (retainObject) { + const templateValue = + this.component.reference && value?._id + ? value._id.toString() + : value; + const shouldSaveData = !valueIsObject || this.component.reference; + if ( + templateValue && + shouldSaveData && + this.templateData && + this.templateData[templateValue] && + this.root?.submission + ) { + const submission = this.root.submission; + if (!submission.metadata) { + submission.metadata = {}; + } + if (!submission.metadata.selectData) { + submission.metadata.selectData = {}; + } + + let templateData = this.templateData[templateValue]; + if (this.component.multiple) { + templateData = {}; + const dataValue = this.dataValue; + if (dataValue && _.isArray(dataValue) && dataValue.length) { + dataValue.forEach((dataValueItem) => { + const dataValueItemValue = this.component.reference + ? dataValueItem._id.toString() + : dataValueItem; + templateData[dataValueItemValue] = + this.templateData[dataValueItemValue]; + }); + } + templateData[value] = this.templateData[value]; + } + + _.set(submission.metadata.selectData, this.path, templateData); + } + } - normalizeSingleValue(value, retainObject) { - if (_.isNil(value)) { - return; - } - const valueIsObject = _.isObject(value); - //check if value equals to default emptyValue - if (valueIsObject && Object.keys(value).length === 0) { - return value; - } - // Check to see if we need to save off the template data into our metadata. - if (retainObject) { - const templateValue = this.component.reference && value?._id ? value._id.toString() : value; - const shouldSaveData = !valueIsObject || this.component.reference; - if (templateValue && shouldSaveData && (this.templateData && this.templateData[templateValue]) && this.root?.submission) { - const submission = this.root.submission; - if (!submission.metadata) { - submission.metadata = {}; + const dataType = this.component.dataType || 'auto'; + const normalize = { + value, + + number() { + const numberValue = Number(this.value); + const isEquivalent = + value.toString() === numberValue.toString(); + + if ( + !Number.isNaN(numberValue) && + Number.isFinite(numberValue) && + value !== '' && + isEquivalent + ) { + this.value = numberValue; + } + + return this; + }, + + boolean() { + if ( + _.isString(this.value) && + (this.value.toLowerCase() === 'true' || + this.value.toLowerCase() === 'false') + ) { + this.value = this.value.toLowerCase() === 'true'; + } + + return this; + }, + + string() { + this.value = String(this.value); + return this; + }, + + object() { + return this; + }, + + auto() { + if (_.isObject(this.value)) { + this.value = this.object().value; + } else { + this.value = this.string().number().boolean().value; + } + + return this; + }, + }; + + try { + return normalize[dataType]().value; + } catch (err) { + console.warn('Failed to normalize value', err); + return value; } - if (!submission.metadata.selectData) { - submission.metadata.selectData = {}; + } + + /** + * Normalize values coming into updateValue. + * + * @param value + * @return {*} + */ + normalizeValue(value) { + if (this.component.multiple && Array.isArray(value)) { + return value.map((singleValue) => + this.normalizeSingleValue(singleValue, true), + ); } - let templateData = this.templateData[templateValue]; - if (this.component.multiple) { - templateData = {}; - const dataValue = this.dataValue; - if (dataValue && _.isArray(dataValue) && dataValue.length) { - dataValue.forEach((dataValueItem) => { - const dataValueItemValue = this.component.reference ? dataValueItem._id.toString() : dataValueItem; - templateData[dataValueItemValue] = this.templateData[dataValueItemValue]; + return super.normalizeValue(this.normalizeSingleValue(value, true)); + } + + setValue(value, flags = {}) { + const previousValue = this.dataValue; + if ( + this.component.widget === 'html5' && + (_.isEqual(value, previousValue) || + (_.isEqual(previousValue, {}) && _.isEqual(flags, {}))) && + !flags.fromSubmission + ) { + return false; + } + const changed = this.updateValue(value, flags); + value = this.dataValue; + const hasPreviousValue = !this.isEmpty(previousValue); + const hasValue = !this.isEmpty(value); + + // Undo typing when searching to set the value. + if (this.component.multiple && Array.isArray(value)) { + value = value.map((value) => { + if (typeof value === 'boolean' || typeof value === 'number') { + return value.toString(); + } + return value; }); - } - templateData[value] = this.templateData[value]; + } else { + if (typeof value === 'boolean' || typeof value === 'number') { + value = value.toString(); + } } - _.set(submission.metadata.selectData, this.path, templateData); - } - } + if ( + this.isHtmlRenderMode() && + flags && + flags.fromSubmission && + changed + ) { + this.itemsLoaded.then(() => { + this.redraw(); + }); - const dataType = this.component.dataType || 'auto'; - const normalize = { - value, + return changed; + } - number() { - const numberValue = Number(this.value); - const isEquivalent = value.toString() === numberValue.toString(); + // Do not set the value if we are loading... that will happen after it is done. + if (this.loading) { + return changed; + } - if (!Number.isNaN(numberValue) && Number.isFinite(numberValue) && value !== '' && isEquivalent) { - this.value = numberValue; + // Determine if we need to perform an initial lazyLoad api call if searchField is provided. + if (this.isInitApiCallNeeded(hasValue)) { + this.loading = true; + this.lazyLoadInit = true; + const searchProperty = + this.component.searchField || this.component.valueProperty; + this.triggerUpdate( + _.get(value.data || value, searchProperty, value), + true, + ); + return changed; } - return this; - }, + // Add the value options. + this.itemsLoaded.then(() => { + this.addValueOptions(); + this.setChoicesValue(value, hasPreviousValue, flags); + }); - boolean() { - if ( - _.isString(this.value) - && (this.value.toLowerCase() === 'true' - || this.value.toLowerCase() === 'false') - ) { - this.value = (this.value.toLowerCase() === 'true'); + return changed; + } + + isInitApiCallNeeded(hasValue) { + return ( + this.component.lazyLoad && + !this.lazyLoadInit && + !this.active && + !this.selectOptions.length && + hasValue && + this.shouldInitialLoad && + this.visible && + (this.component.searchField || this.component.valueProperty) + ); + } + + setChoicesValue(value, hasPreviousValue, flags = {}) { + const hasValue = !this.isEmpty(value) || flags.fromSubmission; + hasPreviousValue = + hasPreviousValue === undefined ? true : hasPreviousValue; + if (this.choices) { + // Now set the value. + if (hasValue) { + this.choices.removeActiveItems(); + // Add the currently selected choices if they don't already exist. + const currentChoices = + Array.isArray(value) && this.component.multiple + ? value + : [value]; + if ( + !this.addCurrentChoices( + currentChoices, + this.selectOptions, + true, + ) + ) { + this.choices.setChoices( + this.selectOptions, + 'value', + 'label', + true, + ); + } + this.choices.setChoiceByValue(currentChoices); + } else if (hasPreviousValue || flags.resetValue) { + this.choices.removeActiveItems(); + } + } else { + if (hasValue) { + const values = Array.isArray(value) ? value : [value]; + if ( + (!_.isEqual(this.dataValue, this.defaultValue) && + this.selectOptions.length < 2) || + (this.selectData && flags.fromSubmission) + ) { + const { value, label } = this.selectValueAndLabel( + this.dataValue, + ); + this.addOption(value, label); + } + _.each(this.selectOptions, (selectOption) => { + _.each(values, (val) => { + if (selectOption.value === '') { + selectOption.value = {}; + } + if ( + _.isEqual(val, selectOption.value) && + selectOption.element + ) { + selectOption.element.selected = true; + selectOption.element.setAttribute( + 'selected', + 'selected', + ); + return false; + } + }); + }); + } else { + _.each(this.selectOptions, (selectOption) => { + if (selectOption.element) { + selectOption.element.selected = false; + selectOption.element.removeAttribute('selected'); + } + }); + } } + } + + get itemsLoaded() { + return this._itemsLoaded || Promise.resolve(); + } - return this; - }, + set itemsLoaded(promise) { + this._itemsLoaded = promise; + } - string() { - this.value = String(this.value); - return this; - }, + validateValueAvailability(setting, value) { + if (!boolValue(setting) || !value) { + return true; + } - object() { - return this; - }, + const values = this.getOptionsValues(); + + if (values) { + if (_.isObject(value)) { + const compareComplexValues = (optionValue) => { + const normalizedOptionValue = this.normalizeSingleValue( + optionValue, + true, + ); + + if (!_.isObject(normalizedOptionValue)) { + return false; + } + + try { + return ( + JSON.stringify(normalizedOptionValue) === + JSON.stringify(value) + ); + } catch (err) { + console.warn.error('Error while comparing items', err); + return false; + } + }; + + return ( + values.findIndex((optionValue) => + compareComplexValues(optionValue), + ) !== -1 + ); + } - auto() { - if (_.isObject(this.value)) { - this.value = this.object().value; + return ( + values.findIndex( + (optionValue) => + this.normalizeSingleValue(optionValue) === value, + ) !== -1 + ); } - else { - this.value = this.string().number().boolean().value; + return false; + } + + /** + * Performs required transformations on the initial value to use in selectOptions + * @param {*} value + */ + getOptionValue(value) { + return _.isObject(value) && this.isEntireObjectDisplay() + ? this.normalizeSingleValue(value) + : _.isObject(value) && + (this.valueProperty || this.component.key !== 'resource') + ? value + : _.isObject(value) && !this.valueProperty + ? this.interpolate(this.component.template, { + item: value, + }).replace(/<\/?[^>]+(>|$)/g, '') + : _.isNull(value) + ? this.emptyValue + : String(this.normalizeSingleValue(value)); + } + + /** + * If component has static values (values, json) or custom values, returns an array of them + * @returns {Array<*>|undefined} + */ + getOptionsValues() { + let rawItems = []; + switch (this.component.dataSrc) { + case 'values': + rawItems = this.component.data.values; + break; + case 'json': + rawItems = this.component.data.json; + break; + case 'custom': + rawItems = this.getCustomItems(); + break; } - return this; - } - }; + if (typeof rawItems === 'string') { + try { + rawItems = JSON.parse(rawItems); + } catch (err) { + console.warn(err.message); + rawItems = []; + } + } - try { - return normalize[dataType]().value; + if (!Array.isArray(rawItems)) { + return; + } + + return rawItems.map((item) => + this.getOptionValue(this.itemValue(item)), + ); } - catch (err) { - console.warn('Failed to normalize value', err); - return value; + + /** + * Deletes the value of the component. + */ + deleteValue() { + this.setValue('', { + noUpdateEvent: true, + }); + this.unset(); } - } - /** - * Normalize values coming into updateValue. - * - * @param value - * @return {*} - */ - normalizeValue(value) { - if (this.component.multiple && Array.isArray(value)) { - return value.map((singleValue) => this.normalizeSingleValue(singleValue, true)); + /** + * Check if a component is eligible for multiple validation + * + * @return {boolean} + */ + validateMultiple() { + // Select component will contain one input when flagged as multiple. + return false; } - return super.normalizeValue(this.normalizeSingleValue(value, true)); - } + /** + * Output this select dropdown as a string value. + * @return {*} + */ - setValue(value, flags = {}) { - const previousValue = this.dataValue; - if (this.component.widget === 'html5' && (_.isEqual(value, previousValue) || _.isEqual(previousValue, {}) && _.isEqual(flags, {})) && !flags.fromSubmission ) { - return false; + isBooleanOrNumber(value) { + return typeof value === 'number' || typeof value === 'boolean'; } - const changed = this.updateValue(value, flags); - value = this.dataValue; - const hasPreviousValue = !this.isEmpty(previousValue); - const hasValue = !this.isEmpty(value); - // Undo typing when searching to set the value. - if (this.component.multiple && Array.isArray(value)) { - value = value.map(value => { - if (typeof value === 'boolean' || typeof value === 'number') { - return value.toString(); + getNormalizedValues() { + if ( + !this.component || + !this.component.data || + !this.component.data.values + ) { + return; } - return value; - }); - } - else { - if (typeof value === 'boolean' || typeof value === 'number') { - value = value.toString(); - } - } - - if (this.isHtmlRenderMode() && flags && flags.fromSubmission && changed) { - this.itemsLoaded.then(() => { - this.redraw(); - }); - - return changed; - } - - // Do not set the value if we are loading... that will happen after it is done. - if (this.loading) { - return changed; - } - - // Determine if we need to perform an initial lazyLoad api call if searchField is provided. - if (this.isInitApiCallNeeded(hasValue)) { - this.loading = true; - this.lazyLoadInit = true; - const searchProperty = this.component.searchField || this.component.valueProperty; - this.triggerUpdate(_.get(value.data || value, searchProperty, value), true); - return changed; - } - - // Add the value options. - this.itemsLoaded.then(() => { - this.addValueOptions(); - this.setChoicesValue(value, hasPreviousValue, flags); - }); - - return changed; - } - - isInitApiCallNeeded(hasValue) { - return this.component.lazyLoad && - !this.lazyLoadInit && - !this.active && - !this.selectOptions.length && - hasValue && - this.shouldInitialLoad && - this.visible && (this.component.searchField || this.component.valueProperty); - } - - setChoicesValue(value, hasPreviousValue, flags = {}) { - const hasValue = !this.isEmpty(value) || flags.fromSubmission; - hasPreviousValue = (hasPreviousValue === undefined) ? true : hasPreviousValue; - if (this.choices) { - // Now set the value. - if (hasValue) { - this.choices.removeActiveItems(); - // Add the currently selected choices if they don't already exist. - const currentChoices = Array.isArray(value) && this.component.multiple ? value : [value]; - if (!this.addCurrentChoices(currentChoices, this.selectOptions, true)) { - this.choices.setChoices(this.selectOptions, 'value', 'label', true); - } - this.choices.setChoiceByValue(currentChoices); - } - else if (hasPreviousValue || flags.resetValue) { - this.choices.removeActiveItems(); - } - } - else { - if (hasValue) { - const values = Array.isArray(value) ? value : [value]; - if (!_.isEqual(this.dataValue, this.defaultValue) && this.selectOptions.length < 2 - || (this.selectData && flags.fromSubmission)) { - const { value, label } = this.selectValueAndLabel(this.dataValue); - this.addOption(value, label); - } - _.each(this.selectOptions, (selectOption) => { - _.each(values, (val) => { - if (selectOption.value === '') { - selectOption.value = {}; - } - if (_.isEqual(val, selectOption.value) && selectOption.element) { - selectOption.element.selected = true; - selectOption.element.setAttribute('selected', 'selected'); - return false; + return this.component.data.values.map((value) => ({ + label: value.label, + value: String(this.normalizeSingleValue(value.value)), + })); + } + + asString(value, options = {}) { + value = value ?? this.getValue(); + //need to convert values to strings to be able to compare values with available options that are strings + const convertToString = (data, valueProperty) => { + if (valueProperty) { + if (Array.isArray(data)) { + data.forEach( + (item) => + (item[valueProperty] = + item[valueProperty].toString()), + ); + } else { + data[valueProperty] = data[valueProperty].toString(); + } + return data; } - }); - }); - } - else { - _.each(this.selectOptions, (selectOption) => { - if (selectOption.element) { - selectOption.element.selected = false; - selectOption.element.removeAttribute('selected'); - } - }); - } - } - } - get itemsLoaded() { - return this._itemsLoaded || Promise.resolve(); - } + if (this.isBooleanOrNumber(data)) { + data = data.toString(); + } - set itemsLoaded(promise) { - this._itemsLoaded = promise; - } + if ( + Array.isArray(data) && + data.some((item) => this.isBooleanOrNumber(item)) + ) { + data = data.map((item) => { + if (this.isBooleanOrNumber(item)) { + item = item.toString(); + } + }); + } - validateValueAvailability(setting, value) { - if (!boolValue(setting) || !value) { - return true; - } + return data; + }; - const values = this.getOptionsValues(); + value = convertToString(value); - if (values) { - if (_.isObject(value)) { - const compareComplexValues = (optionValue) => { - const normalizedOptionValue = this.normalizeSingleValue(optionValue, true); + if ( + ['values', 'custom'].includes(this.component.dataSrc) && + !this.asyncCustomValues() + ) { + const { items, valueProperty } = + this.component.dataSrc === 'values' + ? { + items: convertToString( + this.getNormalizedValues(), + 'value', + ), + valueProperty: 'value', + } + : { + items: convertToString( + this.getCustomItems(), + this.valueProperty, + ), + valueProperty: this.valueProperty, + }; + + const getFromValues = () => { + const initialValue = _.find(items, [valueProperty, value]); + const values = this.defaultSchema.data.values || []; + return _.isEqual(initialValue, values[0]) ? '-' : initialValue; + }; + value = + this.component.multiple && Array.isArray(value) + ? _.filter(items, (item) => value.includes(item.value)) + : valueProperty + ? getFromValues() ?? { value, label: value } + : value; + } - if (!_.isObject(normalizedOptionValue)) { - return false; - } + if (_.isString(value)) { + return value; + } - try { - return (JSON.stringify(normalizedOptionValue) === JSON.stringify(value)); - } - catch (err) { - console.warn.error('Error while comparing items', err); - return false; - } + const getTemplateValue = (v) => { + const itemTemplate = this.itemTemplate(v); + return options.csv && itemTemplate + ? unescapeHTML(itemTemplate) + : itemTemplate; }; - return values.findIndex((optionValue) => compareComplexValues(optionValue)) !== -1; - } - - return values.findIndex((optionValue) => this.normalizeSingleValue(optionValue) === value) !== -1; - } - return false; - } - - /** - * Performs required transformations on the initial value to use in selectOptions - * @param {*} value - */ - getOptionValue(value) { - return _.isObject(value) && this.isEntireObjectDisplay() - ? this.normalizeSingleValue(value) - : _.isObject(value) && (this.valueProperty || this.component.key !== 'resource') - ? value - : _.isObject(value) && !this.valueProperty - ? this.interpolate(this.component.template, { item: value }).replace(/<\/?[^>]+(>|$)/g, '') - : _.isNull(value) - ? this.emptyValue - : String(this.normalizeSingleValue(value)); - } - - /** - * If component has static values (values, json) or custom values, returns an array of them - * @returns {Array<*>|undefined} - */ - getOptionsValues() { - let rawItems = []; - switch (this.component.dataSrc) { - case 'values': - rawItems = this.component.data.values; - break; - case 'json': - rawItems = this.component.data.json; - break; - case 'custom': - rawItems = this.getCustomItems(); - break; - } - - if (typeof rawItems === 'string') { - try { - rawItems = JSON.parse(rawItems); - } - catch (err) { - console.warn(err.message); - rawItems = []; - } - } - - if (!Array.isArray(rawItems)) { - return; - } - - return rawItems.map((item) => this.getOptionValue(this.itemValue(item))); - } - - /** - * Deletes the value of the component. - */ - deleteValue() { - this.setValue('', { - noUpdateEvent: true - }); - this.unset(); - } - - /** - * Check if a component is eligible for multiple validation - * - * @return {boolean} - */ - validateMultiple() { - // Select component will contain one input when flagged as multiple. - return false; - } - - /** - * Output this select dropdown as a string value. - * @return {*} - */ - - isBooleanOrNumber(value) { - return typeof value === 'number' || typeof value === 'boolean'; - } - - getNormalizedValues() { - if (!this.component || !this.component.data || !this.component.data.values) { - return; - } - return this.component.data.values.map( - value => ({ label: value.label, value: String(this.normalizeSingleValue(value.value)) }) - ); - } - - asString(value, options = {}) { - value = value ?? this.getValue(); - //need to convert values to strings to be able to compare values with available options that are strings - const convertToString = (data, valueProperty) => { - if (valueProperty) { - if (Array.isArray(data)) { - data.forEach((item) => item[valueProperty] = item[valueProperty].toString()); + if (Array.isArray(value)) { + const items = []; + value.forEach((item) => items.push(getTemplateValue(item))); + if (this.component.dataSrc === 'resource' && items.length > 0) { + return items.join(', '); + } else if (items.length > 0) { + return items.join('
    '); + } else { + return '-'; + } } - else { - data[valueProperty] = data[valueProperty].toString(); + + if (this.isEntireObjectDisplay() && _.isObject(value)) { + return JSON.stringify(value); } - return data; - } - if (this.isBooleanOrNumber(data)) { - data = data.toString(); - } + return !_.isNil(value) ? getTemplateValue(value) : '-'; + } - if (Array.isArray(data) && data.some(item => this.isBooleanOrNumber(item))) { - data = data.map(item => { - if (this.isBooleanOrNumber(item)) { - item = item.toString(); - } - }); - } - - return data; - }; - - value = convertToString(value); - - if (['values', 'custom'].includes(this.component.dataSrc) && !this.asyncCustomValues()) { - const { - items, - valueProperty, - } = this.component.dataSrc === 'values' - ? { - items: convertToString(this.getNormalizedValues(), 'value'), - valueProperty: 'value', - } - : { - items: convertToString(this.getCustomItems(), this.valueProperty), - valueProperty: this.valueProperty, - }; - - const getFromValues = () => { - const initialValue = _.find(items, [valueProperty, value]); - const values = this.defaultSchema.data.values || []; - return _.isEqual(initialValue, values[0]) ? '-' : initialValue; - }; - value = (this.component.multiple && Array.isArray(value)) - ? _.filter(items, (item) => value.includes(item.value)) - : valueProperty - ? getFromValues() ?? { value, label: value } - : value; - } - - if (_.isString(value)) { - return value; - } - - const getTemplateValue = (v) => { - const itemTemplate = this.itemTemplate(v); - return options.csv && itemTemplate - ? unescapeHTML(itemTemplate) - : itemTemplate; - }; - - if (Array.isArray(value)) { - const items = []; - value.forEach(item => items.push(getTemplateValue(item))); - if (this.component.dataSrc === 'resource' && items.length > 0 ) { - return items.join(', '); - } - else if (items.length > 0) { - return items.join('
    '); - } - else { - return '-'; - } - } - - if (this.isEntireObjectDisplay() && _.isObject(value)) { - return JSON.stringify(value); - } - - return !_.isNil(value) - ? getTemplateValue(value) - : '-'; - } - - detach() { - this.off('blur'); - if (this.choices) { - if (this.choices.containerOuter?.element?.parentNode) { - this.choices.destroy(); - } - this.choices = null; - } - super.detach(); - } - - focus() { - if (this.focusableElement) { - super.focus.call(this); - this.focusableElement.focus(); - } - } - - setErrorClasses(elements, dirty, hasError, hasMessages, element = this.element) { - super.setErrorClasses(elements, dirty, hasError, hasMessages, element); - if (this.choices) { - super.setErrorClasses([this.choices.containerInner.element], dirty, hasError, hasMessages, element); - } - else { - super.setErrorClasses([this.refs.selectContainer], dirty, hasError, hasMessages, element); - } - } + detach() { + this.off('blur'); + if (this.choices) { + if (this.choices.containerOuter?.element?.parentNode) { + this.choices.destroy(); + } + this.choices = null; + } + super.detach(); + } + + focus() { + if (this.focusableElement) { + super.focus.call(this); + this.focusableElement.focus(); + } + } + + setErrorClasses( + elements, + dirty, + hasError, + hasMessages, + element = this.element, + ) { + super.setErrorClasses(elements, dirty, hasError, hasMessages, element); + if (this.choices) { + super.setErrorClasses( + [this.choices.containerInner.element], + dirty, + hasError, + hasMessages, + element, + ); + } else { + super.setErrorClasses( + [this.refs.selectContainer], + dirty, + hasError, + hasMessages, + element, + ); + } + } } diff --git a/src/components/select/Select.unit.js b/src/components/select/Select.unit.js index 49d651ac67..0c3e72169e 100644 --- a/src/components/select/Select.unit.js +++ b/src/components/select/Select.unit.js @@ -9,1206 +9,1444 @@ import { Formio } from './../../Formio'; import _ from 'lodash'; import { - comp1, - comp2, - multiSelect, - multiSelectOptions, - comp4, - comp5, - comp6, - comp7, - comp8, - comp9, - comp10, - comp11, - comp12, - comp13, - comp14, - comp15, - comp16, - comp17, - comp18, - comp19, - comp20, - comp21, + comp1, + comp2, + multiSelect, + multiSelectOptions, + comp4, + comp5, + comp6, + comp7, + comp8, + comp9, + comp10, + comp11, + comp12, + comp13, + comp14, + comp15, + comp16, + comp17, + comp18, + comp19, + comp20, + comp21, } from './fixtures'; // eslint-disable-next-line max-statements -describe('Select Component', function() { - it('should not stringify select option value', function(done) { - Harness.testCreate(SelectComponent, comp6).then((component) => { - component.setValue({ value:'a', label:'A' }); - setTimeout(()=> { - assert.equal(component.choices._currentState.items[0].value.value, 'a'); - assert.equal(typeof component.choices._currentState.items[0].value , 'object'); - assert.equal(component.dataValue.value, 'a'); - assert.equal(typeof component.dataValue , 'object'); - done(); - }, 300); +describe('Select Component', function () { + it('should not stringify select option value', function (done) { + Harness.testCreate(SelectComponent, comp6).then((component) => { + component.setValue({ value: 'a', label: 'A' }); + setTimeout(() => { + assert.equal( + component.choices._currentState.items[0].value.value, + 'a', + ); + assert.equal( + typeof component.choices._currentState.items[0].value, + 'object', + ); + assert.equal(component.dataValue.value, 'a'); + assert.equal(typeof component.dataValue, 'object'); + done(); + }, 300); + }); }); - }); - - it('should return string value for different value types', function(done) { - Harness.testCreate(SelectComponent, comp4).then((component) => { - const stringValue = component.asString(true); - const stringValue1 = component.asString(11); - const stringValue2 = component.asString('test'); - const stringValue3 = component.asString(12); - assert.equal(stringValue, 'true'); - assert.equal(stringValue1, '11'); - assert.equal(stringValue2, 'test'); - assert.equal(stringValue3, '1.2'); - done(); + + it('should return string value for different value types', function (done) { + Harness.testCreate(SelectComponent, comp4).then((component) => { + const stringValue = component.asString(true); + const stringValue1 = component.asString(11); + const stringValue2 = component.asString('test'); + const stringValue3 = component.asString(12); + assert.equal(stringValue, 'true'); + assert.equal(stringValue1, '11'); + assert.equal(stringValue2, 'test'); + assert.equal(stringValue3, '1.2'); + done(); + }); }); - }); - it('Should return plain text when csv option is provided', function() { - return Harness.testCreate(SelectComponent, comp1).then((component) => { - assert.equal(component.getView('red', { csv:true }), 'Red'); + it('Should return plain text when csv option is provided', function () { + return Harness.testCreate(SelectComponent, comp1).then((component) => { + assert.equal(component.getView('red', { csv: true }), 'Red'); + }); }); - }); - - it('should correctly determine storage type when dataType is auto', function(done) { - Harness.testCreate(SelectComponent, comp4).then((component) => { - const value = component.normalizeSingleValue('true'); - const value1 = component.normalizeSingleValue('11'); - const value2 = component.normalizeSingleValue('test'); - const value3 = component.normalizeSingleValue('11test11test'); - const value4 = component.normalizeSingleValue('test11'); - const value5 = component.normalizeSingleValue('0'); - const value6 = component.normalizeSingleValue(''); - assert.equal(typeof value, 'boolean'); - assert.equal(typeof value1, 'number'); - assert.equal(typeof value2, 'string'); - assert.equal(typeof value3, 'string'); - assert.equal(typeof value4, 'string'); - assert.equal(typeof value5, 'number'); - assert.equal(typeof value6, 'string'); - done(); + + it('should correctly determine storage type when dataType is auto', function (done) { + Harness.testCreate(SelectComponent, comp4).then((component) => { + const value = component.normalizeSingleValue('true'); + const value1 = component.normalizeSingleValue('11'); + const value2 = component.normalizeSingleValue('test'); + const value3 = component.normalizeSingleValue('11test11test'); + const value4 = component.normalizeSingleValue('test11'); + const value5 = component.normalizeSingleValue('0'); + const value6 = component.normalizeSingleValue(''); + assert.equal(typeof value, 'boolean'); + assert.equal(typeof value1, 'number'); + assert.equal(typeof value2, 'string'); + assert.equal(typeof value3, 'string'); + assert.equal(typeof value4, 'string'); + assert.equal(typeof value5, 'number'); + assert.equal(typeof value6, 'string'); + done(); + }); }); - }); - - it('should not stringify default empty values', function(done) { - Harness.testCreate(SelectComponent, comp4).then((component) => { - const value = component.normalizeSingleValue({}); - const value1 = component.normalizeSingleValue([]); - assert.equal(typeof value, 'object'); - assert.equal(typeof value1, 'object'); - done(); + + it('should not stringify default empty values', function (done) { + Harness.testCreate(SelectComponent, comp4).then((component) => { + const value = component.normalizeSingleValue({}); + const value1 = component.normalizeSingleValue([]); + assert.equal(typeof value, 'object'); + assert.equal(typeof value1, 'object'); + done(); + }); }); - }); - - it('should not change value letter case', function(done) { - Harness.testCreate(SelectComponent, comp4).then((component) => { - const value = component.normalizeSingleValue('data.textArea'); - const value1 = component.normalizeSingleValue('ECMAScript'); - const value2 = component.normalizeSingleValue('JS'); - assert.equal(value, 'data.textArea'); - assert.equal(value1, 'ECMAScript'); - assert.equal(value2, 'JS'); - done(); + + it('should not change value letter case', function (done) { + Harness.testCreate(SelectComponent, comp4).then((component) => { + const value = component.normalizeSingleValue('data.textArea'); + const value1 = component.normalizeSingleValue('ECMAScript'); + const value2 = component.normalizeSingleValue('JS'); + assert.equal(value, 'data.textArea'); + assert.equal(value1, 'ECMAScript'); + assert.equal(value2, 'JS'); + done(); + }); }); - }); - - it('should define boolean value', function(done) { - Harness.testCreate(SelectComponent, comp4).then((component) => { - const value = component.normalizeSingleValue('TRUE'); - const value1 = component.normalizeSingleValue('False'); - const value2 = component.normalizeSingleValue('true'); - assert.equal(value, true); - assert.equal(value1, false); - assert.equal(value2, true); - done(); + + it('should define boolean value', function (done) { + Harness.testCreate(SelectComponent, comp4).then((component) => { + const value = component.normalizeSingleValue('TRUE'); + const value1 = component.normalizeSingleValue('False'); + const value2 = component.normalizeSingleValue('true'); + assert.equal(value, true); + assert.equal(value1, false); + assert.equal(value2, true); + done(); + }); }); - }); - - it('1/2 should not display empty choice options if property value is not defined', function(done) { - Harness.testCreate(SelectComponent, comp5).then((component) => { - component.setItems([{ - 'label': '111', - 'value': '111' - }, { - 'label': '222', - 'value': '222' - }, { - 'label': '333', - 'value': '333' - }], false); - assert.equal(component.selectOptions.length, 0); - done(); + + it('1/2 should not display empty choice options if property value is not defined', function (done) { + Harness.testCreate(SelectComponent, comp5).then((component) => { + component.setItems( + [ + { + label: '111', + value: '111', + }, + { + label: '222', + value: '222', + }, + { + label: '333', + value: '333', + }, + ], + false, + ); + assert.equal(component.selectOptions.length, 0); + done(); + }); }); - }); - - it('2/2 should display choice option if property value is set', function(done) { - comp5.template = '{{ item.label }}'; - Harness.testCreate(SelectComponent, comp5).then((component) => { - component.setItems([{ - 'label': '111', - 'value': '111' - }, { - 'label': '222', - 'value': '222' - }, { - 'label': '333', - 'value': '333' - }], false); - assert.equal(component.selectOptions.length, 3); - done(); + + it('2/2 should display choice option if property value is set', function (done) { + comp5.template = '{{ item.label }}'; + Harness.testCreate(SelectComponent, comp5).then((component) => { + component.setItems( + [ + { + label: '111', + value: '111', + }, + { + label: '222', + value: '222', + }, + { + label: '333', + value: '333', + }, + ], + false, + ); + assert.equal(component.selectOptions.length, 3); + done(); + }); }); - }); - - it('should have only unique dropdown options', function(done) { - comp5.template = '{{ item.label }}'; - comp5.uniqueOptions = true; - Harness.testCreate(SelectComponent, comp5).then((component) => { - component.setItems([{ - 'label': 'Label 1', - 'value': 'value1' - }, { - 'label': 'Label 2', - 'value': 'value2' - }, { - 'label': 'Label 3', - 'value': 'value3' - }, { - 'label': 'Label 4', - 'value': 'value3' - }], false); - - assert.equal(component.selectOptions.length, 3); - done(); + + it('should have only unique dropdown options', function (done) { + comp5.template = '{{ item.label }}'; + comp5.uniqueOptions = true; + Harness.testCreate(SelectComponent, comp5).then((component) => { + component.setItems( + [ + { + label: 'Label 1', + value: 'value1', + }, + { + label: 'Label 2', + value: 'value2', + }, + { + label: 'Label 3', + value: 'value3', + }, + { + label: 'Label 4', + value: 'value3', + }, + ], + false, + ); + + assert.equal(component.selectOptions.length, 3); + done(); + }); }); - }); - it('should format unlisted values', function(done) { - comp5.template = '{{ item.label }}'; - Harness.testCreate(SelectComponent, comp5).then((component) => { - const formattedValue1 = component.getView('Unlisted value'); - const formattedValue2 = component.getView(0); + it('should format unlisted values', function (done) { + comp5.template = '{{ item.label }}'; + Harness.testCreate(SelectComponent, comp5).then((component) => { + const formattedValue1 = component.getView('Unlisted value'); + const formattedValue2 = component.getView(0); - assert.equal(formattedValue1, 'Unlisted value'); - assert.equal(formattedValue2, '0'); - done(); + assert.equal(formattedValue1, 'Unlisted value'); + assert.equal(formattedValue2, '0'); + done(); + }); }); - }); - - it('should set multiple selected values not repeating them', function(done) { - Harness.testCreate(SelectComponent, multiSelect).then((component) => { - component.setItems(multiSelectOptions, false); - component.setChoicesValue(['Cheers']); - component.setChoicesValue(['Cheers', 'Cyberdyne Systems'], 1); - component.setChoicesValue(['Cheers', 'Cyberdyne Systems', 'Massive Dynamic'], 2); - const choices = component.element.querySelector('.choices__list--multiple').children; - assert.equal(choices.length, 3); - done(); + + it('should set multiple selected values not repeating them', function (done) { + Harness.testCreate(SelectComponent, multiSelect).then((component) => { + component.setItems(multiSelectOptions, false); + component.setChoicesValue(['Cheers']); + component.setChoicesValue(['Cheers', 'Cyberdyne Systems'], 1); + component.setChoicesValue( + ['Cheers', 'Cyberdyne Systems', 'Massive Dynamic'], + 2, + ); + const choices = component.element.querySelector( + '.choices__list--multiple', + ).children; + assert.equal(choices.length, 3); + done(); + }); }); - }); - - it('should not show selected values in dropdown when searching', function(done) { - Harness.testCreate(SelectComponent, multiSelect).then((component) => { - component.setItems(multiSelectOptions, false); - component.setChoicesValue(['Cheers']); - component.setChoicesValue(['Cheers', 'Cyberdyne Systems'], 1); - component.setItems([], true); - const itemsInDropdown = component.element.querySelectorAll('.choices__item--choice'); - const choices = component.element.querySelector('.choices__list--multiple').children; - assert.equal(choices.length, 2); - assert.equal(itemsInDropdown.length, 1); - done(); + + it('should not show selected values in dropdown when searching', function (done) { + Harness.testCreate(SelectComponent, multiSelect).then((component) => { + component.setItems(multiSelectOptions, false); + component.setChoicesValue(['Cheers']); + component.setChoicesValue(['Cheers', 'Cyberdyne Systems'], 1); + component.setItems([], true); + const itemsInDropdown = component.element.querySelectorAll( + '.choices__item--choice', + ); + const choices = component.element.querySelector( + '.choices__list--multiple', + ).children; + assert.equal(choices.length, 2); + assert.equal(itemsInDropdown.length, 1); + done(); + }); + }); + + it('Should build a Select component', function () { + return Harness.testCreate(SelectComponent, comp1).then((component) => { + Harness.testElements(component, 'select', 1); + }); }); - }); - it('Should build a Select component', function() { - return Harness.testCreate(SelectComponent, comp1).then((component) => { - Harness.testElements(component, 'select', 1); + it('Should preserve the tabindex', function () { + return Harness.testCreate(SelectComponent, comp2).then((component) => { + const element = component.element.getElementsByClassName( + 'choices__list choices__list--single', + )[0]; + Harness.testElementAttribute(element, 'tabindex', '10'); + }); }); - }); - it('Should preserve the tabindex', function() { - return Harness.testCreate(SelectComponent, comp2).then((component) => { - const element = component.element.getElementsByClassName('choices__list choices__list--single')[0]; - Harness.testElementAttribute(element, 'tabindex', '10'); + it('Should default to 0 when tabindex is not specified', function () { + return Harness.testCreate(SelectComponent, comp1).then((component) => { + const element = component.element.getElementsByClassName( + 'choices__list choices__list--single', + )[0]; + Harness.testElementAttribute(element, 'tabindex', '0'); + }); }); - }); - it('Should default to 0 when tabindex is not specified', function() { - return Harness.testCreate(SelectComponent, comp1).then((component) => { - const element = component.element.getElementsByClassName('choices__list choices__list--single')[0]; - Harness.testElementAttribute(element, 'tabindex', '0'); + it('Should allow to override threshold option of fuzzy search', function () { + try { + const c1 = Object.assign(cloneDeep(comp1), { + selectThreshold: 0.2, + }); + const c2 = Object.assign(cloneDeep(comp1), { + selectThreshold: 0.4, + }); + const c3 = Object.assign(cloneDeep(comp1), { + selectThreshold: 0.8, + }); + const comps = [ + Harness.testCreate(SelectComponent, c1), + Harness.testCreate(SelectComponent, c2), + Harness.testCreate(SelectComponent, c3), + ]; + + return Promise.all(comps).then(([a, b, c]) => { + expect(a.choices.config.fuseOptions.threshold).to.equal(0.2); + expect(b.choices.config.fuseOptions.threshold).to.equal(0.4); + expect(c.choices.config.fuseOptions.threshold).to.equal(0.8); + }); + } catch (error) { + return Promise.reject(error); + } }); - }); - - it('Should allow to override threshold option of fuzzy search', function() { - try { - const c1 = Object.assign(cloneDeep(comp1), { selectThreshold: 0.2 }); - const c2 = Object.assign(cloneDeep(comp1), { selectThreshold: 0.4 }); - const c3 = Object.assign(cloneDeep(comp1), { selectThreshold: 0.8 }); - const comps = [ - Harness.testCreate(SelectComponent, c1), - Harness.testCreate(SelectComponent, c2), - Harness.testCreate(SelectComponent, c3), - ]; - - return Promise - .all(comps) - .then(([a, b, c]) => { - expect(a.choices.config.fuseOptions.threshold).to.equal(0.2); - expect(b.choices.config.fuseOptions.threshold).to.equal(0.4); - expect(c.choices.config.fuseOptions.threshold).to.equal(0.8); + + it('should set component value', function () { + return Harness.testCreate(SelectComponent, comp1).then((component) => { + assert.deepEqual(component.dataValue, ''); + component.setValue('red'); + assert.equal(component.dataValue, 'red'); }); - } - catch (error) { - return Promise.reject(error); - } - }); - - it('should set component value', function() { - return Harness.testCreate(SelectComponent, comp1).then((component) => { - assert.deepEqual(component.dataValue, ''); - component.setValue('red'); - assert.equal(component.dataValue, 'red'); }); - }); - it('should remove selected item', function() { - return Harness.testCreate(SelectComponent, comp1).then((component) => { - assert.deepEqual(component.dataValue, ''); - component.setValue('red'); - assert.equal(component.dataValue, 'red'); + it('should remove selected item', function () { + return Harness.testCreate(SelectComponent, comp1).then((component) => { + assert.deepEqual(component.dataValue, ''); + component.setValue('red'); + assert.equal(component.dataValue, 'red'); - const element = component.element.getElementsByClassName('choices__button')[0]; - component.choices._handleButtonAction(component.choices._store.activeItems, element); + const element = + component.element.getElementsByClassName('choices__button')[0]; + component.choices._handleButtonAction( + component.choices._store.activeItems, + element, + ); - assert.equal(component.dataValue, ''); + assert.equal(component.dataValue, ''); + }); }); - }); - it('should open dropdown after item has been removed', function() { - global.requestAnimationFrame = cb => cb(); - window.matchMedia = window.matchMedia || function() { - return { - matches : false, - addListener : function() {}, - removeListener: function() {} - }; - }; + it('should open dropdown after item has been removed', function () { + global.requestAnimationFrame = (cb) => cb(); + window.matchMedia = + window.matchMedia || + function () { + return { + matches: false, + addListener: function () {}, + removeListener: function () {}, + }; + }; + + return Harness.testCreate(SelectComponent, comp1).then((component) => { + component.setValue('red'); + + const element = + component.element.getElementsByClassName('choices__button')[0]; + component.choices._handleButtonAction( + component.choices._store.activeItems, + element, + ); + + component.choices.showDropdown(true); + + assert.equal(component.choices.dropdown.isActive, true); + }); + }); - return Harness.testCreate(SelectComponent, comp1).then((component) => { - component.setValue('red'); + it('should keep dropdown closed after item has been removed by keypress', function () { + return Harness.testCreate(SelectComponent, comp1).then((component) => { + component.setValue('red'); - const element = component.element.getElementsByClassName('choices__button')[0]; - component.choices._handleButtonAction(component.choices._store.activeItems, element); + const element = component.element.querySelector('.choices__button'); + const ke = new KeyboardEvent('keydown', { + bubbles: true, + cancelable: true, + keyCode: 13, + }); - component.choices.showDropdown(true); + element.dispatchEvent(ke); - assert.equal(component.choices.dropdown.isActive, true); + assert.equal(component.dataValue, ''); + assert.equal(component.choices.dropdown.isActive, false); + }); }); - }); - it('should keep dropdown closed after item has been removed by keypress', function() { - return Harness.testCreate(SelectComponent, comp1).then((component) => { - component.setValue('red'); + it('Should render and set values in selects with different widget types', function (done) { + const form = _.cloneDeep(comp7); + const element = document.createElement('div'); + + Formio.createForm(element, form) + .then((form) => { + const selectHTML = form.getComponent('selectHtml'); + const selectChoices = form.getComponent('selectChoices'); + assert.equal(!!selectHTML.choices, false); + assert.equal(!!selectChoices.choices, true); + + setTimeout(() => { + assert.equal( + selectChoices.element.querySelectorAll( + '.choices__item--choice', + ).length, + 3, + ); + const value = 'b'; + selectHTML.setValue(value); + selectChoices.setValue(value); + + setTimeout(() => { + assert.equal(selectHTML.dataValue, value); + assert.equal(selectChoices.dataValue, value); + assert.equal(selectHTML.getValue(), value); + assert.equal(selectChoices.getValue(), value); + + done(); + }, 200); + }, 200); + }) + .catch(done); + }); - const element = component.element.querySelector('.choices__button'); - const ke = new KeyboardEvent('keydown', { - bubbles: true, cancelable: true, keyCode: 13 - }); + it('Should clear select value when "clear value on refresh options" and "refresh options on" is enable and number component is changed ', function (done) { + const form = _.cloneDeep(comp8); + const element = document.createElement('div'); + + Formio.createForm(element, form) + .then((form) => { + const select = form.getComponent('select'); + const numberComp = form.getComponent('number'); + const value = 'b'; + select.setValue(value); + + setTimeout(() => { + assert.equal(select.dataValue, value); + assert.equal(select.getValue(), value); + const numberInput = numberComp.refs.input[0]; + const numberValue = 5; + const inputEvent = new Event('input'); + numberInput.value = numberValue; + numberInput.dispatchEvent(inputEvent); + + setTimeout(() => { + assert.equal(numberComp.dataValue, numberValue); + assert.equal(numberComp.getValue(), numberValue); + assert.equal(select.dataValue, ''); + assert.equal(select.getValue(), ''); + + done(); + }, 400); + }, 200); + }) + .catch(done); + }); - element.dispatchEvent(ke); + it('Should update select items when "refresh options on" is enable and number component is changed', function (done) { + const form = _.cloneDeep(comp9); + const element = document.createElement('div'); + const originalMakeRequest = Formio.makeRequest; + Formio.makeRequest = function (formio, type, url) { + return new Promise((resolve) => { + let values = [{ name: 'Ivan' }, { name: 'Mike' }]; + + if (url.endsWith('5')) { + values = [ + { name: 'Kate' }, + { name: 'Ann' }, + { name: 'Lana' }, + ]; + } + resolve(values); + }); + }; + + Formio.createForm(element, form) + .then((form) => { + const select = form.getComponent('select'); + const numberComp = form.getComponent('number'); + setTimeout(() => { + assert.equal(select.selectOptions.length, 2); + assert.deepEqual(select.selectOptions[0].value, { + name: 'Ivan', + }); + + const numberValue = 5; + const inputEvent = new Event('input'); + const numberInput = numberComp.refs.input[0]; + + numberInput.value = numberValue; + numberInput.dispatchEvent(inputEvent); + + setTimeout(() => { + assert.equal(numberComp.dataValue, numberValue); + assert.equal(numberComp.getValue(), numberValue); + assert.equal(select.selectOptions.length, 3); + assert.deepEqual(select.selectOptions[0].value, { + name: 'Kate', + }); + + Formio.makeRequest = originalMakeRequest; + done(); + }, 500); + }, 200); + }) + .catch(done); + }); - assert.equal(component.dataValue, ''); - assert.equal(component.choices.dropdown.isActive, false); + it('Should update select items when "refresh options on blur" is enable and number component is changed', function (done) { + const form = _.cloneDeep(comp9); + form.components[1].refreshOn = null; + form.components[1].refreshOnBlur = 'number'; + + const element = document.createElement('div'); + const originalMakeRequest = Formio.makeRequest; + Formio.makeRequest = function (formio, type, url) { + return new Promise((resolve) => { + let values = [{ name: 'Ivan' }, { name: 'Mike' }]; + + if (url.endsWith('5')) { + values = [ + { name: 'Kate' }, + { name: 'Ann' }, + { name: 'Lana' }, + ]; + } + resolve(values); + }); + }; + + Formio.createForm(element, form) + .then((form) => { + const select = form.getComponent('select'); + const numberComp = form.getComponent('number'); + setTimeout(() => { + assert.equal(select.selectOptions.length, 2); + assert.deepEqual(select.selectOptions[0].value, { + name: 'Ivan', + }); + + const numberValue = 5; + const inputEvent = new Event('input'); + const focusEvent = new Event('focus'); + const blurEvent = new Event('blur'); + const numberInput = numberComp.refs.input[0]; + numberInput.dispatchEvent(focusEvent); + numberInput.value = numberValue; + numberInput.dispatchEvent(inputEvent); + numberInput.dispatchEvent(blurEvent); + + setTimeout(() => { + assert.equal(numberComp.dataValue, numberValue); + assert.equal(numberComp.getValue(), numberValue); + assert.equal(select.selectOptions.length, 3); + assert.deepEqual(select.selectOptions[0].value, { + name: 'Kate', + }); + + Formio.makeRequest = originalMakeRequest; + done(); + }, 500); + }, 200); + }) + .catch(done); }); - }); - - it('Should render and set values in selects with different widget types', function(done) { - const form = _.cloneDeep(comp7); - const element = document.createElement('div'); - - Formio.createForm(element, form).then(form => { - const selectHTML = form.getComponent('selectHtml'); - const selectChoices = form.getComponent('selectChoices'); - assert.equal(!!selectHTML.choices, false); - assert.equal(!!selectChoices.choices, true); - - setTimeout(() => { - assert.equal(selectChoices.element.querySelectorAll('.choices__item--choice').length, 3); - const value = 'b'; - selectHTML.setValue(value); - selectChoices.setValue(value); - - setTimeout(() => { - assert.equal(selectHTML.dataValue, value); - assert.equal(selectChoices.dataValue, value); - assert.equal(selectHTML.getValue(), value); - assert.equal(selectChoices.getValue(), value); - - done(); - }, 200); - }, 200); - }).catch(done); - }); - - it('Should clear select value when "clear value on refresh options" and "refresh options on" is enable and number component is changed ', function(done) { - const form = _.cloneDeep(comp8); - const element = document.createElement('div'); - - Formio.createForm(element, form).then(form => { - const select = form.getComponent('select'); - const numberComp = form.getComponent('number'); - const value = 'b'; - select.setValue(value); - - setTimeout(() => { - assert.equal(select.dataValue, value); - assert.equal(select.getValue(), value); - const numberInput = numberComp.refs.input[0]; - const numberValue = 5; - const inputEvent = new Event('input'); - numberInput.value = numberValue; - numberInput.dispatchEvent(inputEvent); - - setTimeout(() => { - assert.equal(numberComp.dataValue, numberValue); - assert.equal(numberComp.getValue(), numberValue); - assert.equal(select.dataValue, ''); - assert.equal(select.getValue(), ''); - - done(); - }, 400); - }, 200); - }).catch(done); - }); - - it('Should update select items when "refresh options on" is enable and number component is changed', function(done) { - const form = _.cloneDeep(comp9); - const element = document.createElement('div'); - const originalMakeRequest = Formio.makeRequest; - Formio.makeRequest = function(formio, type, url) { - return new Promise(resolve => { - let values =[{ name: 'Ivan' }, { name: 'Mike' }]; - - if (url.endsWith('5')) { - values = [{ name: 'Kate' }, { name: 'Ann' }, { name: 'Lana' }]; - } - resolve(values); - }); - }; - - Formio.createForm(element, form).then(form => { - const select = form.getComponent('select'); - const numberComp = form.getComponent('number'); - setTimeout(() => { - assert.equal(select.selectOptions.length, 2); - assert.deepEqual(select.selectOptions[0].value, { name: 'Ivan' }); - - const numberValue = 5; - const inputEvent = new Event('input'); - const numberInput = numberComp.refs.input[0]; - - numberInput.value = numberValue; - numberInput.dispatchEvent(inputEvent); - - setTimeout(() => { - assert.equal(numberComp.dataValue, numberValue); - assert.equal(numberComp.getValue(), numberValue); - assert.equal(select.selectOptions.length, 3); - assert.deepEqual(select.selectOptions[0].value, { name: 'Kate' }); - - Formio.makeRequest = originalMakeRequest; - done(); - }, 500); - }, 200); - }).catch(done); - }); - - it('Should update select items when "refresh options on blur" is enable and number component is changed', function(done) { - const form = _.cloneDeep(comp9); - form.components[1].refreshOn = null; - form.components[1].refreshOnBlur = 'number'; - - const element = document.createElement('div'); - const originalMakeRequest = Formio.makeRequest; - Formio.makeRequest = function(formio, type, url) { - return new Promise(resolve => { - let values =[{ name: 'Ivan' }, { name: 'Mike' }]; - - if (url.endsWith('5')) { - values = [{ name: 'Kate' }, { name: 'Ann' }, { name: 'Lana' }]; - } - resolve(values); - }); - }; - - Formio.createForm(element, form).then(form => { - const select = form.getComponent('select'); - const numberComp = form.getComponent('number'); - setTimeout(() => { - assert.equal(select.selectOptions.length, 2); - assert.deepEqual(select.selectOptions[0].value, { name: 'Ivan' }); - - const numberValue = 5; - const inputEvent = new Event('input'); - const focusEvent = new Event('focus'); - const blurEvent = new Event('blur'); - const numberInput = numberComp.refs.input[0]; - numberInput.dispatchEvent(focusEvent); - numberInput.value = numberValue; - numberInput.dispatchEvent(inputEvent); - numberInput.dispatchEvent(blurEvent); - - setTimeout(() => { - assert.equal(numberComp.dataValue, numberValue); - assert.equal(numberComp.getValue(), numberValue); - assert.equal(select.selectOptions.length, 3); - assert.deepEqual(select.selectOptions[0].value, { name: 'Kate' }); - - Formio.makeRequest = originalMakeRequest; - done(); - }, 500); - }, 200); - }).catch(done); - }); - - it('Should be able to search if static search is enable', function(done) { - const form = _.cloneDeep(comp10); - const element = document.createElement('div'); - - Formio.createForm(element, form).then(form => { - const select = form.getComponent('select'); - - const searchField = select.element.querySelector('.choices__input.choices__input--cloned'); - const focusEvent = new Event('focus'); - searchField.dispatchEvent(focusEvent); - - setTimeout(() => { - assert.equal(select.choices.dropdown.isActive, true); - const items = select.choices.choiceList.element.children; - assert.equal(items.length, 5); - - const keyupEvent = new Event('keyup'); - const searchField = select.element.querySelector('.choices__input.choices__input--cloned'); - searchField.value = 'par'; - searchField.dispatchEvent(keyupEvent); - - setTimeout(() => { - const items = select.choices.choiceList.element.children; - assert.equal(items.length, 1); - - done(); - }, 400); - }, 200); - }).catch(done); - }); - - it('Should not be able to search if static search is disable', function(done) { - const form = _.cloneDeep(comp10); - form.components[0].searchEnabled = false; - const element = document.createElement('div'); - - Formio.createForm(element, form).then(form => { - const select = form.getComponent('select'); - const searchField = select.element.querySelector('.choices__input.choices__input--cloned'); - assert.equal(searchField, null); - - done(); - }).catch(done); - }); - - it('Should save correct value if value property and item template property are different', function(done) { - const form = _.cloneDeep(comp9); - form.components[1].refreshOn = null; - form.components[1].valueProperty = 'age'; - form.components[1].lazyLoad = true; - - const element = document.createElement('div'); - const originalMakeRequest = Formio.makeRequest; - - Formio.makeRequest = function() { - return new Promise(resolve => { - const values =[{ name: 'Ivan', age: 35 }, { name: 'Mike', age: 41 }]; - resolve(values); - }); - }; - - Formio.createForm(element, form).then(form => { - const select = form.getComponent('select'); - assert.equal(select.selectOptions.length, 0); - select.choices.showDropdown(); - - setTimeout(() => { - assert.equal(select.selectOptions.length, 2); - assert.deepEqual(select.selectOptions[0].value, 35); - assert.deepEqual(select.selectOptions[0].label, 'Ivan'); - - const items = select.choices.choiceList.element.children; - assert.equal(items.length, 2); - assert.equal(items[0].textContent.trim(), 'Ivan'); - - select.setValue(41); - - setTimeout(() => { - assert.equal(select.getValue(), 41); - assert.equal(select.choices.containerInner.element.children[1].children[0].children[0].textContent, 'Mike'); - - Formio.makeRequest = originalMakeRequest; - - done(); - }, 400); - }, 200); - }).catch(done); - }); - - it('Should set custom header when sending request in select url', function(done) { - const form = _.cloneDeep(comp9); - form.components[1].refreshOn = null; - form.components[1].lazyLoad = true; - form.components[1].data.headers = [{ key:'testHeader', value:'test' }]; - - const element = document.createElement('div'); - const originalMakeRequest = Formio.makeRequest; - - Formio.makeRequest = function(formio, type, url, method, data, opts) { - assert.equal(opts.header.get('testHeader'), 'test'); - return new Promise(resolve => { - const values = [{ name: 'Ivan', age: 35 }, { name: 'Mike', age: 41 }]; - resolve(values); - }); - }; - - Formio.createForm(element, form).then(form => { - const select = form.getComponent('select'); - assert.equal(select.selectOptions.length, 0); - select.choices.showDropdown(); - - setTimeout(() => { - Formio.makeRequest = originalMakeRequest; - done(); - }, 200); - }).catch(done); - }); - - it('Should set value in select url with lazy load option', function(done) { - const form = _.cloneDeep(comp9); - form.components[1].refreshOn = null; - form.components[1].lazyLoad = true; - - const element = document.createElement('div'); - const originalMakeRequest = Formio.makeRequest; - - Formio.makeRequest = function() { - return new Promise(resolve => { - const values = [{ name: 'Ivan' }, { name: 'Mike' }]; - resolve(values); - }); - }; - - Formio.createForm(element, form).then(form => { - const select = form.getComponent('select'); - select.setValue({ name: 'Ivan' }); - setTimeout(() => { - assert.deepEqual(select.getValue(), { name: 'Ivan' }); - assert.deepEqual(select.dataValue, { name: 'Ivan' }); - assert.equal(select.choices.containerInner.element.children[1].children[0].children[0].textContent, 'Ivan'); - - Formio.makeRequest = originalMakeRequest; - - done(); - }, 200); - }).catch(done); - }); - - it('Should set value in select url with lazy load option when value property is defined', function(done) { - const form = _.cloneDeep(comp9); - form.components[1].refreshOn = null; - form.components[1].lazyLoad = true; - form.components[1].valueProperty = 'name'; - const element = document.createElement('div'); - const originalMakeRequest = Formio.makeRequest; - - Formio.makeRequest = function() { - return new Promise(resolve => { - const values = [{ name: 'Ivan' }, { name: 'Mike' }]; - resolve(values); - }); - }; - - Formio.createForm(element, form).then(form => { - const select = form.getComponent('select'); - select.setValue('Ivan'); - setTimeout(() => { - assert.equal(select.getValue(), 'Ivan'); - assert.equal(select.dataValue, 'Ivan'); - assert.equal(select.choices.containerInner.element.children[1].children[0].children[0].textContent, 'Ivan'); - - Formio.makeRequest = originalMakeRequest; - - done(); - }, 200); - }).catch(done); - }); - - it('Should be able to search if static search is enabled', function(done) { - const form = _.cloneDeep(comp10); - const element = document.createElement('div'); - - Formio.createForm(element, form).then(form => { - const select = form.getComponent('select'); - - const searchField = select.element.querySelector('.choices__input.choices__input--cloned'); - const focusEvent = new Event('focus'); - searchField.dispatchEvent(focusEvent); - - setTimeout(() => { - assert.equal(select.choices.dropdown.isActive, true); - const items = select.choices.choiceList.element.children; - assert.equal(items.length, 5); - - const keyupEvent = new Event('keyup'); - const searchField = select.element.querySelector('.choices__input.choices__input--cloned'); - searchField.value = 'par'; - searchField.dispatchEvent(keyupEvent); - - setTimeout(() => { - const items = select.choices.choiceList.element.children; - assert.equal(items.length, 1); - - done(); - }, 400); - }, 200); - }).catch(done); - }); - - it('Server side search is debounced with the correct timeout', function(done) { - const form = _.cloneDeep(comp9); - form.components[1].lazyLoad = false; - form.components[1].searchDebounce = 0.7; - form.components[1].disableLimit = false; - form.components[1].searchField = 'name'; - const element = document.createElement('div'); - - const originalMakeRequest = Formio.makeRequest; - Formio.makeRequest = function() { - return new Promise(resolve => { - resolve([]); - }); - }; - - var searchHasBeenDebounced = false; - var originalDebounce = _.debounce; - _.debounce = (fn, timeout, opts) => { - searchHasBeenDebounced = timeout === 700; - return originalDebounce(fn, 0, opts); - }; - - Formio.createForm(element, form).then(form => { - const select = form.getComponent('select'); - const searchField = select.element.querySelector('.choices__input.choices__input--cloned'); - const focusEvent = new Event('focus'); - searchField.dispatchEvent(focusEvent); - - setTimeout(() => { - const keyupEvent = new Event('keyup'); - searchField.value = 'the_name'; - searchField.dispatchEvent(keyupEvent); - - setTimeout(() => { - _.debounce = originalDebounce; - Formio.makeRequest = originalMakeRequest; - - assert.equal(searchHasBeenDebounced, true); - done(); - }, 50); - }, 200); - }).catch(done); - }); - - it('Should provide "Allow only available values" validation', function(done) { - const form = _.cloneDeep(comp10); - form.components[0].validate.onlyAvailableItems = true; - const element = document.createElement('div'); - - Formio.createForm(element, form).then(form => { - const select = form.getComponent('select'); - const value = 'Dallas'; - select.setValue(value); - - setTimeout(() => { - assert.equal(select.getValue(), value); - assert.equal(select.dataValue, value); - const submit = form.getComponent('submit'); - const clickEvent = new Event('click'); - const submitBtn = submit.refs.button; - submitBtn.dispatchEvent(clickEvent); - - setTimeout(() => { - assert.equal(form.errors.length, 1); - assert.equal(select.error.message, 'Select is an invalid value.'); - document.innerHTML = ''; - done(); - }, 400); - }, 200); - }).catch(done); - }); - - it('Should render and set value in select json', function(done) { - const formObj = _.cloneDeep(comp11); - const element = document.createElement('div'); - - Formio.createForm(element, formObj).then(form => { - const select = form.getComponent('select'); - assert.equal(select.choices.containerInner.element.children[1].children[0].dataset.value, ''); - select.choices.showDropdown(); - - setTimeout(() => { - const items = select.choices.choiceList.element.children; - assert.equal(items.length, 4); - - const value = { value: 'a', label:'A' }; - select.setValue(value); - - setTimeout(() => { - assert.deepEqual(select.getValue(), value); - assert.deepEqual(select.dataValue, value); - assert.equal(select.choices.containerInner.element.children[1].children[0].children[0].textContent, 'A'); - - done(); - }, 400); - }, 200); - }).catch(done); - }); - - it('Should load and set items in select resource and set value', function(done) { - const form = _.cloneDeep(comp12); - const element = document.createElement('div'); - const originalMakeRequest = Formio.makeRequest; - - Formio.makeRequest = function(formio, type, url) { - return new Promise(resolve => { - let values = [{ data: { name: 'Ivan' } }, { data: { name: 'Mike' } }]; - - if (url.endsWith('Ivan')) { - assert.equal(url.endsWith('/form/60114dd32cab36ad94ac4f94/submission?limit=100&skip=0&data.name__regex=Ivan'), true); - values = [{ data: { name: 'Ivan' } }]; - } - else { - assert.equal(url.endsWith('/form/60114dd32cab36ad94ac4f94/submission?limit=100&skip=0'), true); - } - resolve(values); - }); - }; - - Formio.createForm(element, form).then(form => { - const select = form.getComponent('select'); - const items = select.choices.choiceList.element.children; - assert.equal(items.length, 1); - select.setValue('Ivan'); - - setTimeout(() => { - assert.equal(select.getValue(), 'Ivan'); - assert.equal(select.dataValue, 'Ivan'); - assert.equal(select.choices.containerInner.element.children[1].children[0].children[0].textContent, 'Ivan'); - select.choices.showDropdown(); - - setTimeout(() => { - const items = select.choices.choiceList.element.children; - - assert.equal(items.length, 2); - assert.equal(items[0].textContent, 'Ivan'); - - Formio.makeRequest = originalMakeRequest; - done(); - }, 400); - }, 200); - }).catch(done); - }); - - it('Should not have "limit" and "skip" query params when "Disable limit" option checked', function(done) { - const form = _.cloneDeep(comp9); - const element = document.createElement('div'); - const originalMakeRequest = Formio.makeRequest; - Formio.makeRequest = (_, __, url) => { - assert.equal(url, 'https://test.com/'); - return Promise.resolve({}); - }; - - Formio.createForm(element, form).then(() => { - setTimeout(() => { - Formio.makeRequest = originalMakeRequest; - done(); - }, 200); - }).catch(done); - }); - - it('The empty option in html5 shouldn\'t have the [Object Object] value', function() { - return Harness.testCreate(SelectComponent, comp13).then((component) => { - const emptyOption = component.element.querySelectorAll('option')[0]; - assert.notEqual(emptyOption.value, '[object Object]'); - assert.equal(emptyOption.value, ''); + it('Should be able to search if static search is enable', function (done) { + const form = _.cloneDeep(comp10); + const element = document.createElement('div'); + + Formio.createForm(element, form) + .then((form) => { + const select = form.getComponent('select'); + + const searchField = select.element.querySelector( + '.choices__input.choices__input--cloned', + ); + const focusEvent = new Event('focus'); + searchField.dispatchEvent(focusEvent); + + setTimeout(() => { + assert.equal(select.choices.dropdown.isActive, true); + const items = select.choices.choiceList.element.children; + assert.equal(items.length, 5); + + const keyupEvent = new Event('keyup'); + const searchField = select.element.querySelector( + '.choices__input.choices__input--cloned', + ); + searchField.value = 'par'; + searchField.dispatchEvent(keyupEvent); + + setTimeout(() => { + const items = + select.choices.choiceList.element.children; + assert.equal(items.length, 1); + + done(); + }, 400); + }, 200); + }) + .catch(done); }); - }); - - it('Should not have default values in schema', function(done) { - const form = _.cloneDeep(comp14); - const element = document.createElement('div'); - - const requiredSchema = { - label: 'Select', - tableView: true, - key: 'select', - type: 'select', - input: true - }; - - Formio.createForm(element, form).then(form => { - const select = form.getComponent('select'); - assert.deepEqual(requiredSchema, select.schema); - done(); - }).catch(done); - }); - - it('Should show async custom values and be able to set submission', function(done) { - const formObj = _.cloneDeep(comp16); - const element = document.createElement('div'); - - Formio.createForm(element, formObj).then(form => { - const select = form.getComponent('select'); - select.choices.showDropdown(); - - setTimeout(() => { - const items = select.choices.choiceList.element.children; - assert.equal(items.length, 3); - const value = 'bb'; - form.submission = { data: { select: value } }; - - setTimeout(() => { - assert.deepEqual(select.getValue(), value); - assert.deepEqual(select.dataValue, value); - assert.equal(select.choices.containerInner.element.children[1].children[0].children[0].textContent, 'B'); - - done(); - }, 400); - }, 200); - }).catch(done); - }); - - it('Should provide metadata.selectData for Select component pointed to a resource where value property is set to a field', function(done) { - const form = _.cloneDeep(comp17); - const testItems = [ - { textField: 'John' }, - { textField: 'Mary' }, - { textField: 'Sally' } - ]; - const element = document.createElement('div'); - - Formio.createForm(element, form).then(form => { - const select = form.getComponent('select'); - select.setItems(testItems.map(item => ({ data: item }))); - const value = 'John'; - select.setValue(value); - - setTimeout(() => { - assert.equal(select.dataValue, value); - const submit = form.getComponent('submit'); - const clickEvent = new Event('click'); - const submitBtn = submit.refs.button; - submitBtn.dispatchEvent(clickEvent); - - setTimeout(() => { - assert.equal(_.isEqual(form.submission.metadata.selectData.select.data, testItems[0]), true); - done(); - }, 200); - }, 200); - }).catch(done); - }); - - it('Should provide correct metadata.selectData for multiple Select', function(done) { - const form = _.cloneDeep(comp20); - const element = document.createElement('div'); - - Formio.createForm(element, form).then(form => { - const select = form.getComponent('select'); - const values = ['apple', 'orange']; - select.setValue(values); - - setTimeout(()=> { - const submit = form.getComponent('submit'); - const clickEvent = new Event('click'); - const submitBtn = submit.refs.button; - submitBtn.dispatchEvent(clickEvent); - - setTimeout(() => { - const metadata = form.submission.metadata.selectData.select; - assert.equal(_.keys(metadata).length, 2); - values.forEach((value) => { - assert.equal(_.find(select.component.data.values, { value }).label, metadata[value].label); - }); - done(); - }, 200); - }, 200); - }).catch(done); - }); - - it('Should provide correct metadata.selectData for HTML5 Select', function(done) { - const element = document.createElement('div'); - - Formio.createForm(element, comp21).then(form => { - const select = form.getComponent('animals'); - const checkbox = form.getComponent('checkbox'); - const value = 'dog'; - select.setValue(value); - - setTimeout(()=> { - checkbox.setValue(true); - setTimeout(() => { - const submit = form.getComponent('submit'); - const clickEvent = new Event('click'); - const submitBtn = submit.refs.button; - submitBtn.dispatchEvent(clickEvent); - - setTimeout(() => { - const metadata = form.submission.metadata.selectData.animals2; - assert.equal(metadata.label, 'Dog'); - done(); - }, 200); - }, 300); - }, 200); - }).catch(done); - }); - - it('OnBlur validation should work properly with Select component', function(done) { - this.timeout(0); - const element = document.createElement('div'); - - Formio.createForm(element, comp19).then(form => { - const select = form.components[0]; - select.setValue('banana'); - select.focusableElement.focus(); - select.pristine = false; - - setTimeout(() => { - assert(!select.error, 'Select should be valid while changing'); - select.focusableElement.dispatchEvent(new Event('blur')); - - setTimeout(() => { - assert(select.error, 'Should set error after Select component was blurred'); - done(); - }, 500); - }, 200); - }).catch(done); - }); - - it('Should escape special characters in regex search field', function(done) { - const form = _.cloneDeep(comp17); - const element = document.createElement('div'); - - Formio.createForm(element, form).then(form => { - const select = form.getComponent('select'); - const searchField = select.element.querySelector('.choices__input.choices__input--cloned'); - const focusEvent = new Event('focus'); - searchField.dispatchEvent(focusEvent); - - setTimeout(() => { - const keyupEvent = new Event('keyup'); - searchField.value = '^$.*+?()[]{}|'; - searchField.dispatchEvent(keyupEvent); - - const spy = sinon.spy(Formio, 'makeRequest'); - - setTimeout(() => { - assert.equal(spy.callCount, 1); - - const urlArg = spy.args[0][2]; - - assert.ok(urlArg && typeof urlArg === 'string' && urlArg.startsWith('http'), 'A URL should be passed as the third argument to "Formio.makeRequest()"'); - - assert.ok(urlArg.includes('__regex=%5C%5E%5C%24%5C.%5C*%5C%2B%5C%3F%5C(%5C)%5C%5B%5C%5D%5C%7B%5C%7D%5C%7C'), 'The URL should contain escaped and encoded search value regex'); - done(); - }, 500); - }, 200); - }).catch(done); - }); - - // it('should reset input value when called with empty value', () => { - // const comp = Object.assign({}, comp1); - // delete comp.placeholder; - // - // return Harness.testCreate(SelectComponent, comp).then((component) => { - // assert.deepEqual(component.dataValue, ''); - // assert.equal(component.refs.input[0].value, ''); - // component.setValue('red'); - // assert.equal(component.dataValue, 'red'); - // assert.equal(component.refs.input[0].value, 'red'); - // component.setValue(''); - // assert.equal(component.dataValue, ''); - // assert.equal(component.refs.input[0].value, ''); - // }); - // }); + + it('Should not be able to search if static search is disable', function (done) { + const form = _.cloneDeep(comp10); + form.components[0].searchEnabled = false; + const element = document.createElement('div'); + + Formio.createForm(element, form) + .then((form) => { + const select = form.getComponent('select'); + const searchField = select.element.querySelector( + '.choices__input.choices__input--cloned', + ); + assert.equal(searchField, null); + + done(); + }) + .catch(done); + }); + + it('Should save correct value if value property and item template property are different', function (done) { + const form = _.cloneDeep(comp9); + form.components[1].refreshOn = null; + form.components[1].valueProperty = 'age'; + form.components[1].lazyLoad = true; + + const element = document.createElement('div'); + const originalMakeRequest = Formio.makeRequest; + + Formio.makeRequest = function () { + return new Promise((resolve) => { + const values = [ + { name: 'Ivan', age: 35 }, + { name: 'Mike', age: 41 }, + ]; + resolve(values); + }); + }; + + Formio.createForm(element, form) + .then((form) => { + const select = form.getComponent('select'); + assert.equal(select.selectOptions.length, 0); + select.choices.showDropdown(); + + setTimeout(() => { + assert.equal(select.selectOptions.length, 2); + assert.deepEqual(select.selectOptions[0].value, 35); + assert.deepEqual( + select.selectOptions[0].label, + 'Ivan', + ); + + const items = select.choices.choiceList.element.children; + assert.equal(items.length, 2); + assert.equal(items[0].textContent.trim(), 'Ivan'); + + select.setValue(41); + + setTimeout(() => { + assert.equal(select.getValue(), 41); + assert.equal( + select.choices.containerInner.element.children[1] + .children[0].children[0].textContent, + 'Mike', + ); + + Formio.makeRequest = originalMakeRequest; + + done(); + }, 400); + }, 200); + }) + .catch(done); + }); + + it('Should set custom header when sending request in select url', function (done) { + const form = _.cloneDeep(comp9); + form.components[1].refreshOn = null; + form.components[1].lazyLoad = true; + form.components[1].data.headers = [ + { key: 'testHeader', value: 'test' }, + ]; + + const element = document.createElement('div'); + const originalMakeRequest = Formio.makeRequest; + + Formio.makeRequest = function (formio, type, url, method, data, opts) { + assert.equal(opts.header.get('testHeader'), 'test'); + return new Promise((resolve) => { + const values = [ + { name: 'Ivan', age: 35 }, + { name: 'Mike', age: 41 }, + ]; + resolve(values); + }); + }; + + Formio.createForm(element, form) + .then((form) => { + const select = form.getComponent('select'); + assert.equal(select.selectOptions.length, 0); + select.choices.showDropdown(); + + setTimeout(() => { + Formio.makeRequest = originalMakeRequest; + done(); + }, 200); + }) + .catch(done); + }); + + it('Should set value in select url with lazy load option', function (done) { + const form = _.cloneDeep(comp9); + form.components[1].refreshOn = null; + form.components[1].lazyLoad = true; + + const element = document.createElement('div'); + const originalMakeRequest = Formio.makeRequest; + + Formio.makeRequest = function () { + return new Promise((resolve) => { + const values = [{ name: 'Ivan' }, { name: 'Mike' }]; + resolve(values); + }); + }; + + Formio.createForm(element, form) + .then((form) => { + const select = form.getComponent('select'); + select.setValue({ name: 'Ivan' }); + setTimeout(() => { + assert.deepEqual(select.getValue(), { name: 'Ivan' }); + assert.deepEqual(select.dataValue, { name: 'Ivan' }); + assert.equal( + select.choices.containerInner.element.children[1] + .children[0].children[0].textContent, + 'Ivan', + ); + + Formio.makeRequest = originalMakeRequest; + + done(); + }, 200); + }) + .catch(done); + }); + + it('Should set value in select url with lazy load option when value property is defined', function (done) { + const form = _.cloneDeep(comp9); + form.components[1].refreshOn = null; + form.components[1].lazyLoad = true; + form.components[1].valueProperty = 'name'; + const element = document.createElement('div'); + const originalMakeRequest = Formio.makeRequest; + + Formio.makeRequest = function () { + return new Promise((resolve) => { + const values = [{ name: 'Ivan' }, { name: 'Mike' }]; + resolve(values); + }); + }; + + Formio.createForm(element, form) + .then((form) => { + const select = form.getComponent('select'); + select.setValue('Ivan'); + setTimeout(() => { + assert.equal(select.getValue(), 'Ivan'); + assert.equal(select.dataValue, 'Ivan'); + assert.equal( + select.choices.containerInner.element.children[1] + .children[0].children[0].textContent, + 'Ivan', + ); + + Formio.makeRequest = originalMakeRequest; + + done(); + }, 200); + }) + .catch(done); + }); + + it('Should be able to search if static search is enabled', function (done) { + const form = _.cloneDeep(comp10); + const element = document.createElement('div'); + + Formio.createForm(element, form) + .then((form) => { + const select = form.getComponent('select'); + + const searchField = select.element.querySelector( + '.choices__input.choices__input--cloned', + ); + const focusEvent = new Event('focus'); + searchField.dispatchEvent(focusEvent); + + setTimeout(() => { + assert.equal(select.choices.dropdown.isActive, true); + const items = select.choices.choiceList.element.children; + assert.equal(items.length, 5); + + const keyupEvent = new Event('keyup'); + const searchField = select.element.querySelector( + '.choices__input.choices__input--cloned', + ); + searchField.value = 'par'; + searchField.dispatchEvent(keyupEvent); + + setTimeout(() => { + const items = + select.choices.choiceList.element.children; + assert.equal(items.length, 1); + + done(); + }, 400); + }, 200); + }) + .catch(done); + }); + + it('Server side search is debounced with the correct timeout', function (done) { + const form = _.cloneDeep(comp9); + form.components[1].lazyLoad = false; + form.components[1].searchDebounce = 0.7; + form.components[1].disableLimit = false; + form.components[1].searchField = 'name'; + const element = document.createElement('div'); + + const originalMakeRequest = Formio.makeRequest; + Formio.makeRequest = function () { + return new Promise((resolve) => { + resolve([]); + }); + }; + + var searchHasBeenDebounced = false; + var originalDebounce = _.debounce; + _.debounce = (fn, timeout, opts) => { + searchHasBeenDebounced = timeout === 700; + return originalDebounce(fn, 0, opts); + }; + + Formio.createForm(element, form) + .then((form) => { + const select = form.getComponent('select'); + const searchField = select.element.querySelector( + '.choices__input.choices__input--cloned', + ); + const focusEvent = new Event('focus'); + searchField.dispatchEvent(focusEvent); + + setTimeout(() => { + const keyupEvent = new Event('keyup'); + searchField.value = 'the_name'; + searchField.dispatchEvent(keyupEvent); + + setTimeout(() => { + _.debounce = originalDebounce; + Formio.makeRequest = originalMakeRequest; + + assert.equal(searchHasBeenDebounced, true); + done(); + }, 50); + }, 200); + }) + .catch(done); + }); + + it('Should provide "Allow only available values" validation', function (done) { + const form = _.cloneDeep(comp10); + form.components[0].validate.onlyAvailableItems = true; + const element = document.createElement('div'); + + Formio.createForm(element, form) + .then((form) => { + const select = form.getComponent('select'); + const value = 'Dallas'; + select.setValue(value); + + setTimeout(() => { + assert.equal(select.getValue(), value); + assert.equal(select.dataValue, value); + const submit = form.getComponent('submit'); + const clickEvent = new Event('click'); + const submitBtn = submit.refs.button; + submitBtn.dispatchEvent(clickEvent); + + setTimeout(() => { + assert.equal(form.errors.length, 1); + assert.equal( + select.error.message, + 'Select is an invalid value.', + ); + document.innerHTML = ''; + done(); + }, 400); + }, 200); + }) + .catch(done); + }); + + it('Should render and set value in select json', function (done) { + const formObj = _.cloneDeep(comp11); + const element = document.createElement('div'); + + Formio.createForm(element, formObj) + .then((form) => { + const select = form.getComponent('select'); + assert.equal( + select.choices.containerInner.element.children[1] + .children[0].dataset.value, + '', + ); + select.choices.showDropdown(); + + setTimeout(() => { + const items = select.choices.choiceList.element.children; + assert.equal(items.length, 4); + + const value = { value: 'a', label: 'A' }; + select.setValue(value); + + setTimeout(() => { + assert.deepEqual(select.getValue(), value); + assert.deepEqual(select.dataValue, value); + assert.equal( + select.choices.containerInner.element.children[1] + .children[0].children[0].textContent, + 'A', + ); + + done(); + }, 400); + }, 200); + }) + .catch(done); + }); + + it('Should load and set items in select resource and set value', function (done) { + const form = _.cloneDeep(comp12); + const element = document.createElement('div'); + const originalMakeRequest = Formio.makeRequest; + + Formio.makeRequest = function (formio, type, url) { + return new Promise((resolve) => { + let values = [ + { data: { name: 'Ivan' } }, + { data: { name: 'Mike' } }, + ]; + + if (url.endsWith('Ivan')) { + assert.equal( + url.endsWith( + '/form/60114dd32cab36ad94ac4f94/submission?limit=100&skip=0&data.name__regex=Ivan', + ), + true, + ); + values = [{ data: { name: 'Ivan' } }]; + } else { + assert.equal( + url.endsWith( + '/form/60114dd32cab36ad94ac4f94/submission?limit=100&skip=0', + ), + true, + ); + } + + resolve(values); + }); + }; + + Formio.createForm(element, form) + .then((form) => { + const select = form.getComponent('select'); + const items = select.choices.choiceList.element.children; + assert.equal(items.length, 1); + select.setValue('Ivan'); + + setTimeout(() => { + assert.equal(select.getValue(), 'Ivan'); + assert.equal(select.dataValue, 'Ivan'); + assert.equal( + select.choices.containerInner.element.children[1] + .children[0].children[0].textContent, + 'Ivan', + ); + select.choices.showDropdown(); + + setTimeout(() => { + const items = + select.choices.choiceList.element.children; + + assert.equal(items.length, 2); + assert.equal(items[0].textContent, 'Ivan'); + + Formio.makeRequest = originalMakeRequest; + done(); + }, 400); + }, 200); + }) + .catch(done); + }); + + it('Should not have "limit" and "skip" query params when "Disable limit" option checked', function (done) { + const form = _.cloneDeep(comp9); + const element = document.createElement('div'); + const originalMakeRequest = Formio.makeRequest; + Formio.makeRequest = (_, __, url) => { + assert.equal(url, 'https://test.com/'); + return Promise.resolve({}); + }; + + Formio.createForm(element, form) + .then(() => { + setTimeout(() => { + Formio.makeRequest = originalMakeRequest; + done(); + }, 200); + }) + .catch(done); + }); + + it("The empty option in html5 shouldn't have the [Object Object] value", function () { + return Harness.testCreate(SelectComponent, comp13).then((component) => { + const emptyOption = component.element.querySelectorAll('option')[0]; + assert.notEqual(emptyOption.value, '[object Object]'); + assert.equal(emptyOption.value, ''); + }); + }); + + it('Should not have default values in schema', function (done) { + const form = _.cloneDeep(comp14); + const element = document.createElement('div'); + + const requiredSchema = { + label: 'Select', + tableView: true, + key: 'select', + type: 'select', + input: true, + }; + + Formio.createForm(element, form) + .then((form) => { + const select = form.getComponent('select'); + assert.deepEqual(requiredSchema, select.schema); + done(); + }) + .catch(done); + }); + + it('Should show async custom values and be able to set submission', function (done) { + const formObj = _.cloneDeep(comp16); + const element = document.createElement('div'); + + Formio.createForm(element, formObj) + .then((form) => { + const select = form.getComponent('select'); + select.choices.showDropdown(); + + setTimeout(() => { + const items = select.choices.choiceList.element.children; + assert.equal(items.length, 3); + const value = 'bb'; + form.submission = { data: { select: value } }; + + setTimeout(() => { + assert.deepEqual(select.getValue(), value); + assert.deepEqual(select.dataValue, value); + assert.equal( + select.choices.containerInner.element.children[1] + .children[0].children[0].textContent, + 'B', + ); + + done(); + }, 400); + }, 200); + }) + .catch(done); + }); + + it('Should provide metadata.selectData for Select component pointed to a resource where value property is set to a field', function (done) { + const form = _.cloneDeep(comp17); + const testItems = [ + { textField: 'John' }, + { textField: 'Mary' }, + { textField: 'Sally' }, + ]; + const element = document.createElement('div'); + + Formio.createForm(element, form) + .then((form) => { + const select = form.getComponent('select'); + select.setItems(testItems.map((item) => ({ data: item }))); + const value = 'John'; + select.setValue(value); + + setTimeout(() => { + assert.equal(select.dataValue, value); + const submit = form.getComponent('submit'); + const clickEvent = new Event('click'); + const submitBtn = submit.refs.button; + submitBtn.dispatchEvent(clickEvent); + + setTimeout(() => { + assert.equal( + _.isEqual( + form.submission.metadata.selectData.select.data, + testItems[0], + ), + true, + ); + done(); + }, 200); + }, 200); + }) + .catch(done); + }); + + it('Should provide correct metadata.selectData for multiple Select', function (done) { + const form = _.cloneDeep(comp20); + const element = document.createElement('div'); + + Formio.createForm(element, form) + .then((form) => { + const select = form.getComponent('select'); + const values = ['apple', 'orange']; + select.setValue(values); + + setTimeout(() => { + const submit = form.getComponent('submit'); + const clickEvent = new Event('click'); + const submitBtn = submit.refs.button; + submitBtn.dispatchEvent(clickEvent); + + setTimeout(() => { + const metadata = + form.submission.metadata.selectData.select; + assert.equal(_.keys(metadata).length, 2); + values.forEach((value) => { + assert.equal( + _.find(select.component.data.values, { value }) + .label, + metadata[value].label, + ); + }); + done(); + }, 200); + }, 200); + }) + .catch(done); + }); + + it('Should provide correct metadata.selectData for HTML5 Select', function (done) { + const element = document.createElement('div'); + + Formio.createForm(element, comp21) + .then((form) => { + const select = form.getComponent('animals'); + const checkbox = form.getComponent('checkbox'); + const value = 'dog'; + select.setValue(value); + + setTimeout(() => { + checkbox.setValue(true); + setTimeout(() => { + const submit = form.getComponent('submit'); + const clickEvent = new Event('click'); + const submitBtn = submit.refs.button; + submitBtn.dispatchEvent(clickEvent); + + setTimeout(() => { + const metadata = + form.submission.metadata.selectData.animals2; + assert.equal(metadata.label, 'Dog'); + done(); + }, 200); + }, 300); + }, 200); + }) + .catch(done); + }); + + it('OnBlur validation should work properly with Select component', function (done) { + this.timeout(0); + const element = document.createElement('div'); + + Formio.createForm(element, comp19) + .then((form) => { + const select = form.components[0]; + select.setValue('banana'); + select.focusableElement.focus(); + select.pristine = false; + + setTimeout(() => { + assert( + !select.error, + 'Select should be valid while changing', + ); + select.focusableElement.dispatchEvent(new Event('blur')); + + setTimeout(() => { + assert( + select.error, + 'Should set error after Select component was blurred', + ); + done(); + }, 500); + }, 200); + }) + .catch(done); + }); + + it('Should escape special characters in regex search field', function (done) { + const form = _.cloneDeep(comp17); + const element = document.createElement('div'); + + Formio.createForm(element, form) + .then((form) => { + const select = form.getComponent('select'); + const searchField = select.element.querySelector( + '.choices__input.choices__input--cloned', + ); + const focusEvent = new Event('focus'); + searchField.dispatchEvent(focusEvent); + + setTimeout(() => { + const keyupEvent = new Event('keyup'); + searchField.value = '^$.*+?()[]{}|'; + searchField.dispatchEvent(keyupEvent); + + const spy = sinon.spy(Formio, 'makeRequest'); + + setTimeout(() => { + assert.equal(spy.callCount, 1); + + const urlArg = spy.args[0][2]; + + assert.ok( + urlArg && + typeof urlArg === 'string' && + urlArg.startsWith('http'), + 'A URL should be passed as the third argument to "Formio.makeRequest()"', + ); + + assert.ok( + urlArg.includes( + '__regex=%5C%5E%5C%24%5C.%5C*%5C%2B%5C%3F%5C(%5C)%5C%5B%5C%5D%5C%7B%5C%7D%5C%7C', + ), + 'The URL should contain escaped and encoded search value regex', + ); + done(); + }, 500); + }, 200); + }) + .catch(done); + }); + + // it('should reset input value when called with empty value', () => { + // const comp = Object.assign({}, comp1); + // delete comp.placeholder; + // + // return Harness.testCreate(SelectComponent, comp).then((component) => { + // assert.deepEqual(component.dataValue, ''); + // assert.equal(component.refs.input[0].value, ''); + // component.setValue('red'); + // assert.equal(component.dataValue, 'red'); + // assert.equal(component.refs.input[0].value, 'red'); + // component.setValue(''); + // assert.equal(component.dataValue, ''); + // assert.equal(component.refs.input[0].value, ''); + // }); + // }); }); -describe('Select Component Array Values', function() { - it('Select Component should work correctly with the values in the form of an array', function(done) { - const form = _.cloneDeep(comp18); - const testItems = [ - { textField: ['one','two'] }, - { textField: ['three','four'] }, - { textField: ['five','six'] }, - ]; - const element = document.createElement('div'); - - Formio.createForm(element, form).then(form => { - const select = form.getComponent('select'); - select.setItems(testItems.map(item => ({ data: item }))); - const value = ['three','four']; - select.setValue(value); - assert.equal(select.selectOptions.length, 3); - setTimeout(() => { - assert.deepEqual(select.getValue(), value); - assert.deepEqual(select.dataValue, value); - const submit = form.getComponent('submit'); - const clickEvent = new Event('click'); - const submitBtn = submit.refs.button; - submitBtn.dispatchEvent(clickEvent); - - setTimeout(() => { - assert.equal(select.dataValue, value); - done(); - }, 200); - }, 200); - }).catch(done); - }); +describe('Select Component Array Values', function () { + it('Select Component should work correctly with the values in the form of an array', function (done) { + const form = _.cloneDeep(comp18); + const testItems = [ + { textField: ['one', 'two'] }, + { textField: ['three', 'four'] }, + { textField: ['five', 'six'] }, + ]; + const element = document.createElement('div'); + + Formio.createForm(element, form) + .then((form) => { + const select = form.getComponent('select'); + select.setItems(testItems.map((item) => ({ data: item }))); + const value = ['three', 'four']; + select.setValue(value); + assert.equal(select.selectOptions.length, 3); + setTimeout(() => { + assert.deepEqual(select.getValue(), value); + assert.deepEqual(select.dataValue, value); + const submit = form.getComponent('submit'); + const clickEvent = new Event('click'); + const submitBtn = submit.refs.button; + submitBtn.dispatchEvent(clickEvent); + + setTimeout(() => { + assert.equal(select.dataValue, value); + done(); + }, 200); + }, 200); + }) + .catch(done); + }); }); -describe('Select Component with Entire Object Value Property', function() { - it('Should provide correct value', function(done) { - const form = _.cloneDeep(comp15); - const element = document.createElement('div'); - - Formio.createForm(element, form).then(form => { - const select = form.getComponent('select'); - const value = { 'textField':'rgd','submit':true,'number':11 }; - select.setValue(value); - - setTimeout(() => { - assert.equal(select.getValue(), value); - assert.equal(select.dataValue, value); - const submit = form.getComponent('submit'); - const clickEvent = new Event('click'); - const submitBtn = submit.refs.button; - submitBtn.dispatchEvent(clickEvent); - - setTimeout(() => { - assert.equal(select.dataValue, value); - done(); - }, 200); - }, 200); - }).catch(done); - }); - - it('Should provide correct items for Resource DataSrc Type and Entire Object Value Property', function(done) { - const form = _.cloneDeep(comp15); - const testItems = [ - { textField: 'Jone', number: 1 }, - { textField: 'Mary', number: 2 }, - { textField: 'Sally', number: 3 } - ]; - const element = document.createElement('div'); - - Formio.createForm(element, form).then(form => { - const select = form.getComponent('select'); - select.setItems(testItems.map(item => ({ data: item }))); - const value = { textField: 'Jone', number: 1 }; - select.setValue(value); - assert.equal(select.selectOptions.length, 3); - - setTimeout(() => { - assert.equal(select.dataValue, value); - const submit = form.getComponent('submit'); - const clickEvent = new Event('click'); - const submitBtn = submit.refs.button; - submitBtn.dispatchEvent(clickEvent); - - setTimeout(() => { - assert.equal(typeof select.dataValue, 'object'); - done(); - }, 200); - }, 200); - }).catch(done); - }); - - it('Should provide correct html value for Resource DataSrc Type and Entire Object Value Property', function(done) { - const form = _.cloneDeep(comp15); - const testItems = [ - { textField: 'Jone', number: 1 }, - { textField: 'Mary', number: 2 }, - { textField: 'Sally', number: 3 } - ]; - const element = document.createElement('div'); - - Formio.createForm(element, form).then(form => { - const select = form.getComponent('select'); - select.setItems(testItems.map(item => ({ data: item }))); - const selectContainer = element.querySelector('[ref="selectContainer"]'); - assert.notEqual(selectContainer, null); - const options = selectContainer.childNodes; - assert.equal(options.length, 4); - options.forEach((option) => { - assert.notEqual(option.value, '[object Object]'); - }); - const value = { textField: 'Jone', number: 1 }; - select.setValue(value); - assert.equal(select.selectOptions.length, 3); - - setTimeout(() => { - assert.equal(select.dataValue, value); - const submit = form.getComponent('submit'); - const clickEvent = new Event('click'); - const submitBtn = submit.refs.button; - submitBtn.dispatchEvent(clickEvent); - - setTimeout(() => { - assert.equal(typeof select.dataValue, 'object'); - done(); - }, 200); - }, 200); - }).catch(done); - }); - - it('Should set submission value for Resource DataSrc Type and Entire Object Value Property', function(done) { - const form = _.cloneDeep(comp15); - const element = document.createElement('div'); - - Formio.createForm(element, form).then(form => { - const select = form.getComponent('select'); - const value = { textField: 'Jone', nubmer: 1 }; - form.submission = { - data: { - select: value - } - }; - - setTimeout(() => { - assert.equal(typeof select.dataValue, 'object'); - const selectContainer = element.querySelector('[ref="selectContainer"]'); - assert.notEqual(selectContainer, null); - assert.notEqual(selectContainer.value, ''); - const options = selectContainer.childNodes; - assert.equal(options.length, 2); - done(); - }, 1000); - }).catch(done); - }); - - it('Should get string representation of value for Resource DataSrc Type and Entire Object Value Property', function(done) { - Harness.testCreate(SelectComponent, comp15.components[0]).then((component) => { - const entireObject = { - a: '1', - b: '2', - }; - const formattedValue = component.getView(entireObject); - assert.equal(formattedValue, JSON.stringify(entireObject)); - done(); +describe('Select Component with Entire Object Value Property', function () { + it('Should provide correct value', function (done) { + const form = _.cloneDeep(comp15); + const element = document.createElement('div'); + + Formio.createForm(element, form) + .then((form) => { + const select = form.getComponent('select'); + const value = { textField: 'rgd', submit: true, number: 11 }; + select.setValue(value); + + setTimeout(() => { + assert.equal(select.getValue(), value); + assert.equal(select.dataValue, value); + const submit = form.getComponent('submit'); + const clickEvent = new Event('click'); + const submitBtn = submit.refs.button; + submitBtn.dispatchEvent(clickEvent); + + setTimeout(() => { + assert.equal(select.dataValue, value); + done(); + }, 200); + }, 200); + }) + .catch(done); + }); + + it('Should provide correct items for Resource DataSrc Type and Entire Object Value Property', function (done) { + const form = _.cloneDeep(comp15); + const testItems = [ + { textField: 'Jone', number: 1 }, + { textField: 'Mary', number: 2 }, + { textField: 'Sally', number: 3 }, + ]; + const element = document.createElement('div'); + + Formio.createForm(element, form) + .then((form) => { + const select = form.getComponent('select'); + select.setItems(testItems.map((item) => ({ data: item }))); + const value = { textField: 'Jone', number: 1 }; + select.setValue(value); + assert.equal(select.selectOptions.length, 3); + + setTimeout(() => { + assert.equal(select.dataValue, value); + const submit = form.getComponent('submit'); + const clickEvent = new Event('click'); + const submitBtn = submit.refs.button; + submitBtn.dispatchEvent(clickEvent); + + setTimeout(() => { + assert.equal(typeof select.dataValue, 'object'); + done(); + }, 200); + }, 200); + }) + .catch(done); + }); + + it('Should provide correct html value for Resource DataSrc Type and Entire Object Value Property', function (done) { + const form = _.cloneDeep(comp15); + const testItems = [ + { textField: 'Jone', number: 1 }, + { textField: 'Mary', number: 2 }, + { textField: 'Sally', number: 3 }, + ]; + const element = document.createElement('div'); + + Formio.createForm(element, form) + .then((form) => { + const select = form.getComponent('select'); + select.setItems(testItems.map((item) => ({ data: item }))); + const selectContainer = element.querySelector( + '[ref="selectContainer"]', + ); + assert.notEqual(selectContainer, null); + const options = selectContainer.childNodes; + assert.equal(options.length, 4); + options.forEach((option) => { + assert.notEqual(option.value, '[object Object]'); + }); + const value = { textField: 'Jone', number: 1 }; + select.setValue(value); + assert.equal(select.selectOptions.length, 3); + + setTimeout(() => { + assert.equal(select.dataValue, value); + const submit = form.getComponent('submit'); + const clickEvent = new Event('click'); + const submitBtn = submit.refs.button; + submitBtn.dispatchEvent(clickEvent); + + setTimeout(() => { + assert.equal(typeof select.dataValue, 'object'); + done(); + }, 200); + }, 200); + }) + .catch(done); + }); + + it('Should set submission value for Resource DataSrc Type and Entire Object Value Property', function (done) { + const form = _.cloneDeep(comp15); + const element = document.createElement('div'); + + Formio.createForm(element, form) + .then((form) => { + const select = form.getComponent('select'); + const value = { textField: 'Jone', nubmer: 1 }; + form.submission = { + data: { + select: value, + }, + }; + + setTimeout(() => { + assert.equal(typeof select.dataValue, 'object'); + const selectContainer = element.querySelector( + '[ref="selectContainer"]', + ); + assert.notEqual(selectContainer, null); + assert.notEqual(selectContainer.value, ''); + const options = selectContainer.childNodes; + assert.equal(options.length, 2); + done(); + }, 1000); + }) + .catch(done); + }); + + it('Should get string representation of value for Resource DataSrc Type and Entire Object Value Property', function (done) { + Harness.testCreate(SelectComponent, comp15.components[0]).then( + (component) => { + const entireObject = { + a: '1', + b: '2', + }; + const formattedValue = component.getView(entireObject); + assert.equal(formattedValue, JSON.stringify(entireObject)); + done(); + }, + ); }); - }); }); diff --git a/src/components/select/editForm/Select.edit.data.js b/src/components/select/editForm/Select.edit.data.js index df6ad3e1df..e557f1bddd 100644 --- a/src/components/select/editForm/Select.edit.data.js +++ b/src/components/select/editForm/Select.edit.data.js @@ -1,629 +1,602 @@ import { eachComponent } from '../../../utils/utils'; export default [ - { - key: 'dataSrc', - data: { - values: [ - { label: 'Values', value: 'values' }, - { label: 'URL', value: 'url' }, - { label: 'Resource', value: 'resource' }, - { label: 'Custom', value: 'custom' }, - { label: 'Raw JSON', value: 'json' }, - ], - }, - }, - { - type: 'textfield', - weight: 10, - input: true, - key: 'indexeddb.database', - label: 'Database name', - tooltip: 'The name of the indexeddb database.', - conditional: { - json: { '===': [{ var: 'data.dataSrc' }, 'indexeddb'] }, - }, - }, - { - type: 'textfield', - input: true, - key: 'indexeddb.table', - label: 'Table name', - weight: 16, - tooltip: 'The name of table in the indexeddb database.', - conditional: { - json: { '===': [{ var: 'data.dataSrc' }, 'indexeddb'] }, - } - }, - { - type: 'textarea', - as: 'json', - editor: 'ace', - weight: 18, - input: true, - key: 'indexeddb.filter', - label: 'Row Filter', - tooltip: 'Filter table items that match the object.', - defaultValue: {}, - conditional: { - json: { '===': [{ var: 'data.dataSrc' }, 'indexeddb'] }, - }, - }, - { - type: 'textarea', - as: 'json', - editor: 'ace', - weight: 10, - input: true, - key: 'data.json', - label: 'Data Source Raw JSON', - tooltip: 'A valid JSON array to use as a data source.', - description: '
    Example:
    ["apple", "banana", "orange"].
    Example 2:
    [{"name": "John", "email": "john.doe@test.com"}, {"name": "Jane", "email": "jane.doe@test.com"}].
    ', - conditional: { - json: { '===': [{ var: 'data.dataSrc' }, 'json'] }, - }, - }, - { - type: 'checkbox', - input: true, - label: 'Lazy Load Data', - key: 'lazyLoad', - tooltip: 'When set, this will not fire off the request to the URL until this control is within focus. This can improve performance if you have many Select dropdowns on your form where the API\'s will only fire when the control is activated.', - weight: 11, - conditional: { - json: { - and: [ - { - in: [ - { var: 'data.dataSrc' }, - [ - 'resource', - 'url', - ], + { + key: 'dataSrc', + data: { + values: [ + { label: 'Values', value: 'values' }, + { label: 'URL', value: 'url' }, + { label: 'Resource', value: 'resource' }, + { label: 'Custom', value: 'custom' }, + { label: 'Raw JSON', value: 'json' }, ], - }, - { - '!==': [ - { var: 'data.widget' }, - 'html5' - ] - } - ] - }, - }, - }, - { - type: 'datagrid', - input: true, - label: 'Data Source Values', - key: 'data.values', - tooltip: 'Values to use as the data source. Labels are shown in the select field. Values are the corresponding values saved with the submission.', - weight: 10, - reorder: true, - defaultValue: [{ label: '', value: '' }], - components: [ - { - label: 'Label', - key: 'label', + }, + }, + { + type: 'textfield', + weight: 10, input: true, + key: 'indexeddb.database', + label: 'Database name', + tooltip: 'The name of the indexeddb database.', + conditional: { + json: { '===': [{ var: 'data.dataSrc' }, 'indexeddb'] }, + }, + }, + { type: 'textfield', - }, - { - label: 'Value', - key: 'value', input: true, + key: 'indexeddb.table', + label: 'Table name', + weight: 16, + tooltip: 'The name of table in the indexeddb database.', + conditional: { + json: { '===': [{ var: 'data.dataSrc' }, 'indexeddb'] }, + }, + }, + { + type: 'textarea', + as: 'json', + editor: 'ace', + weight: 18, + input: true, + key: 'indexeddb.filter', + label: 'Row Filter', + tooltip: 'Filter table items that match the object.', + defaultValue: {}, + conditional: { + json: { '===': [{ var: 'data.dataSrc' }, 'indexeddb'] }, + }, + }, + { + type: 'textarea', + as: 'json', + editor: 'ace', + weight: 10, + input: true, + key: 'data.json', + label: 'Data Source Raw JSON', + tooltip: 'A valid JSON array to use as a data source.', + description: + '
    Example:
    ["apple", "banana", "orange"].
    Example 2:
    [{"name": "John", "email": "john.doe@test.com"}, {"name": "Jane", "email": "jane.doe@test.com"}].
    ', + conditional: { + json: { '===': [{ var: 'data.dataSrc' }, 'json'] }, + }, + }, + { + type: 'checkbox', + input: true, + label: 'Lazy Load Data', + key: 'lazyLoad', + tooltip: + "When set, this will not fire off the request to the URL until this control is within focus. This can improve performance if you have many Select dropdowns on your form where the API's will only fire when the control is activated.", + weight: 11, + conditional: { + json: { + and: [ + { + in: [{ var: 'data.dataSrc' }, ['resource', 'url']], + }, + { + '!==': [{ var: 'data.widget' }, 'html5'], + }, + ], + }, + }, + }, + { + type: 'datagrid', + input: true, + label: 'Data Source Values', + key: 'data.values', + tooltip: + 'Values to use as the data source. Labels are shown in the select field. Values are the corresponding values saved with the submission.', + weight: 10, + reorder: true, + defaultValue: [{ label: '', value: '' }], + components: [ + { + label: 'Label', + key: 'label', + input: true, + type: 'textfield', + }, + { + label: 'Value', + key: 'value', + input: true, + type: 'textfield', + allowCalculateOverride: true, + calculateValue: 'value = _.camelCase(row.label);', + }, + ], + conditional: { + json: { '===': [{ var: 'data.dataSrc' }, 'values'] }, + }, + }, + { + type: 'select', + input: true, + dataSrc: 'url', + data: { + url: '/form?type=resource&limit=1000000&select=_id,title', + }, + authenticate: true, + template: '{{ item.title }}', + valueProperty: '_id', + clearOnHide: false, + label: 'Resource', + key: 'data.resource', + lazyLoad: false, + weight: 10, + tooltip: 'The resource to be used with this field.', + conditional: { + json: { '===': [{ var: 'data.dataSrc' }, 'resource'] }, + }, + }, + { type: 'textfield', - allowCalculateOverride: true, - calculateValue: 'value = _.camelCase(row.label);', - }, - ], - conditional: { - json: { '===': [{ var: 'data.dataSrc' }, 'values'] }, - }, - }, - { - type: 'select', - input: true, - dataSrc: 'url', - data: { - url: '/form?type=resource&limit=1000000&select=_id,title', - }, - authenticate: true, - template: '{{ item.title }}', - valueProperty: '_id', - clearOnHide: false, - label: 'Resource', - key: 'data.resource', - lazyLoad: false, - weight: 10, - tooltip: 'The resource to be used with this field.', - conditional: { - json: { '===': [{ var: 'data.dataSrc' }, 'resource'] }, - }, - }, - { - type: 'textfield', - input: true, - label: 'Data Path', - key: 'selectValues', - weight: 12, - description: 'The object path to the iterable items.', - tooltip: 'The property within the source data, where iterable items reside. For example: results.items or results[0].items', - conditional: { - json: { '===': [{ var: 'data.dataSrc' }, 'url'] }, - }, - }, - { - type: 'select', - input: true, - label: 'Value Property', - key: 'valueProperty', - skipMerge: true, - clearOnHide: true, - tooltip: 'The field to use as the value.', - weight: 11, - refreshOn: 'data.resource', - template: '{{ item.label }}', - valueProperty: 'key', - dataSrc: 'url', - lazyLoad: false, - onSetItems(component, form) { - const newItems = form.type === 'resource' - ? [{ - label: '{Entire Object}', - key: 'data', - }] - : []; + input: true, + label: 'Data Path', + key: 'selectValues', + weight: 12, + description: 'The object path to the iterable items.', + tooltip: + 'The property within the source data, where iterable items reside. For example: results.items or results[0].items', + conditional: { + json: { '===': [{ var: 'data.dataSrc' }, 'url'] }, + }, + }, + { + type: 'select', + input: true, + label: 'Value Property', + key: 'valueProperty', + skipMerge: true, + clearOnHide: true, + tooltip: 'The field to use as the value.', + weight: 11, + refreshOn: 'data.resource', + template: '{{ item.label }}', + valueProperty: 'key', + dataSrc: 'url', + lazyLoad: false, + onSetItems(component, form) { + const newItems = + form.type === 'resource' + ? [ + { + label: '{Entire Object}', + key: 'data', + }, + ] + : []; - eachComponent(form.components, (component, path) => { - if (component.input) { - newItems.push({ - label: component.label || component.key, - key: `data.${path}` - }); - } - }); - return newItems; - }, - onChange(context) { - if (context && context.flags && context.flags.modified) { - const valueProp = context.instance.data.valueProperty; - const templateProp = valueProp ? valueProp : 'data'; - const template = `{{ item.${templateProp} }}`; - const searchField = valueProp ? `${valueProp}__regex` : ''; - context.instance.root.getComponent('template').setValue(template); - context.instance.root.getComponent('searchField').setValue(searchField); - } - }, - data: { - url: '/form/{{ data.data.resource }}', - }, - conditional: { - json: { - and: [ - { '===': [{ var: 'data.dataSrc' }, 'resource'] }, - { '!==': [{ var: 'data.reference' }, true] }, - { var: 'data.data.resource' }, - ], - }, - }, - }, - { - type: 'select', - input: true, - label: 'Storage Type', - key: 'dataType', - clearOnHide: true, - tooltip: 'The type to store the data. If you select something other than autotype, it will force it to that type.', - weight: 12, - template: '{{ item.label }}', - dataSrc: 'values', - data: { - values: [ - { label: 'Autotype', value: 'auto' }, - { label: 'String', value: 'string' }, - { label: 'Number', value: 'number' }, - { label: 'Boolean', value: 'boolean' }, - { label: 'Object', value: 'object' }, - ], - }, - }, - { - type: 'textfield', - input: true, - key: 'idPath', - weight: 12, - label: 'ID Path', - placeholder: 'id', - tooltip: 'Path to the select option id.' - }, - { - type: 'textfield', - input: true, - label: 'Select Fields', - key: 'selectFields', - tooltip: 'The properties on the resource to return as part of the options. Separate property names by commas. If left blank, all properties will be returned.', - placeholder: 'Comma separated list of fields to select.', - weight: 14, - conditional: { - json: { - and: [ - { '===': [{ var: 'data.dataSrc' }, 'resource'] }, - { '===': [{ var: 'data.valueProperty' }, ''] }, - ], - }, - }, - }, - { - type: 'checkbox', - input: true, - key: 'disableLimit', - label: 'Disable limiting response', - tooltip: 'When enabled the request will not include the limit and skip options in the query string', - weight: 15, - conditional: { - json: { '===': [{ var: 'data.dataSrc' }, 'url'] }, - }, - }, - { - type: 'textfield', - input: true, - key: 'searchField', - label: 'Search Query Name', - weight: 16, - description: 'Name of URL query parameter', - tooltip: 'The name of the search querystring parameter used when sending a request to filter results with. The server at the URL must handle this query parameter.', - conditional: { - json: { - in: [ - { var: 'data.dataSrc' }, - [ - 'url', - 'resource', - ], - ], - }, - }, - }, - { - type: 'number', - input: true, - key: 'searchDebounce', - label: 'Search request delay', - weight: 16, - description: 'The delay (in seconds) before the search request is sent.', - tooltip: 'The delay in seconds before the search request is sent, measured from the last character input in the search field.', - validate: { - min: 0, - customMessage: '', - json: '', - max: 1, - }, - delimiter: false, - requireDecimal: false, - encrypted: false, - defaultValue: 0.3, - conditional: { - json: { - in: [ - { var: 'data.dataSrc' }, - [ - 'url', - 'resource', - ], - ], - }, - }, - }, - { - type: 'number', - input: true, - key: 'minSearch', - weight: 17, - label: 'Minimum Search Length', - tooltip: 'The minimum amount of characters they must type before a search is made.', - defaultValue: 0, - conditional: { - json: { - and: [ - { '===': [{ var: 'data.dataSrc' }, 'url'] }, - { '!=': [{ var: 'data.searchField' }, ''] }, - ], - }, - }, - }, - { - type: 'textfield', - input: true, - key: 'filter', - label: 'Filter Query', - weight: 18, - description: 'The filter query for results.', - tooltip: 'Use this to provide additional filtering using query parameters.', - conditional: { - json: { - in: [ - { var: 'data.dataSrc' }, - [ - 'url', - 'resource', - ], - ], - }, - }, - }, - { - type: 'textfield', - input: true, - key: 'sort', - label: 'Sort Query', - weight: 18, - description: 'The sort query for results', - tooltip: 'Use this to provide additional sorting using query parameters', - conditional: { - json: { - in: [ - { var: 'data.dataSrc' }, - [ - 'url', - 'resource', - ], - ], - }, - }, - }, - { - type: 'number', - input: true, - key: 'limit', - label: 'Limit', - weight: 18, - description: 'Maximum number of items to view per page of results.', - tooltip: 'Use this to limit the number of items to request or view.', - clearOnHide: false, - conditional: { - json: { - and: [ - { in: [ - { var: 'data.dataSrc' }, - [ - 'url', - 'resource' - ], - ] }, - { '!==': [{ var: 'data.disableLimit' }, true] } - ] - }, - }, - }, - { - type: 'textarea', - input: true, - key: 'data.custom', - label: 'Custom Values', - editor: 'ace', - rows: 10, - weight: 14, - placeholder: "values = data['mykey'] or values = Promise.resolve(['myValue'])", - tooltip: 'Write custom code to return the value options or a promise with value options. The form data object is available.', - conditional: { - json: { '===': [{ var: 'data.dataSrc' }, 'custom'] }, - }, - }, - { - type: 'select', - input: true, - key: 'refreshOn', - label: 'Refresh Options On', - weight: 19, - tooltip: 'Refresh data when another field changes.', - dataSrc: 'custom', - valueProperty: 'value', - data: { - custom(context) { - var values = []; - values.push({ label: 'Any Change', value: 'data' }); - context.utils.eachComponent(context.instance.options.editForm.components, function(component, path) { - if (component.key !== context.data.key) { - values.push({ - label: component.label || component.key, - value: path + eachComponent(form.components, (component, path) => { + if (component.input) { + newItems.push({ + label: component.label || component.key, + key: `data.${path}`, + }); + } }); - } - }); - return values; - } - }, - conditional: { - json: { - in: [ - { var: 'data.dataSrc' }, - [ - 'url', - 'resource', - 'values', - 'custom' - ], - ], - }, - }, - }, - { - type: 'select', - input: true, - key: 'refreshOnBlur', - label: 'Refresh Options On Blur', - weight: 19, - tooltip: 'Refresh data when another field is blured.', - dataSrc: 'custom', - valueProperty: 'value', - data: { - custom(context) { - var values = []; - values.push({ label: 'Any Change', value: 'data' }); - context.utils.eachComponent(context.instance.options.editForm.components, function(component, path) { - if (component.key !== context.data.key) { - values.push({ - label: component.label || component.key, - value: path - }); - } - }); - return values; - } - }, - conditional: { - json: { - in: [ - { var: 'data.dataSrc' }, - [ - 'url', - 'resource', - 'values' - ], - ], - }, - }, - }, - { - type: 'checkbox', - input: true, - weight: 20, - key: 'clearOnRefresh', - label: 'Clear Value On Refresh Options', - defaultValue: false, - tooltip: 'When the Refresh On field is changed, clear this components value.', - conditional: { - json: { - in: [ - { var: 'data.dataSrc' }, - [ - 'url', - 'resource', - 'values', - 'custom' - ], - ], - }, - }, - }, - { - type: 'checkbox', - input: true, - weight: 21, - key: 'searchEnabled', - label: 'Enable Static Search', - defaultValue: true, - tooltip: 'When checked, the select dropdown will allow for searching within the static list of items provided.', - }, - { - type: 'checkbox', - input: true, - weight: 21, - key: 'noRefreshOnScroll', - label: 'Disable Options Refresh When Scrolling', - defaultValue: false, - tooltip: 'When checked, the select with search input won\'t perform new api requests when scrolling through the list of options.', - conditional: { - json: { - and: [ - { in: [ - { var: 'data.dataSrc' }, - [ - 'url', - 'resource' - ], - ] }, - { '===': [{ var: 'data.searchEnabled' }, true] } - ] - }, - }, - }, - { - label: 'Search Threshold', - mask: false, - tableView: true, - alwaysEnabled: false, - type: 'number', - input: true, - key: 'selectThreshold', - validate: { - min: 0, - customMessage: '', - json: '', - max: 1, - }, - delimiter: false, - requireDecimal: false, - encrypted: false, - defaultValue: 0.3, - weight: 22, - tooltip: 'At what point does the match algorithm give up. A threshold of 0.0 requires a perfect match, a threshold of 1.0 would match anything.', - }, - { - type: 'checkbox', - input: true, - weight: 23, - key: 'addResource', - label: 'Add Resource', - tooltip: 'Allows to create a new resource while entering a submission.', - conditional: { - json: { '===': [{ var: 'data.dataSrc' }, 'resource'] }, - }, - }, - { - type: 'textfield', - label: 'Add Resource Label', - key: 'addResourceLabel', - tooltip: 'Set the text of the Add Resource button.', - placeholder: 'Add Resource', - weight: 24, - input: true, - conditional: { - json: { - and: [ - { '===': [{ var: 'data.dataSrc' }, 'resource'] }, - { '!!': { var: 'data.addResource' } }, - ], - }, - }, - }, - { - type: 'checkbox', - input: true, - weight: 25, - key: 'reference', - label: 'Save as reference', - tooltip: 'Using this option will save this field as a reference and link its value to the value of the origin record.', - conditional: { - json: { '===': [{ var: 'data.dataSrc' }, 'resource'] }, - }, - }, - { - type: 'checkbox', - input: true, - weight: 27, - key: 'readOnlyValue', - label: 'Read Only Value', - tooltip: 'Check this if you would like to show just the value when in Read Only mode.', - }, - { - type: 'textarea', - as: 'json', - editor: 'ace', - weight: 28, - input: true, - key: 'customOptions', - label: 'Choices.js options', - tooltip: 'A raw JSON object to use as options for the Select component (Choices JS).', - defaultValue: {}, - }, - { - type: 'checkbox', - input: true, - weight: 29, - key: 'useExactSearch', - label: 'Use exact search', - tooltip: 'Disables search algorithm threshold.', - } + return newItems; + }, + onChange(context) { + if (context && context.flags && context.flags.modified) { + const valueProp = context.instance.data.valueProperty; + const templateProp = valueProp ? valueProp : 'data'; + const template = `{{ item.${templateProp} }}`; + const searchField = valueProp ? `${valueProp}__regex` : ''; + context.instance.root + .getComponent('template') + .setValue(template); + context.instance.root + .getComponent('searchField') + .setValue(searchField); + } + }, + data: { + url: '/form/{{ data.data.resource }}', + }, + conditional: { + json: { + and: [ + { '===': [{ var: 'data.dataSrc' }, 'resource'] }, + { '!==': [{ var: 'data.reference' }, true] }, + { var: 'data.data.resource' }, + ], + }, + }, + }, + { + type: 'select', + input: true, + label: 'Storage Type', + key: 'dataType', + clearOnHide: true, + tooltip: + 'The type to store the data. If you select something other than autotype, it will force it to that type.', + weight: 12, + template: '{{ item.label }}', + dataSrc: 'values', + data: { + values: [ + { label: 'Autotype', value: 'auto' }, + { label: 'String', value: 'string' }, + { label: 'Number', value: 'number' }, + { label: 'Boolean', value: 'boolean' }, + { label: 'Object', value: 'object' }, + ], + }, + }, + { + type: 'textfield', + input: true, + key: 'idPath', + weight: 12, + label: 'ID Path', + placeholder: 'id', + tooltip: 'Path to the select option id.', + }, + { + type: 'textfield', + input: true, + label: 'Select Fields', + key: 'selectFields', + tooltip: + 'The properties on the resource to return as part of the options. Separate property names by commas. If left blank, all properties will be returned.', + placeholder: 'Comma separated list of fields to select.', + weight: 14, + conditional: { + json: { + and: [ + { '===': [{ var: 'data.dataSrc' }, 'resource'] }, + { '===': [{ var: 'data.valueProperty' }, ''] }, + ], + }, + }, + }, + { + type: 'checkbox', + input: true, + key: 'disableLimit', + label: 'Disable limiting response', + tooltip: + 'When enabled the request will not include the limit and skip options in the query string', + weight: 15, + conditional: { + json: { '===': [{ var: 'data.dataSrc' }, 'url'] }, + }, + }, + { + type: 'textfield', + input: true, + key: 'searchField', + label: 'Search Query Name', + weight: 16, + description: 'Name of URL query parameter', + tooltip: + 'The name of the search querystring parameter used when sending a request to filter results with. The server at the URL must handle this query parameter.', + conditional: { + json: { + in: [{ var: 'data.dataSrc' }, ['url', 'resource']], + }, + }, + }, + { + type: 'number', + input: true, + key: 'searchDebounce', + label: 'Search request delay', + weight: 16, + description: + 'The delay (in seconds) before the search request is sent.', + tooltip: + 'The delay in seconds before the search request is sent, measured from the last character input in the search field.', + validate: { + min: 0, + customMessage: '', + json: '', + max: 1, + }, + delimiter: false, + requireDecimal: false, + encrypted: false, + defaultValue: 0.3, + conditional: { + json: { + in: [{ var: 'data.dataSrc' }, ['url', 'resource']], + }, + }, + }, + { + type: 'number', + input: true, + key: 'minSearch', + weight: 17, + label: 'Minimum Search Length', + tooltip: + 'The minimum amount of characters they must type before a search is made.', + defaultValue: 0, + conditional: { + json: { + and: [ + { '===': [{ var: 'data.dataSrc' }, 'url'] }, + { '!=': [{ var: 'data.searchField' }, ''] }, + ], + }, + }, + }, + { + type: 'textfield', + input: true, + key: 'filter', + label: 'Filter Query', + weight: 18, + description: 'The filter query for results.', + tooltip: + 'Use this to provide additional filtering using query parameters.', + conditional: { + json: { + in: [{ var: 'data.dataSrc' }, ['url', 'resource']], + }, + }, + }, + { + type: 'textfield', + input: true, + key: 'sort', + label: 'Sort Query', + weight: 18, + description: 'The sort query for results', + tooltip: + 'Use this to provide additional sorting using query parameters', + conditional: { + json: { + in: [{ var: 'data.dataSrc' }, ['url', 'resource']], + }, + }, + }, + { + type: 'number', + input: true, + key: 'limit', + label: 'Limit', + weight: 18, + description: 'Maximum number of items to view per page of results.', + tooltip: 'Use this to limit the number of items to request or view.', + clearOnHide: false, + conditional: { + json: { + and: [ + { in: [{ var: 'data.dataSrc' }, ['url', 'resource']] }, + { '!==': [{ var: 'data.disableLimit' }, true] }, + ], + }, + }, + }, + { + type: 'textarea', + input: true, + key: 'data.custom', + label: 'Custom Values', + editor: 'ace', + rows: 10, + weight: 14, + placeholder: + "values = data['mykey'] or values = Promise.resolve(['myValue'])", + tooltip: + 'Write custom code to return the value options or a promise with value options. The form data object is available.', + conditional: { + json: { '===': [{ var: 'data.dataSrc' }, 'custom'] }, + }, + }, + { + type: 'select', + input: true, + key: 'refreshOn', + label: 'Refresh Options On', + weight: 19, + tooltip: 'Refresh data when another field changes.', + dataSrc: 'custom', + valueProperty: 'value', + data: { + custom(context) { + var values = []; + values.push({ label: 'Any Change', value: 'data' }); + context.utils.eachComponent( + context.instance.options.editForm.components, + function (component, path) { + if (component.key !== context.data.key) { + values.push({ + label: component.label || component.key, + value: path, + }); + } + }, + ); + return values; + }, + }, + conditional: { + json: { + in: [ + { var: 'data.dataSrc' }, + ['url', 'resource', 'values', 'custom'], + ], + }, + }, + }, + { + type: 'select', + input: true, + key: 'refreshOnBlur', + label: 'Refresh Options On Blur', + weight: 19, + tooltip: 'Refresh data when another field is blured.', + dataSrc: 'custom', + valueProperty: 'value', + data: { + custom(context) { + var values = []; + values.push({ label: 'Any Change', value: 'data' }); + context.utils.eachComponent( + context.instance.options.editForm.components, + function (component, path) { + if (component.key !== context.data.key) { + values.push({ + label: component.label || component.key, + value: path, + }); + } + }, + ); + return values; + }, + }, + conditional: { + json: { + in: [{ var: 'data.dataSrc' }, ['url', 'resource', 'values']], + }, + }, + }, + { + type: 'checkbox', + input: true, + weight: 20, + key: 'clearOnRefresh', + label: 'Clear Value On Refresh Options', + defaultValue: false, + tooltip: + 'When the Refresh On field is changed, clear this components value.', + conditional: { + json: { + in: [ + { var: 'data.dataSrc' }, + ['url', 'resource', 'values', 'custom'], + ], + }, + }, + }, + { + type: 'checkbox', + input: true, + weight: 21, + key: 'searchEnabled', + label: 'Enable Static Search', + defaultValue: true, + tooltip: + 'When checked, the select dropdown will allow for searching within the static list of items provided.', + }, + { + type: 'checkbox', + input: true, + weight: 21, + key: 'noRefreshOnScroll', + label: 'Disable Options Refresh When Scrolling', + defaultValue: false, + tooltip: + "When checked, the select with search input won't perform new api requests when scrolling through the list of options.", + conditional: { + json: { + and: [ + { in: [{ var: 'data.dataSrc' }, ['url', 'resource']] }, + { '===': [{ var: 'data.searchEnabled' }, true] }, + ], + }, + }, + }, + { + label: 'Search Threshold', + mask: false, + tableView: true, + alwaysEnabled: false, + type: 'number', + input: true, + key: 'selectThreshold', + validate: { + min: 0, + customMessage: '', + json: '', + max: 1, + }, + delimiter: false, + requireDecimal: false, + encrypted: false, + defaultValue: 0.3, + weight: 22, + tooltip: + 'At what point does the match algorithm give up. A threshold of 0.0 requires a perfect match, a threshold of 1.0 would match anything.', + }, + { + type: 'checkbox', + input: true, + weight: 23, + key: 'addResource', + label: 'Add Resource', + tooltip: 'Allows to create a new resource while entering a submission.', + conditional: { + json: { '===': [{ var: 'data.dataSrc' }, 'resource'] }, + }, + }, + { + type: 'textfield', + label: 'Add Resource Label', + key: 'addResourceLabel', + tooltip: 'Set the text of the Add Resource button.', + placeholder: 'Add Resource', + weight: 24, + input: true, + conditional: { + json: { + and: [ + { '===': [{ var: 'data.dataSrc' }, 'resource'] }, + { '!!': { var: 'data.addResource' } }, + ], + }, + }, + }, + { + type: 'checkbox', + input: true, + weight: 25, + key: 'reference', + label: 'Save as reference', + tooltip: + 'Using this option will save this field as a reference and link its value to the value of the origin record.', + conditional: { + json: { '===': [{ var: 'data.dataSrc' }, 'resource'] }, + }, + }, + { + type: 'checkbox', + input: true, + weight: 27, + key: 'readOnlyValue', + label: 'Read Only Value', + tooltip: + 'Check this if you would like to show just the value when in Read Only mode.', + }, + { + type: 'textarea', + as: 'json', + editor: 'ace', + weight: 28, + input: true, + key: 'customOptions', + label: 'Choices.js options', + tooltip: + 'A raw JSON object to use as options for the Select component (Choices JS).', + defaultValue: {}, + }, + { + type: 'checkbox', + input: true, + weight: 29, + key: 'useExactSearch', + label: 'Use exact search', + tooltip: 'Disables search algorithm threshold.', + }, ]; diff --git a/src/components/select/editForm/Select.edit.display.js b/src/components/select/editForm/Select.edit.display.js index cd6e222f5d..ea2ee21755 100644 --- a/src/components/select/editForm/Select.edit.display.js +++ b/src/components/select/editForm/Select.edit.display.js @@ -1,26 +1,26 @@ export default [ - { - type: 'select', - input: true, - weight: 20, - tooltip: 'Select the type of widget you\'d like to use.', - key: 'widget', - defaultValue: 'choicesjs', - label: 'Widget Type', - dataSrc: 'values', - data: { - values: [ - { label: 'ChoicesJS', value: 'choicesjs' }, - { label: 'HTML 5', value: 'html5' }, - ], + { + type: 'select', + input: true, + weight: 20, + tooltip: "Select the type of widget you'd like to use.", + key: 'widget', + defaultValue: 'choicesjs', + label: 'Widget Type', + dataSrc: 'values', + data: { + values: [ + { label: 'ChoicesJS', value: 'choicesjs' }, + { label: 'HTML 5', value: 'html5' }, + ], + }, + }, + { + weight: 1230, + type: 'checkbox', + label: 'Unique Options', + tooltip: 'Display only unique dropdown options.', + key: 'uniqueOptions', + input: true, }, - }, - { - weight: 1230, - type: 'checkbox', - label: 'Unique Options', - tooltip: 'Display only unique dropdown options.', - key: 'uniqueOptions', - input: true - }, ]; diff --git a/src/components/select/editForm/Select.edit.validation.js b/src/components/select/editForm/Select.edit.validation.js index 704fd1444d..e251927729 100644 --- a/src/components/select/editForm/Select.edit.validation.js +++ b/src/components/select/editForm/Select.edit.validation.js @@ -1,33 +1,28 @@ export default [ - { - weight: 50, - type: 'checkbox', - label: 'Perform server validation of remote value', - tooltip: 'Check this if you would like for the server to perform a validation check to ensure the selected value is an available option. This requires a Search query to ensure a record is found.', - key: 'validate.select', - input: true, - conditional: { - json: { var: 'data.searchField' } - } - }, - { - weight: 52, - type: 'checkbox', - label: 'Allow only available values', - tooltip: 'Check this if you would like to perform a validation check to ensure the selected value is an available option (only for synchronous values).', - key: 'validate.onlyAvailableItems', - input: true, - conditional: { - json: { - in: [ - { var: 'data.dataSrc' }, - [ - 'values', - 'json', - 'custom' - ], - ], - }, + { + weight: 50, + type: 'checkbox', + label: 'Perform server validation of remote value', + tooltip: + 'Check this if you would like for the server to perform a validation check to ensure the selected value is an available option. This requires a Search query to ensure a record is found.', + key: 'validate.select', + input: true, + conditional: { + json: { var: 'data.searchField' }, + }, + }, + { + weight: 52, + type: 'checkbox', + label: 'Allow only available values', + tooltip: + 'Check this if you would like to perform a validation check to ensure the selected value is an available option (only for synchronous values).', + key: 'validate.onlyAvailableItems', + input: true, + conditional: { + json: { + in: [{ var: 'data.dataSrc' }, ['values', 'json', 'custom']], + }, + }, }, - } ]; diff --git a/src/components/select/fixtures/comp1.js b/src/components/select/fixtures/comp1.js index 325f174a75..11c72436d8 100644 --- a/src/components/select/fixtures/comp1.js +++ b/src/components/select/fixtures/comp1.js @@ -1,64 +1,64 @@ export default { - 'conditional': { - 'eq': '', - 'when': null, - 'show': '' - }, - 'tags': [], - 'type': 'select', - 'validate': { - 'required': false - }, - 'persistent': true, - 'unique': false, - 'protected': false, - 'multiple': false, - 'template': '{{ item.label }}', - 'authenticate': false, - 'filter': '', - 'refreshOn': '', - 'defaultValue': '', - 'valueProperty': '', - 'dataSrc': 'values', - 'data': { - 'custom': '', - 'resource': '', - 'url': '', - 'json': '', - 'values': [ - { - 'label': 'Red', - 'value': 'red' - }, - { - 'label': 'Blue', - 'value': 'blue' - }, - { - 'label': 'Green', - 'value': 'green' - }, - { - 'label': 'Yellow', - 'value': 'yellow' - }, - { - 'label': 'Purple', - 'value': 'purple' - }, - { - 'label': 'Orange', - 'value': 'orange' - }, - { - 'label': 'Black', - 'value': 'black' - } - ] - }, - 'placeholder': 'Enter your favorite color', - 'key': 'favoriteColor', - 'label': 'Favorite Color', - 'tableView': true, - 'input': true + conditional: { + eq: '', + when: null, + show: '', + }, + tags: [], + type: 'select', + validate: { + required: false, + }, + persistent: true, + unique: false, + protected: false, + multiple: false, + template: '{{ item.label }}', + authenticate: false, + filter: '', + refreshOn: '', + defaultValue: '', + valueProperty: '', + dataSrc: 'values', + data: { + custom: '', + resource: '', + url: '', + json: '', + values: [ + { + label: 'Red', + value: 'red', + }, + { + label: 'Blue', + value: 'blue', + }, + { + label: 'Green', + value: 'green', + }, + { + label: 'Yellow', + value: 'yellow', + }, + { + label: 'Purple', + value: 'purple', + }, + { + label: 'Orange', + value: 'orange', + }, + { + label: 'Black', + value: 'black', + }, + ], + }, + placeholder: 'Enter your favorite color', + key: 'favoriteColor', + label: 'Favorite Color', + tableView: true, + input: true, }; diff --git a/src/components/select/fixtures/comp10.js b/src/components/select/fixtures/comp10.js index 3af21848f0..cb5ef8b89d 100644 --- a/src/components/select/fixtures/comp10.js +++ b/src/components/select/fixtures/comp10.js @@ -1,30 +1,37 @@ export default { - type: 'form', - components: [ - { - label: 'Select', - tableView: true, - data: { - values: [ - { label: 'Paris', value: 'paris' }, - { label: 'London', value: 'london' }, - { label: 'Madrid', value: 'madrid' }, - { label: 'Berlin', value: 'berlin' }, - { label: 'Kiev', value: 'kiev' } - ] - }, - selectThreshold: 0.3, - validate: { onlyAvailableItems: false }, - key: 'select', - type: 'select', - indexeddb: { filter: {} }, - input: true - }, - { type: 'button', label: 'Submit', key: 'submit', disableOnInvalid: true, input: true, tableView: false } - ], - title: 'test', - display: 'form', - name: 'testCheckbox', - path: 'testcheckbox', - project: '5ebcf8938bdebc4c58a949a3', + type: 'form', + components: [ + { + label: 'Select', + tableView: true, + data: { + values: [ + { label: 'Paris', value: 'paris' }, + { label: 'London', value: 'london' }, + { label: 'Madrid', value: 'madrid' }, + { label: 'Berlin', value: 'berlin' }, + { label: 'Kiev', value: 'kiev' }, + ], + }, + selectThreshold: 0.3, + validate: { onlyAvailableItems: false }, + key: 'select', + type: 'select', + indexeddb: { filter: {} }, + input: true, + }, + { + type: 'button', + label: 'Submit', + key: 'submit', + disableOnInvalid: true, + input: true, + tableView: false, + }, + ], + title: 'test', + display: 'form', + name: 'testCheckbox', + path: 'testcheckbox', + project: '5ebcf8938bdebc4c58a949a3', }; diff --git a/src/components/select/fixtures/comp11.js b/src/components/select/fixtures/comp11.js index 13830e1cb5..9b1b210869 100644 --- a/src/components/select/fixtures/comp11.js +++ b/src/components/select/fixtures/comp11.js @@ -1,22 +1,29 @@ export default { - components: [ - { - type: 'select', - label: 'Select JSON', - key: 'select', - placeholder: 'Select one', - data: { - json: `[ + components: [ + { + type: 'select', + label: 'Select JSON', + key: 'select', + placeholder: 'Select one', + data: { + json: `[ {"value":"a","label":"A"}, {"value":"b","label":"B"}, {"value":"c","label":"C"}, {"value":"d","label":"D"} - ]` - }, - dataSrc: 'json', - template: '{{ item.label }}', - input: true - }, - { type: 'button', label: 'Submit', key: 'submit', disableOnInvalid: false, input: true, tableView: false } - ] + ]`, + }, + dataSrc: 'json', + template: '{{ item.label }}', + input: true, + }, + { + type: 'button', + label: 'Submit', + key: 'submit', + disableOnInvalid: false, + input: true, + tableView: false, + }, + ], }; diff --git a/src/components/select/fixtures/comp12.js b/src/components/select/fixtures/comp12.js index f92e14b1eb..a52f39302f 100644 --- a/src/components/select/fixtures/comp12.js +++ b/src/components/select/fixtures/comp12.js @@ -1,27 +1,37 @@ export default { - type: 'form', - components: [ - { - label: 'Select', - tableView: true, - dataSrc: 'resource', - data: { values: [{ label: '', value: '' }], resource: '60114dd32cab36ad94ac4f94' }, - valueProperty: 'data.name', - template: '{{ item.data.name }}', - selectThreshold: 0.3, - validate: { onlyAvailableItems: false }, - key: 'select', - type: 'select', - indexeddb: { filter: {} }, - searchField: 'data.name__regex', - input: true, - addResource: false, - reference: false - }, - { type: 'button', label: 'Submit', key: 'submit', disableOnInvalid: true, input: true, tableView: false } - ], - title: 'test', - display: 'form', - name: 'test', - path: 'test', + type: 'form', + components: [ + { + label: 'Select', + tableView: true, + dataSrc: 'resource', + data: { + values: [{ label: '', value: '' }], + resource: '60114dd32cab36ad94ac4f94', + }, + valueProperty: 'data.name', + template: '{{ item.data.name }}', + selectThreshold: 0.3, + validate: { onlyAvailableItems: false }, + key: 'select', + type: 'select', + indexeddb: { filter: {} }, + searchField: 'data.name__regex', + input: true, + addResource: false, + reference: false, + }, + { + type: 'button', + label: 'Submit', + key: 'submit', + disableOnInvalid: true, + input: true, + tableView: false, + }, + ], + title: 'test', + display: 'form', + name: 'test', + path: 'test', }; diff --git a/src/components/select/fixtures/comp13.js b/src/components/select/fixtures/comp13.js index 2af63a6628..d2993445ad 100644 --- a/src/components/select/fixtures/comp13.js +++ b/src/components/select/fixtures/comp13.js @@ -1,30 +1,29 @@ export default { - label: 'Location', - widget: 'html5', - tableView: true, - dataSrc: 'custom', - data: { - values: [ - { - label: '', - value: '', - }, - ], - custom: - 'values = [\r\n{\r\n "label" : "Test 1",\r\n "value" : "test1"\r\n },\r\n{\r\n "label" : "Test 2",\r\n "value" : "test2"\r\n }\r\n];\r\n', - }, - refreshOn: 'district', - clearOnRefresh: true, - selectThreshold: 0.3, - calculateServer: true, - validate: { - onlyAvailableItems: false, - }, - key: 'location', - type: 'select', - indexeddb: { - filter: {}, - }, - input: true, - hideOnChildrenHidden: false, + label: 'Location', + widget: 'html5', + tableView: true, + dataSrc: 'custom', + data: { + values: [ + { + label: '', + value: '', + }, + ], + custom: 'values = [\r\n{\r\n "label" : "Test 1",\r\n "value" : "test1"\r\n },\r\n{\r\n "label" : "Test 2",\r\n "value" : "test2"\r\n }\r\n];\r\n', + }, + refreshOn: 'district', + clearOnRefresh: true, + selectThreshold: 0.3, + calculateServer: true, + validate: { + onlyAvailableItems: false, + }, + key: 'location', + type: 'select', + indexeddb: { + filter: {}, + }, + input: true, + hideOnChildrenHidden: false, }; diff --git a/src/components/select/fixtures/comp14.js b/src/components/select/fixtures/comp14.js index 8d79677230..def08d129d 100644 --- a/src/components/select/fixtures/comp14.js +++ b/src/components/select/fixtures/comp14.js @@ -1,25 +1,30 @@ export default { - type: 'form', - components: [ - { - label: 'Select', - tableView: true, - data: { - values: [ - { label: '', value: '' } - ] - }, - selectThreshold: 0.3, - validate: { onlyAvailableItems: false }, - key: 'select', - type: 'select', - indexeddb: { filter: {} }, - input: true - }, - { type: 'button', label: 'Submit', key: 'submit', disableOnInvalid: true, input: true, tableView: false } - ], - title: 'test', - display: 'form', - name: 'selectform', - path: 'selectform', + type: 'form', + components: [ + { + label: 'Select', + tableView: true, + data: { + values: [{ label: '', value: '' }], + }, + selectThreshold: 0.3, + validate: { onlyAvailableItems: false }, + key: 'select', + type: 'select', + indexeddb: { filter: {} }, + input: true, + }, + { + type: 'button', + label: 'Submit', + key: 'submit', + disableOnInvalid: true, + input: true, + tableView: false, + }, + ], + title: 'test', + display: 'form', + name: 'selectform', + path: 'selectform', }; diff --git a/src/components/select/fixtures/comp15.js b/src/components/select/fixtures/comp15.js index d1ab01b116..cf1b6eb32b 100644 --- a/src/components/select/fixtures/comp15.js +++ b/src/components/select/fixtures/comp15.js @@ -1,39 +1,39 @@ export default { - _id: '6110fab8ca845631779c2bc5', - type: 'form', - components: [ - { - label: 'Select', - widget: 'html5', - tableView: true, - dataSrc: 'resource', - data: { - resource: '6110fa7961e298191332514d', - project: 'gjrwbdsqkpxesha', - }, - dataType: 'object', - valueProperty: 'data', - template: '{{ item.data }}', - validate: { select: false }, - key: 'select', - type: 'select', - searchField: 'data__regex', - input: true, - addResource: false, - reference: false, - }, - { - label: 'Submit', - showValidations: false, - tableView: false, - key: 'submit', - type: 'button', - input: true, - }, - ], - title: 'FIO-3011', - display: 'form', - name: 'fio3011', - path: 'fio3011', - project: '6108d710d447e01ac3d81b8f', + _id: '6110fab8ca845631779c2bc5', + type: 'form', + components: [ + { + label: 'Select', + widget: 'html5', + tableView: true, + dataSrc: 'resource', + data: { + resource: '6110fa7961e298191332514d', + project: 'gjrwbdsqkpxesha', + }, + dataType: 'object', + valueProperty: 'data', + template: '{{ item.data }}', + validate: { select: false }, + key: 'select', + type: 'select', + searchField: 'data__regex', + input: true, + addResource: false, + reference: false, + }, + { + label: 'Submit', + showValidations: false, + tableView: false, + key: 'submit', + type: 'button', + input: true, + }, + ], + title: 'FIO-3011', + display: 'form', + name: 'fio3011', + path: 'fio3011', + project: '6108d710d447e01ac3d81b8f', }; diff --git a/src/components/select/fixtures/comp16.js b/src/components/select/fixtures/comp16.js index 4106d67ae1..d33dc5c37f 100644 --- a/src/components/select/fixtures/comp16.js +++ b/src/components/select/fixtures/comp16.js @@ -1,31 +1,31 @@ export default { _id: '60ec032f86daf81238ab4e37', - type: 'form', - components: [ - { - label: 'Select', - widget: 'choicesjs', - tableView: true, - dataSrc: 'custom', - valueProperty:'value', - data: { - custom:"values = Promise.resolve([\n {label: 'A', value: 'aa'}, \n {label: 'B', value: 'bb'}, \n {label: 'C', value: 'cc'}\n]);", - }, - key: 'select', - type: 'select', - input: true - }, - { - type: 'button', - label: 'Submit', - key: 'submit', - disableOnInvalid: true, - input: true, - tableView: false - } - ], - title: 'custom async', - display: 'form', - name: 'customAsync', - path: 'customasync', + type: 'form', + components: [ + { + label: 'Select', + widget: 'choicesjs', + tableView: true, + dataSrc: 'custom', + valueProperty: 'value', + data: { + custom: "values = Promise.resolve([\n {label: 'A', value: 'aa'}, \n {label: 'B', value: 'bb'}, \n {label: 'C', value: 'cc'}\n]);", + }, + key: 'select', + type: 'select', + input: true, + }, + { + type: 'button', + label: 'Submit', + key: 'submit', + disableOnInvalid: true, + input: true, + tableView: false, + }, + ], + title: 'custom async', + display: 'form', + name: 'customAsync', + path: 'customasync', }; diff --git a/src/components/select/fixtures/comp17.js b/src/components/select/fixtures/comp17.js index 9960c0b778..389db52ea9 100644 --- a/src/components/select/fixtures/comp17.js +++ b/src/components/select/fixtures/comp17.js @@ -1,41 +1,41 @@ export default { _id: '6110fab8ca845631779c2bc5', - type: 'form', - components: [ - { - label: 'Select', - widget: 'choicesjs', - tableView: true, - dataSrc: 'resource', - data: { - resource: '63f8a8d04890866e84b9540c' + type: 'form', + components: [ + { + label: 'Select', + widget: 'choicesjs', + tableView: true, + dataSrc: 'resource', + data: { + resource: '63f8a8d04890866e84b9540c', + }, + dataType: 'string', + valueProperty: 'data.textField', + template: '{{ item.data.textField }}', + validate: { + select: false, + }, + key: 'select', + type: 'select', + searchField: 'data.textField__regex', + noRefreshOnScroll: false, + addResource: false, + reference: false, + input: true, }, - dataType: 'string', - valueProperty: 'data.textField', - template: '{{ item.data.textField }}', - validate: { - select: false + { + label: 'Submit', + showValidations: false, + tableView: false, + key: 'submit', + type: 'button', + input: true, }, - key: 'select', - type: 'select', - searchField: 'data.textField__regex', - noRefreshOnScroll: false, - addResource: false, - reference: false, - input: true - }, - { - label: 'Submit', - showValidations: false, - tableView: false, - key: 'submit', - type: 'button', - input: true, - }, - ], - title: 'FIO-3011', - display: 'form', - name: 'fio3011', - path: 'fio3011', - project: '6108d710d447e01ac3d81b8f', + ], + title: 'FIO-3011', + display: 'form', + name: 'fio3011', + path: 'fio3011', + project: '6108d710d447e01ac3d81b8f', }; diff --git a/src/components/select/fixtures/comp18.js b/src/components/select/fixtures/comp18.js index 63b7b56875..5c4649974c 100644 --- a/src/components/select/fixtures/comp18.js +++ b/src/components/select/fixtures/comp18.js @@ -1,40 +1,40 @@ export default { - _id: '6450b301441fa8778d1ca28e', - type: 'form', - components: [ - { - label: 'Select', - widget: 'choicesjs', - tableView: true, - dataSrc: 'resource', - data: { - resource: '6450b4bb441fa8778d1ca69e' - }, - valueProperty: 'data.textField', - template: '{{ item.data.textField }}', - validate: { - select: false - }, - key: 'select', - type: 'select', - searchField: 'data.textField__regex', - noRefreshOnScroll: false, - addResource: false, - reference: false, - input: true - }, - { - label: 'Submit', - showValidations: false, - tableView: false, - key: 'submit', - type: 'button', - input: true, - }, - ], - title: 'FIO-6760-Select Component', - display: 'form', - name: 'fio6760SelectComponent', - path: 'fio6760selectcomponent', - project: '6406eec64f70ff1b445c6f44', + _id: '6450b301441fa8778d1ca28e', + type: 'form', + components: [ + { + label: 'Select', + widget: 'choicesjs', + tableView: true, + dataSrc: 'resource', + data: { + resource: '6450b4bb441fa8778d1ca69e', + }, + valueProperty: 'data.textField', + template: '{{ item.data.textField }}', + validate: { + select: false, + }, + key: 'select', + type: 'select', + searchField: 'data.textField__regex', + noRefreshOnScroll: false, + addResource: false, + reference: false, + input: true, + }, + { + label: 'Submit', + showValidations: false, + tableView: false, + key: 'submit', + type: 'button', + input: true, + }, + ], + title: 'FIO-6760-Select Component', + display: 'form', + name: 'fio6760SelectComponent', + path: 'fio6760selectcomponent', + project: '6406eec64f70ff1b445c6f44', }; diff --git a/src/components/select/fixtures/comp19.js b/src/components/select/fixtures/comp19.js index 5e7707c0f6..0cfb8685f9 100644 --- a/src/components/select/fixtures/comp19.js +++ b/src/components/select/fixtures/comp19.js @@ -1,34 +1,34 @@ export default { - type: 'form', - display: 'form', - components: [ - { - label: 'Select', - widget: 'choicesjs', - tableView: true, - data: { - values: [ - { - label: 'Banana', - value: 'banana' - }, - { - label: 'Apple', - value: 'apple' - }, - { - label: 'Pineapple', - value: 'pineapple' - } - ] - }, - validateOn: 'blur', - validate: { - custom: "valid = data.select == 'apple' ? true : 'You must select an apple';" - }, - key: 'select', - type: 'select', - input: true - }, - ] + type: 'form', + display: 'form', + components: [ + { + label: 'Select', + widget: 'choicesjs', + tableView: true, + data: { + values: [ + { + label: 'Banana', + value: 'banana', + }, + { + label: 'Apple', + value: 'apple', + }, + { + label: 'Pineapple', + value: 'pineapple', + }, + ], + }, + validateOn: 'blur', + validate: { + custom: "valid = data.select == 'apple' ? true : 'You must select an apple';", + }, + key: 'select', + type: 'select', + input: true, + }, + ], }; diff --git a/src/components/select/fixtures/comp2.js b/src/components/select/fixtures/comp2.js index 7405df5c7e..b8622bca71 100644 --- a/src/components/select/fixtures/comp2.js +++ b/src/components/select/fixtures/comp2.js @@ -1,65 +1,65 @@ export default { - 'conditional': { - 'eq': '', - 'when': null, - 'show': '' - }, - 'tags': [], - 'type': 'select', - 'validate': { - 'required': false - }, - 'tabindex': '10', - 'persistent': true, - 'unique': false, - 'protected': false, - 'multiple': false, - 'template': '{{ item.label }}', - 'authenticate': false, - 'filter': '', - 'refreshOn': '', - 'defaultValue': '', - 'valueProperty': '', - 'dataSrc': 'values', - 'data': { - 'custom': '', - 'resource': '', - 'url': '', - 'json': '', - 'values': [ - { - 'label': 'Red', - 'value': 'red' - }, - { - 'label': 'Blue', - 'value': 'blue' - }, - { - 'label': 'Green', - 'value': 'green' - }, - { - 'label': 'Yellow', - 'value': 'yellow' - }, - { - 'label': 'Purple', - 'value': 'purple' - }, - { - 'label': 'Orange', - 'value': 'orange' - }, - { - 'label': 'Black', - 'value': 'black' - } - ] - }, - 'placeholder': 'Enter your favorite color', - 'key': 'favoriteColor', - 'label': 'Favorite Color', - 'tableView': true, - 'input': true + conditional: { + eq: '', + when: null, + show: '', + }, + tags: [], + type: 'select', + validate: { + required: false, + }, + tabindex: '10', + persistent: true, + unique: false, + protected: false, + multiple: false, + template: '{{ item.label }}', + authenticate: false, + filter: '', + refreshOn: '', + defaultValue: '', + valueProperty: '', + dataSrc: 'values', + data: { + custom: '', + resource: '', + url: '', + json: '', + values: [ + { + label: 'Red', + value: 'red', + }, + { + label: 'Blue', + value: 'blue', + }, + { + label: 'Green', + value: 'green', + }, + { + label: 'Yellow', + value: 'yellow', + }, + { + label: 'Purple', + value: 'purple', + }, + { + label: 'Orange', + value: 'orange', + }, + { + label: 'Black', + value: 'black', + }, + ], + }, + placeholder: 'Enter your favorite color', + key: 'favoriteColor', + label: 'Favorite Color', + tableView: true, + input: true, }; diff --git a/src/components/select/fixtures/comp20.js b/src/components/select/fixtures/comp20.js index 69ff024e7a..e5c76a5c05 100644 --- a/src/components/select/fixtures/comp20.js +++ b/src/components/select/fixtures/comp20.js @@ -11,26 +11,25 @@ export default { widget: 'choicesjs', tableView: true, multiple: true, - data: - { - values: [ - { - label: 'Apple', - value: 'apple' - }, - { - label: 'Orange', - value: 'orange' - }, - { - label: 'Pear', - value: 'pear' - } - ] - }, + data: { + values: [ + { + label: 'Apple', + value: 'apple', + }, + { + label: 'Orange', + value: 'orange', + }, + { + label: 'Pear', + value: 'pear', + }, + ], + }, key: 'select', type: 'select', - input: true + input: true, }, { type: 'button', @@ -38,8 +37,8 @@ export default { key: 'submit', disableOnInvalid: true, input: true, - tableView: false - } + tableView: false, + }, ], - project: '63cead09be0090345b109e22' + project: '63cead09be0090345b109e22', }; diff --git a/src/components/select/fixtures/comp21.js b/src/components/select/fixtures/comp21.js index 198eb7189d..989a8b6a67 100644 --- a/src/components/select/fixtures/comp21.js +++ b/src/components/select/fixtures/comp21.js @@ -1,104 +1,105 @@ export default { - title: 'FIO-7632', - name: 'fio7632', - path: 'fio7632', - type: 'form', - display: 'form', - components: [ - { - collapsible: false, - key: 'panel', - type: 'panel', - label: 'Panel', - input: false, - tableView: false, - components: [ + title: 'FIO-7632', + name: 'fio7632', + path: 'fio7632', + type: 'form', + display: 'form', + components: [ { - label: 'Animals', - widget: 'html5', - tableView: true, - data: { - values: [ - { - label: 'Dog', - value: 'dog', - }, - { - label: 'Cat', - value: 'cat', - }, - { - label: 'Horse', - value: 'horse', - }, - ], - }, - key: 'animals', - type: 'select', - input: true, - }, - { - label: 'Checkbox', - tableView: false, - key: 'checkbox', - type: 'checkbox', - input: true, - }, - { - label: 'Animals2', - widget: 'html5', - tableView: true, - data: { - values: [ - { - label: 'Dog', - value: 'dog', - }, - { - label: 'Cat', - value: 'cat', - }, - { - label: 'Horse', - value: 'horse', - }, - ], - }, - calculateValue: 'if (data.checkbox === true) {\n value = data.animals;\n}', - key: 'animals2', - logic: [ - { - name: 'disable', - trigger: { - type: 'javascript', - javascript: 'result = row.checkbox === true;', - }, - actions: [ + collapsible: false, + key: 'panel', + type: 'panel', + label: 'Panel', + input: false, + tableView: false, + components: [ { - name: 'disable', - type: 'property', - property: { - label: 'Disabled', - value: 'disabled', - type: 'boolean', - }, - state: true, + label: 'Animals', + widget: 'html5', + tableView: true, + data: { + values: [ + { + label: 'Dog', + value: 'dog', + }, + { + label: 'Cat', + value: 'cat', + }, + { + label: 'Horse', + value: 'horse', + }, + ], + }, + key: 'animals', + type: 'select', + input: true, }, - ], - }, - ], - type: 'select', - input: true, - }, - { - type: 'button', - label: 'Submit', - key: 'submit', - disableOnInvalid: true, - input: true, - tableView: false, + { + label: 'Checkbox', + tableView: false, + key: 'checkbox', + type: 'checkbox', + input: true, + }, + { + label: 'Animals2', + widget: 'html5', + tableView: true, + data: { + values: [ + { + label: 'Dog', + value: 'dog', + }, + { + label: 'Cat', + value: 'cat', + }, + { + label: 'Horse', + value: 'horse', + }, + ], + }, + calculateValue: + 'if (data.checkbox === true) {\n value = data.animals;\n}', + key: 'animals2', + logic: [ + { + name: 'disable', + trigger: { + type: 'javascript', + javascript: 'result = row.checkbox === true;', + }, + actions: [ + { + name: 'disable', + type: 'property', + property: { + label: 'Disabled', + value: 'disabled', + type: 'boolean', + }, + state: true, + }, + ], + }, + ], + type: 'select', + input: true, + }, + { + type: 'button', + label: 'Submit', + key: 'submit', + disableOnInvalid: true, + input: true, + tableView: false, + }, + ], }, - ], - }, - ], + ], }; diff --git a/src/components/select/fixtures/comp3.js b/src/components/select/fixtures/comp3.js index dad067d2e9..a28f42af1a 100644 --- a/src/components/select/fixtures/comp3.js +++ b/src/components/select/fixtures/comp3.js @@ -1,208 +1,218 @@ export const multiSelect = { - type: 'select', - label: 'Companies', - key: 'companies', - placeholder: 'Select a company', - dataSrc: 'url', - data: { - url: 'https://example.form.io/company/submission?limit={{ limit }}&skip={{ skip }}' - }, - valueProperty: 'data.name', - searchField: 'data.name__regex', - template: '{{ item.data.name }}', - multiple: true, - input: true + type: 'select', + label: 'Companies', + key: 'companies', + placeholder: 'Select a company', + dataSrc: 'url', + data: { + url: 'https://example.form.io/company/submission?limit={{ limit }}&skip={{ skip }}', + }, + valueProperty: 'data.name', + searchField: 'data.name__regex', + template: '{{ item.data.name }}', + multiple: true, + input: true, }; -export const multiSelectOptions = [{ - '_id': '5c5f1901a590ab507db886b1', - 'owner': '553dbfc08d22d5cb1a7024f2', - 'roles': [], - '_vid': 0, - '_fvid': 0, - 'state': 'submitted', - 'data': { - 'name': 'Cheers', - 'email': 'cheers@example.com', - 'phoneNumber': '(982) 143-9839', - 'submit': true - }, - 'access': [], - 'form': '5b8c1017edc1a6c05601af8e', - 'externalIds': [], - 'created': '2019-02-09T18:16:33.636Z', - 'modified': '2019-02-09T18:16:33.637Z', - 'project': '58b78b87f5609a0070f3f456' -}, { - '_id': '5c5f18f1a590ab874fb886b0', - 'owner': '553dbfc08d22d5cb1a7024f2', - 'roles': [], - '_vid': 0, - '_fvid': 0, - 'state': 'submitted', - 'data': { - 'name': 'Cyberdyne Systems', - 'email': 'cyberdyne@example.com', - 'phoneNumber': '(982) 382-9039', - 'submit': true - }, - 'access': [], - 'form': '5b8c1017edc1a6c05601af8e', - 'externalIds': [], - 'created': '2019-02-09T18:16:17.956Z', - 'modified': '2019-02-09T18:16:17.956Z', - 'project': '58b78b87f5609a0070f3f456' -}, { - '_id': '5c5f18e1d809c48f4abd1496', - 'owner': '553dbfc08d22d5cb1a7024f2', - 'roles': [], - '_vid': 0, - '_fvid': 0, - 'state': 'submitted', - 'data': { - 'name': 'Wayne Enterprises', - 'email': 'we@example.com', - 'phoneNumber': '(982) 338-9432', - 'submit': true - }, - 'access': [], - 'form': '5b8c1017edc1a6c05601af8e', - 'externalIds': [], - 'created': '2019-02-09T18:16:01.388Z', - 'modified': '2019-02-09T18:16:01.389Z', - 'project': '58b78b87f5609a0070f3f456' -}, { - '_id': '5c5f18d1a590ab5693b886ae', - 'owner': '553dbfc08d22d5cb1a7024f2', - 'roles': [], - '_vid': 0, - '_fvid': 0, - 'state': 'submitted', - 'data': { - 'name': 'Gekko & Co', - 'email': 'gekko@example.com', - 'phoneNumber': '(982) 982-3989', - 'submit': true - }, - 'access': [], - 'form': '5b8c1017edc1a6c05601af8e', - 'externalIds': [], - 'created': '2019-02-09T18:15:45.716Z', - 'modified': '2019-02-09T18:15:45.716Z', - 'project': '58b78b87f5609a0070f3f456' -}, { - '_id': '5c5f18c4ce9978f8e6ab91fe', - 'owner': '553dbfc08d22d5cb1a7024f2', - 'roles': [], - '_vid': 0, - '_fvid': 0, - 'state': 'submitted', - 'data': { - 'name': 'Ollivander`s Wand Shop', - 'email': 'ows@example.com', - 'phoneNumber': '(987) 190-2398', - 'submit': true - }, - 'access': [], - 'form': '5b8c1017edc1a6c05601af8e', - 'externalIds': [], - 'created': '2019-02-09T18:15:32.390Z', - 'modified': '2019-02-09T18:15:32.391Z', - 'project': '58b78b87f5609a0070f3f456' -}, { - '_id': '5c5f18b5c6782092f6379b43', - 'owner': '553dbfc08d22d5cb1a7024f2', - 'roles': [], - '_vid': 0, - '_fvid': 0, - 'state': 'submitted', - 'data': { - 'name': 'Stark Industries', - 'email': 'stark@example.com', - 'phoneNumber': '(897) 239-8723', - 'submit': true - }, - 'access': [], - 'form': '5b8c1017edc1a6c05601af8e', - 'externalIds': [], - 'created': '2019-02-09T18:15:17.861Z', - 'modified': '2019-02-09T18:15:17.863Z', - 'project': '58b78b87f5609a0070f3f456' -}, { - '_id': '5c5f189ea590ab5c90b886aa', - 'owner': '553dbfc08d22d5cb1a7024f2', - 'roles': [], - '_vid': 0, - '_fvid': 0, - 'state': 'submitted', - 'data': { - 'name': 'Massive Dynamic', - 'email': 'md@example.com', - 'phoneNumber': '(872) 939-8439', - 'submit': true - }, - 'access': [], - 'form': '5b8c1017edc1a6c05601af8e', - 'externalIds': [], - 'created': '2019-02-09T18:14:54.203Z', - 'modified': '2019-02-09T18:14:54.204Z', - 'project': '58b78b87f5609a0070f3f456' -}, { - '_id': '5c5f188ece997855cfab91fb', - 'owner': '553dbfc08d22d5cb1a7024f2', - 'roles': [], - '_vid': 0, - '_fvid': 0, - 'state': 'submitted', - 'data': { - 'name': 'Vehement Capital Partners', - 'email': 'Vehement@example.com', - 'phoneNumber': '(982) 720-9289', - 'submit': true - }, - 'access': [], - 'form': '5b8c1017edc1a6c05601af8e', - 'externalIds': [], - 'created': '2019-02-09T18:14:38.059Z', - 'modified': '2019-02-09T18:14:38.060Z', - 'project': '58b78b87f5609a0070f3f456' -}, { - '_id': '5c5f187b9163aee074c7da29', - 'owner': '553dbfc08d22d5cb1a7024f2', - 'roles': [], - '_vid': 0, - '_fvid': 0, - 'state': 'submitted', - 'data': { - 'name': 'Hooli', - 'email': 'Hooli@example.com', - 'phoneNumber': '(987) 239-8239', - 'submit': true - }, - 'access': [], - 'form': '5b8c1017edc1a6c05601af8e', - 'externalIds': [], - 'created': '2019-02-09T18:14:19.714Z', - 'modified': '2019-02-09T18:14:19.714Z', - 'project': '58b78b87f5609a0070f3f456' -}, { - '_id': '5c5f186dce9978dc6cab91f8', - 'owner': '553dbfc08d22d5cb1a7024f2', - 'roles': [], - '_vid': 0, - '_fvid': 0, - 'state': 'submitted', - 'data': { - 'name': 'Umbrella Corporation', - 'email': 'umbrella@example.com', - 'phoneNumber': '(982) 782-3762', - 'submit': true - }, - 'access': [], - 'form': '5b8c1017edc1a6c05601af8e', - 'externalIds': [], - 'created': '2019-02-09T18:14:05.353Z', - 'modified': '2019-02-09T18:14:05.354Z', - 'project': '58b78b87f5609a0070f3f456' -}]; - +export const multiSelectOptions = [ + { + _id: '5c5f1901a590ab507db886b1', + owner: '553dbfc08d22d5cb1a7024f2', + roles: [], + _vid: 0, + _fvid: 0, + state: 'submitted', + data: { + name: 'Cheers', + email: 'cheers@example.com', + phoneNumber: '(982) 143-9839', + submit: true, + }, + access: [], + form: '5b8c1017edc1a6c05601af8e', + externalIds: [], + created: '2019-02-09T18:16:33.636Z', + modified: '2019-02-09T18:16:33.637Z', + project: '58b78b87f5609a0070f3f456', + }, + { + _id: '5c5f18f1a590ab874fb886b0', + owner: '553dbfc08d22d5cb1a7024f2', + roles: [], + _vid: 0, + _fvid: 0, + state: 'submitted', + data: { + name: 'Cyberdyne Systems', + email: 'cyberdyne@example.com', + phoneNumber: '(982) 382-9039', + submit: true, + }, + access: [], + form: '5b8c1017edc1a6c05601af8e', + externalIds: [], + created: '2019-02-09T18:16:17.956Z', + modified: '2019-02-09T18:16:17.956Z', + project: '58b78b87f5609a0070f3f456', + }, + { + _id: '5c5f18e1d809c48f4abd1496', + owner: '553dbfc08d22d5cb1a7024f2', + roles: [], + _vid: 0, + _fvid: 0, + state: 'submitted', + data: { + name: 'Wayne Enterprises', + email: 'we@example.com', + phoneNumber: '(982) 338-9432', + submit: true, + }, + access: [], + form: '5b8c1017edc1a6c05601af8e', + externalIds: [], + created: '2019-02-09T18:16:01.388Z', + modified: '2019-02-09T18:16:01.389Z', + project: '58b78b87f5609a0070f3f456', + }, + { + _id: '5c5f18d1a590ab5693b886ae', + owner: '553dbfc08d22d5cb1a7024f2', + roles: [], + _vid: 0, + _fvid: 0, + state: 'submitted', + data: { + name: 'Gekko & Co', + email: 'gekko@example.com', + phoneNumber: '(982) 982-3989', + submit: true, + }, + access: [], + form: '5b8c1017edc1a6c05601af8e', + externalIds: [], + created: '2019-02-09T18:15:45.716Z', + modified: '2019-02-09T18:15:45.716Z', + project: '58b78b87f5609a0070f3f456', + }, + { + _id: '5c5f18c4ce9978f8e6ab91fe', + owner: '553dbfc08d22d5cb1a7024f2', + roles: [], + _vid: 0, + _fvid: 0, + state: 'submitted', + data: { + name: 'Ollivander`s Wand Shop', + email: 'ows@example.com', + phoneNumber: '(987) 190-2398', + submit: true, + }, + access: [], + form: '5b8c1017edc1a6c05601af8e', + externalIds: [], + created: '2019-02-09T18:15:32.390Z', + modified: '2019-02-09T18:15:32.391Z', + project: '58b78b87f5609a0070f3f456', + }, + { + _id: '5c5f18b5c6782092f6379b43', + owner: '553dbfc08d22d5cb1a7024f2', + roles: [], + _vid: 0, + _fvid: 0, + state: 'submitted', + data: { + name: 'Stark Industries', + email: 'stark@example.com', + phoneNumber: '(897) 239-8723', + submit: true, + }, + access: [], + form: '5b8c1017edc1a6c05601af8e', + externalIds: [], + created: '2019-02-09T18:15:17.861Z', + modified: '2019-02-09T18:15:17.863Z', + project: '58b78b87f5609a0070f3f456', + }, + { + _id: '5c5f189ea590ab5c90b886aa', + owner: '553dbfc08d22d5cb1a7024f2', + roles: [], + _vid: 0, + _fvid: 0, + state: 'submitted', + data: { + name: 'Massive Dynamic', + email: 'md@example.com', + phoneNumber: '(872) 939-8439', + submit: true, + }, + access: [], + form: '5b8c1017edc1a6c05601af8e', + externalIds: [], + created: '2019-02-09T18:14:54.203Z', + modified: '2019-02-09T18:14:54.204Z', + project: '58b78b87f5609a0070f3f456', + }, + { + _id: '5c5f188ece997855cfab91fb', + owner: '553dbfc08d22d5cb1a7024f2', + roles: [], + _vid: 0, + _fvid: 0, + state: 'submitted', + data: { + name: 'Vehement Capital Partners', + email: 'Vehement@example.com', + phoneNumber: '(982) 720-9289', + submit: true, + }, + access: [], + form: '5b8c1017edc1a6c05601af8e', + externalIds: [], + created: '2019-02-09T18:14:38.059Z', + modified: '2019-02-09T18:14:38.060Z', + project: '58b78b87f5609a0070f3f456', + }, + { + _id: '5c5f187b9163aee074c7da29', + owner: '553dbfc08d22d5cb1a7024f2', + roles: [], + _vid: 0, + _fvid: 0, + state: 'submitted', + data: { + name: 'Hooli', + email: 'Hooli@example.com', + phoneNumber: '(987) 239-8239', + submit: true, + }, + access: [], + form: '5b8c1017edc1a6c05601af8e', + externalIds: [], + created: '2019-02-09T18:14:19.714Z', + modified: '2019-02-09T18:14:19.714Z', + project: '58b78b87f5609a0070f3f456', + }, + { + _id: '5c5f186dce9978dc6cab91f8', + owner: '553dbfc08d22d5cb1a7024f2', + roles: [], + _vid: 0, + _fvid: 0, + state: 'submitted', + data: { + name: 'Umbrella Corporation', + email: 'umbrella@example.com', + phoneNumber: '(982) 782-3762', + submit: true, + }, + access: [], + form: '5b8c1017edc1a6c05601af8e', + externalIds: [], + created: '2019-02-09T18:14:05.353Z', + modified: '2019-02-09T18:14:05.354Z', + project: '58b78b87f5609a0070f3f456', + }, +]; diff --git a/src/components/select/fixtures/comp4.js b/src/components/select/fixtures/comp4.js index bc04d0e4f3..bedc9fb8b4 100644 --- a/src/components/select/fixtures/comp4.js +++ b/src/components/select/fixtures/comp4.js @@ -1,104 +1,109 @@ export default { - 'label': 'Select', - 'labelPosition': 'top', - 'widget': 'choicesjs', - 'tableView': true, - 'modalEdit': false, - 'multiple': false, - 'dataSrc': 'values', - 'data': { - 'values': [{ - 'label': 'true', - 'value': 'true' - }, { - 'label': '1.2', - 'value': '12' - }, { - 'label': '11', - 'value': '11' - }, { - 'label': 'test', - 'value': 'test' - }], - 'resource': '', - 'json': '', - 'url': '', - 'custom': '' + label: 'Select', + labelPosition: 'top', + widget: 'choicesjs', + tableView: true, + modalEdit: false, + multiple: false, + dataSrc: 'values', + data: { + values: [ + { + label: 'true', + value: 'true', + }, + { + label: '1.2', + value: '12', + }, + { + label: '11', + value: '11', + }, + { + label: 'test', + value: 'test', + }, + ], + resource: '', + json: '', + url: '', + custom: '', }, - 'valueProperty': '', - 'dataType': 'auto', - 'template': '{{ item.label }}', - 'searchEnabled': true, - 'selectThreshold': 0.3, - 'readOnlyValue': false, - 'customOptions': {}, - 'persistent': true, - 'protected': false, - 'dbIndex': false, - 'encrypted': false, - 'clearOnHide': true, - 'customDefaultValue': '', - 'calculateValue': '', - 'calculateServer': false, - 'allowCalculateOverride': false, - 'validateOn': 'change', - 'validate': { - 'required': false, - 'customMessage': '', - 'custom': '', - 'customPrivate': false, - 'json': '', - 'strictDateValidation': false, - 'multiple': false, - 'unique': false + valueProperty: '', + dataType: 'auto', + template: '{{ item.label }}', + searchEnabled: true, + selectThreshold: 0.3, + readOnlyValue: false, + customOptions: {}, + persistent: true, + protected: false, + dbIndex: false, + encrypted: false, + clearOnHide: true, + customDefaultValue: '', + calculateValue: '', + calculateServer: false, + allowCalculateOverride: false, + validateOn: 'change', + validate: { + required: false, + customMessage: '', + custom: '', + customPrivate: false, + json: '', + strictDateValidation: false, + multiple: false, + unique: false, }, - 'unique': false, - 'errorLabel': '', - 'key': 'select1', - 'tags': [], - 'properties': {}, - 'conditional': { - 'show': null, - 'when': null, - 'eq': '', - 'json': '' + unique: false, + errorLabel: '', + key: 'select1', + tags: [], + properties: {}, + conditional: { + show: null, + when: null, + eq: '', + json: '', }, - 'customConditional': '', - 'logic': [], - 'attributes': {}, - 'overlay': { - 'style': '', - 'page': '', - 'left': '', - 'top': '', - 'width': '', - 'height': '' + customConditional: '', + logic: [], + attributes: {}, + overlay: { + style: '', + page: '', + left: '', + top: '', + width: '', + height: '', }, - 'type': 'select', - 'indexeddb': { - 'filter': {} + type: 'select', + indexeddb: { + filter: {}, }, - 'selectFields': '', - 'searchField': '', - 'minSearch': 0, - 'filter': '', - 'limit': 100, - 'refreshOn': '', - 'redrawOn': '', - 'input': true, - 'prefix': '', - 'suffix': '', - 'showCharCount': false, - 'showWordCount': false, - 'allowMultipleMasks': false, - 'clearOnRefresh': false, - 'lazyLoad': true, - 'authenticate': false, - 'searchThreshold': 0.3, - 'fuseOptions': { - 'include': 'score', - 'threshold': 0.3 + selectFields: '', + searchField: '', + minSearch: 0, + filter: '', + limit: 100, + refreshOn: '', + redrawOn: '', + input: true, + prefix: '', + suffix: '', + showCharCount: false, + showWordCount: false, + allowMultipleMasks: false, + clearOnRefresh: false, + lazyLoad: true, + authenticate: false, + searchThreshold: 0.3, + fuseOptions: { + include: 'score', + threshold: 0.3, }, - 'id': 'ef6o05a', - 'defaultValue': '' - }; + id: 'ef6o05a', + defaultValue: '', +}; diff --git a/src/components/select/fixtures/comp5.js b/src/components/select/fixtures/comp5.js index 8739831815..b5c29de9e1 100644 --- a/src/components/select/fixtures/comp5.js +++ b/src/components/select/fixtures/comp5.js @@ -1,26 +1,30 @@ export default { - 'label': 'Select', - 'widget': 'choicesjs', - 'tableView': true, - 'data': { - 'values': [{ - 'label': '111', - 'value': '111' - }, { - 'label': '222', - 'value': '222' - }, { - 'label': '333', - 'value': '333' - }] - }, - 'template': '', - 'selectThreshold': 0.3, - 'calculateServer': false, - 'key': 'select2', - 'type': 'select', - 'indexeddb': { - 'filter': {} - }, - 'input': true + label: 'Select', + widget: 'choicesjs', + tableView: true, + data: { + values: [ + { + label: '111', + value: '111', + }, + { + label: '222', + value: '222', + }, + { + label: '333', + value: '333', + }, + ], + }, + template: '', + selectThreshold: 0.3, + calculateServer: false, + key: 'select2', + type: 'select', + indexeddb: { + filter: {}, + }, + input: true, }; diff --git a/src/components/select/fixtures/comp6.js b/src/components/select/fixtures/comp6.js index 436e61dbf3..03d0457d69 100644 --- a/src/components/select/fixtures/comp6.js +++ b/src/components/select/fixtures/comp6.js @@ -1,28 +1,33 @@ export default { - 'label': 'Select', - 'widget': 'choicesjs', - 'tableView': true, - 'dataSrc': 'json', - 'data': { - 'json': [{ - 'value': 'a', - 'label': 'A' - }, { - 'value': 'b', - 'label': 'B' - }, { - 'value': 'c', - 'label': 'C' - }, { - 'value': 'd', - 'label': 'D' - }] - }, - 'selectThreshold': 0.3, - 'key': 'select', - 'type': 'select', - 'indexeddb': { - 'filter': {} - }, - 'input': true + label: 'Select', + widget: 'choicesjs', + tableView: true, + dataSrc: 'json', + data: { + json: [ + { + value: 'a', + label: 'A', + }, + { + value: 'b', + label: 'B', + }, + { + value: 'c', + label: 'C', + }, + { + value: 'd', + label: 'D', + }, + ], + }, + selectThreshold: 0.3, + key: 'select', + type: 'select', + indexeddb: { + filter: {}, + }, + input: true, }; diff --git a/src/components/select/fixtures/comp7.js b/src/components/select/fixtures/comp7.js index 2ffb54c6d4..73d7d635af 100644 --- a/src/components/select/fixtures/comp7.js +++ b/src/components/select/fixtures/comp7.js @@ -1,44 +1,52 @@ export default { - type: 'form', - components: [ - { - label: 'Select Choices', - tableView: true, - data: { - values: [{ label: 'a', value: 'a' }, { label: 'b', value: 'b' }, { label: 'c', value: 'c' }] - }, - selectThreshold: 0.3, - validate: { onlyAvailableItems: false }, - key: 'selectChoices', - type: 'select', - indexeddb: { filter: {} }, - input: true - }, - { - label: 'Select HTML', - widget: 'html5', - tableView: true, - data: { - values: [{ label: 'a', value: 'a' }, { label: 'b', value: 'b' }, { label: 'c', value: 'c' }] - }, - selectThreshold: 0.3, - validate: { onlyAvailableItems: false }, - key: 'selectHtml', - type: 'select', - indexeddb: { filter: {} }, - input: true - }, - { - type: 'button', - label: 'Submit', - key: 'submit', - disableOnInvalid: true, - input: true, - tableView: false - } - ], - title: 'test checkbox', - display: 'form', - name: 'testCheckbox', - path: 'testcheckbox', + type: 'form', + components: [ + { + label: 'Select Choices', + tableView: true, + data: { + values: [ + { label: 'a', value: 'a' }, + { label: 'b', value: 'b' }, + { label: 'c', value: 'c' }, + ], + }, + selectThreshold: 0.3, + validate: { onlyAvailableItems: false }, + key: 'selectChoices', + type: 'select', + indexeddb: { filter: {} }, + input: true, + }, + { + label: 'Select HTML', + widget: 'html5', + tableView: true, + data: { + values: [ + { label: 'a', value: 'a' }, + { label: 'b', value: 'b' }, + { label: 'c', value: 'c' }, + ], + }, + selectThreshold: 0.3, + validate: { onlyAvailableItems: false }, + key: 'selectHtml', + type: 'select', + indexeddb: { filter: {} }, + input: true, + }, + { + type: 'button', + label: 'Submit', + key: 'submit', + disableOnInvalid: true, + input: true, + tableView: false, + }, + ], + title: 'test checkbox', + display: 'form', + name: 'testCheckbox', + path: 'testcheckbox', }; diff --git a/src/components/select/fixtures/comp8.js b/src/components/select/fixtures/comp8.js index a07ad0f100..86d2b48424 100644 --- a/src/components/select/fixtures/comp8.js +++ b/src/components/select/fixtures/comp8.js @@ -1,35 +1,48 @@ export default { - type: 'form', - components: [ - { - label: 'Number', - mask: false, - spellcheck: true, - tableView: false, - delimiter: false, - requireDecimal: false, - inputFormat: 'plain', - key: 'number', - type: 'number', - input: true - }, - { - label: 'Select', - tableView: true, - data: { values: [{ label: 'a', value: 'a' }, { label: 'b', value: 'b' }, { label: 'c', value: 'c' }] }, - refreshOn: 'number', - clearOnRefresh: true, - selectThreshold: 0.3, - validate: { onlyAvailableItems: false }, - key: 'select', - type: 'select', - indexeddb: { filter: {} }, - input: true - }, - { type: 'button', label: 'Submit', key: 'submit', disableOnInvalid: true, input: true, tableView: false } - ], - title: 'test', - display: 'form', - name: 'test', - path: 'test', + type: 'form', + components: [ + { + label: 'Number', + mask: false, + spellcheck: true, + tableView: false, + delimiter: false, + requireDecimal: false, + inputFormat: 'plain', + key: 'number', + type: 'number', + input: true, + }, + { + label: 'Select', + tableView: true, + data: { + values: [ + { label: 'a', value: 'a' }, + { label: 'b', value: 'b' }, + { label: 'c', value: 'c' }, + ], + }, + refreshOn: 'number', + clearOnRefresh: true, + selectThreshold: 0.3, + validate: { onlyAvailableItems: false }, + key: 'select', + type: 'select', + indexeddb: { filter: {} }, + input: true, + }, + { + type: 'button', + label: 'Submit', + key: 'submit', + disableOnInvalid: true, + input: true, + tableView: false, + }, + ], + title: 'test', + display: 'form', + name: 'test', + path: 'test', }; diff --git a/src/components/select/fixtures/comp9.js b/src/components/select/fixtures/comp9.js index 5e667dbcd7..4eabe1dd3a 100644 --- a/src/components/select/fixtures/comp9.js +++ b/src/components/select/fixtures/comp9.js @@ -1,65 +1,65 @@ export default { - type: 'form', - components: [ - { - label: 'Number', - mask: false, - spellcheck: true, - tableView: false, - delimiter: false, - requireDecimal: false, - inputFormat: 'plain', - key: 'number', - type: 'number', - input: true - }, - { - label: 'Select', - tableView: true, - dataSrc: 'url', - data: { - values: [ - { - label: '', - value: '' - } - ], - url: 'https://test.com/{{ data.number }}', - headers: [ - { - key: '', - value: '' - } - ] - }, - template: '{{ item.name }}', - refreshOn: 'number', - lazyLoad: false, - selectThreshold: 0.3, - validate: { - onlyAvailableItems: false - }, - key: 'select', - type: 'select', - indexeddb: { - filter: {} - }, - input: true, - disableLimit: true - }, - { - type: 'button', - label: 'Submit', - key: 'submit', - disableOnInvalid: true, - input: true, - tableView: false - } - ], - title: 'test checkbox', - display: 'form', - name: 'testCheckbox', - path: 'testcheckbox', - project: '5ebcf8938bdebc4c58a949a3', - machineName: 'cjksbatcpbhyfbs:testCheckbox' + type: 'form', + components: [ + { + label: 'Number', + mask: false, + spellcheck: true, + tableView: false, + delimiter: false, + requireDecimal: false, + inputFormat: 'plain', + key: 'number', + type: 'number', + input: true, + }, + { + label: 'Select', + tableView: true, + dataSrc: 'url', + data: { + values: [ + { + label: '', + value: '', + }, + ], + url: 'https://test.com/{{ data.number }}', + headers: [ + { + key: '', + value: '', + }, + ], + }, + template: '{{ item.name }}', + refreshOn: 'number', + lazyLoad: false, + selectThreshold: 0.3, + validate: { + onlyAvailableItems: false, + }, + key: 'select', + type: 'select', + indexeddb: { + filter: {}, + }, + input: true, + disableLimit: true, + }, + { + type: 'button', + label: 'Submit', + key: 'submit', + disableOnInvalid: true, + input: true, + tableView: false, + }, + ], + title: 'test checkbox', + display: 'form', + name: 'testCheckbox', + path: 'testcheckbox', + project: '5ebcf8938bdebc4c58a949a3', + machineName: 'cjksbatcpbhyfbs:testCheckbox', }; diff --git a/src/components/select/fixtures/index.js b/src/components/select/fixtures/index.js index 7e0e7d9307..cc0bca3400 100644 --- a/src/components/select/fixtures/index.js +++ b/src/components/select/fixtures/index.js @@ -19,4 +19,25 @@ import comp18 from './comp18'; import comp19 from './comp19'; import comp20 from './comp20'; import comp21 from './comp21'; -export { comp1, comp2, comp4, comp5, comp6, comp7, comp8, comp9, comp10, comp11, comp12, comp13, comp14, comp15, comp16, comp17, comp18, comp19, comp20, comp21 }; +export { + comp1, + comp2, + comp4, + comp5, + comp6, + comp7, + comp8, + comp9, + comp10, + comp11, + comp12, + comp13, + comp14, + comp15, + comp16, + comp17, + comp18, + comp19, + comp20, + comp21, +}; diff --git a/src/components/select/fixtures/values.js b/src/components/select/fixtures/values.js index c7e1e6576f..4289713807 100644 --- a/src/components/select/fixtures/values.js +++ b/src/components/select/fixtures/values.js @@ -1,5 +1 @@ -export default [ - 'one', - 'two', - false, -]; +export default ['one', 'two', false]; diff --git a/src/components/selectboxes/SelectBoxes.form.js b/src/components/selectboxes/SelectBoxes.form.js index 7d7300fc56..31765fdea7 100644 --- a/src/components/selectboxes/SelectBoxes.form.js +++ b/src/components/selectboxes/SelectBoxes.form.js @@ -1,20 +1,23 @@ import radioEditForm from '../radio/Radio.form'; import SelectBoxesEditValidation from './editForm/SelectBoxes.edit.validation'; -export default function(...extend) { - return radioEditForm([ - { - key: 'data', - components: [ - { - key: 'dataType', - ignore: true, - } - ] - }, - { - key: 'validation', - components: SelectBoxesEditValidation - }, - ], ...extend); +export default function (...extend) { + return radioEditForm( + [ + { + key: 'data', + components: [ + { + key: 'dataType', + ignore: true, + }, + ], + }, + { + key: 'validation', + components: SelectBoxesEditValidation, + }, + ], + ...extend, + ); } diff --git a/src/components/selectboxes/SelectBoxes.js b/src/components/selectboxes/SelectBoxes.js index acd827b9a8..64d1d747a2 100644 --- a/src/components/selectboxes/SelectBoxes.js +++ b/src/components/selectboxes/SelectBoxes.js @@ -1,298 +1,335 @@ import _ from 'lodash'; -import { componentValueTypes, getComponentSavedTypes, boolValue } from '../../utils/utils'; +import { + componentValueTypes, + getComponentSavedTypes, + boolValue, +} from '../../utils/utils'; import RadioComponent from '../radio/Radio'; export default class SelectBoxesComponent extends RadioComponent { - static schema(...extend) { - return RadioComponent.schema({ - type: 'selectboxes', - label: 'Select Boxes', - key: 'selectBoxes', - inline: false - }, ...extend); - } - - static get builderInfo() { - return { - title: 'Select Boxes', - group: 'basic', - icon: 'plus-square', - weight: 60, - documentation: '/userguide/form-building/form-components#select-box', - schema: SelectBoxesComponent.schema() - }; - } - - static get serverConditionSettings() { - return SelectBoxesComponent.conditionOperatorsSettings; - } - - static get conditionOperatorsSettings() { - return { - ...super.conditionOperatorsSettings, - valueComponent(classComp) { + static schema(...extend) { + return RadioComponent.schema( + { + type: 'selectboxes', + label: 'Select Boxes', + key: 'selectBoxes', + inline: false, + }, + ...extend, + ); + } + + static get builderInfo() { return { - type: 'select', - dataSrc: 'custom', - valueProperty: 'value', - dataType: 'string', - data: { - custom: `values = ${classComp && classComp.values ? JSON.stringify(classComp.values) : []}` - }, + title: 'Select Boxes', + group: 'basic', + icon: 'plus-square', + weight: 60, + documentation: + '/userguide/form-building/form-components#select-box', + schema: SelectBoxesComponent.schema(), }; - } - }; - } - - static savedValueTypes(schema) { - return getComponentSavedTypes(schema) || [componentValueTypes.object]; - } - - constructor(...args) { - super(...args); - this.validators = this.validators.concat('minSelectedCount', 'maxSelectedCount', 'availableValueProperty'); - } - - init() { - super.init(); - this.component.inputType = 'checkbox'; - } - - get defaultSchema() { - return SelectBoxesComponent.schema(); - } - - get inputInfo() { - const info = super.elementInfo(); - info.attr.name += '[]'; - info.attr.type = 'checkbox'; - info.attr.class = 'form-check-input'; - return info; - } - - get emptyValue() { - return this.component.values.reduce((prev, value) => { - if (value.value) { - prev[value.value] = false; - } - return prev; - }, {}); - } - - get defaultValue() { - let defaultValue = this.emptyValue; - - if (!_.isEmpty(this.component.defaultValue)) { - defaultValue = this.component.defaultValue; } - if (this.component.customDefaultValue && !this.options.preview) { - defaultValue = this.evaluate( - this.component.customDefaultValue, - { value: '' }, - 'value' - ); + + static get serverConditionSettings() { + return SelectBoxesComponent.conditionOperatorsSettings; } - return defaultValue; - } - - /** - * Only empty if the values are all false. - * - * @param value - * @return {boolean} - */ - isEmpty(value = this.dataValue) { - let empty = true; - for (const key in value) { - if (Object.prototype.hasOwnProperty.call(value, key) && value[key]) { - empty = false; - break; - } + static get conditionOperatorsSettings() { + return { + ...super.conditionOperatorsSettings, + valueComponent(classComp) { + return { + type: 'select', + dataSrc: 'custom', + valueProperty: 'value', + dataType: 'string', + data: { + custom: `values = ${ + classComp && classComp.values + ? JSON.stringify(classComp.values) + : [] + }`, + }, + }; + }, + }; } - return empty; - } + static savedValueTypes(schema) { + return getComponentSavedTypes(schema) || [componentValueTypes.object]; + } - getValue() { - if (this.viewOnly || !this.refs.input || !this.refs.input.length) { - return this.dataValue; + constructor(...args) { + super(...args); + this.validators = this.validators.concat( + 'minSelectedCount', + 'maxSelectedCount', + 'availableValueProperty', + ); } - const value = {}; - _.each(this.refs.input, (input) => { - value[input.value] = !!input.checked; - }); - return value; - } - - /** - * Normalize values coming into updateValue. - * - * @param value - * @return {*} - */ - normalizeValue(value) { - value = value || {}; - if (typeof value !== 'object') { - if (typeof value === 'string') { - value = { - [value]: true - }; - } - else { - value = {}; - } + + init() { + super.init(); + this.component.inputType = 'checkbox'; } - if (Array.isArray(value)) { - _.each(value, (val) => { - value[val] = true; - }); + + get defaultSchema() { + return SelectBoxesComponent.schema(); } - const checkedValues = _.keys(_.pickBy(value, (val) => val)); - if (this.isSelectURL && this.templateData && _.every(checkedValues, (val) => this.templateData[val])) { - const submission = this.root.submission; - if (!submission.metadata.selectData) { - submission.metadata.selectData = {}; - } - const selectData = []; - checkedValues.forEach((value) => selectData.push(this.templateData[value])); - _.set(submission.metadata.selectData, this.path, selectData); + get inputInfo() { + const info = super.elementInfo(); + info.attr.name += '[]'; + info.attr.type = 'checkbox'; + info.attr.class = 'form-check-input'; + return info; } - return value; - } - - /** - * Set the value of this component. - * - * @param value - * @param flags - */ - setValue(value, flags = {}) { - const changed = this.updateValue(value, flags); - value = this.dataValue; - - if (this.isHtmlRenderMode()) { - if (changed) { - this.redraw(); - } + get emptyValue() { + return this.component.values.reduce((prev, value) => { + if (value.value) { + prev[value.value] = false; + } + return prev; + }, {}); } - else { - _.each(this.refs.input, (input) => { - if (_.isUndefined(value[input.value])) { - value[input.value] = false; + + get defaultValue() { + let defaultValue = this.emptyValue; + + if (!_.isEmpty(this.component.defaultValue)) { + defaultValue = this.component.defaultValue; } - input.checked = !!value[input.value]; - }); + if (this.component.customDefaultValue && !this.options.preview) { + defaultValue = this.evaluate( + this.component.customDefaultValue, + { value: '' }, + 'value', + ); + } + + return defaultValue; } - return changed; - } + /** + * Only empty if the values are all false. + * + * @param value + * @return {boolean} + */ + isEmpty(value = this.dataValue) { + let empty = true; + for (const key in value) { + if ( + Object.prototype.hasOwnProperty.call(value, key) && + value[key] + ) { + empty = false; + break; + } + } - getValueAsString(value) { - if (!value) { - return ''; + return empty; } - if (this.isSelectURL) { - return _(value).pickBy((val) => val).keys().join(', '); + getValue() { + if (this.viewOnly || !this.refs.input || !this.refs.input.length) { + return this.dataValue; + } + const value = {}; + _.each(this.refs.input, (input) => { + value[input.value] = !!input.checked; + }); + return value; } - return _(this.component.values || []) - .filter((v) => value[v.value]) - .map('label') - .join(', '); - } - - setSelectedClasses() { - if (this.refs.wrapper) { - //add/remove selected option class - const value = this.dataValue; - const valuesKeys = Object.keys(value); - - this.refs.wrapper.forEach((wrapper, index) => { - let key = valuesKeys[index]; - const input = this.refs.input[index]; - if (input?.value.toString() !== key) { - key = valuesKeys.find((k) => input?.value.toString() === k); + + /** + * Normalize values coming into updateValue. + * + * @param value + * @return {*} + */ + normalizeValue(value) { + value = value || {}; + if (typeof value !== 'object') { + if (typeof value === 'string') { + value = { + [value]: true, + }; + } else { + value = {}; + } } - const isChecked = value[key]; - if ((isChecked && key) || (this.isSelectURL && !this.shouldLoad && this.listData && _.findIndex(this.selectData, this.listData[index]) !== -1)) { - //add class to container when selected - this.addClass(wrapper, this.optionSelectedClass); - //change "checked" attribute - input.setAttribute('checked', 'true'); + if (Array.isArray(value)) { + _.each(value, (val) => { + value[val] = true; + }); } - else if (!isChecked && key) { - this.removeClass(wrapper, this.optionSelectedClass); - input.removeAttribute('checked'); + + const checkedValues = _.keys(_.pickBy(value, (val) => val)); + if ( + this.isSelectURL && + this.templateData && + _.every(checkedValues, (val) => this.templateData[val]) + ) { + const submission = this.root.submission; + if (!submission.metadata.selectData) { + submission.metadata.selectData = {}; + } + const selectData = []; + checkedValues.forEach((value) => + selectData.push(this.templateData[value]), + ); + _.set(submission.metadata.selectData, this.path, selectData); } - }); + + return value; } - } - setInputsDisabled(value, onlyUnchecked) { - if (this.refs.input) { - this.refs.input.forEach(item => { - if (onlyUnchecked && !item.checked || !onlyUnchecked) { - item.disabled = value; + /** + * Set the value of this component. + * + * @param value + * @param flags + */ + setValue(value, flags = {}) { + const changed = this.updateValue(value, flags); + value = this.dataValue; + + if (this.isHtmlRenderMode()) { + if (changed) { + this.redraw(); + } + } else { + _.each(this.refs.input, (input) => { + if (_.isUndefined(value[input.value])) { + value[input.value] = false; + } + input.checked = !!value[input.value]; + }); } - }); + + return changed; } - } - - checkComponentValidity(data, dirty, rowData, options) { - const minCount = this.component.validate.minSelectedCount; - const maxCount = this.component.validate.maxSelectedCount; - if (!this.shouldSkipValidation(data, dirty, rowData)) { - const isValid = this.isValid(data, dirty); - if ((maxCount || minCount)) { - const count = Object.keys(this.validationValue).reduce((total, key) => { - if (this.validationValue[key]) { - total++; - } - return total; - }, 0); - - // Disable the rest of inputs if the max amount is already checked - if (maxCount && count >= maxCount) { - this.setInputsDisabled(true, true); + + getValueAsString(value) { + if (!value) { + return ''; } - else if (maxCount && !this.shouldDisabled) { - this.setInputsDisabled(false); + + if (this.isSelectURL) { + return _(value) + .pickBy((val) => val) + .keys() + .join(', '); } + return _(this.component.values || []) + .filter((v) => value[v.value]) + .map('label') + .join(', '); + } - if (!isValid && maxCount && count > maxCount) { - const message = this.t( - this.component.maxSelectedCountMessage || 'You can only select up to {{maxCount}} items.', - { maxCount } - ); - this.setCustomValidity(message, dirty); - return false; + setSelectedClasses() { + if (this.refs.wrapper) { + //add/remove selected option class + const value = this.dataValue; + const valuesKeys = Object.keys(value); + + this.refs.wrapper.forEach((wrapper, index) => { + let key = valuesKeys[index]; + const input = this.refs.input[index]; + if (input?.value.toString() !== key) { + key = valuesKeys.find((k) => input?.value.toString() === k); + } + const isChecked = value[key]; + if ( + (isChecked && key) || + (this.isSelectURL && + !this.shouldLoad && + this.listData && + _.findIndex(this.selectData, this.listData[index]) !== + -1) + ) { + //add class to container when selected + this.addClass(wrapper, this.optionSelectedClass); + //change "checked" attribute + input.setAttribute('checked', 'true'); + } else if (!isChecked && key) { + this.removeClass(wrapper, this.optionSelectedClass); + input.removeAttribute('checked'); + } + }); } - else if (!isValid && minCount && count < minCount) { - this.setInputsDisabled(false); - const message = this.t( - this.component.minSelectedCountMessage || 'You must select at least {{minCount}} items.', - { minCount } - ); - this.setCustomValidity(message, dirty); - return false; + } + + setInputsDisabled(value, onlyUnchecked) { + if (this.refs.input) { + this.refs.input.forEach((item) => { + if ((onlyUnchecked && !item.checked) || !onlyUnchecked) { + item.disabled = value; + } + }); } - } } - return super.checkComponentValidity(data, dirty, rowData, options); - } - validateValueAvailability(setting, value) { - if (!boolValue(setting) || !value) { - return true; + checkComponentValidity(data, dirty, rowData, options) { + const minCount = this.component.validate.minSelectedCount; + const maxCount = this.component.validate.maxSelectedCount; + if (!this.shouldSkipValidation(data, dirty, rowData)) { + const isValid = this.isValid(data, dirty); + if (maxCount || minCount) { + const count = Object.keys(this.validationValue).reduce( + (total, key) => { + if (this.validationValue[key]) { + total++; + } + return total; + }, + 0, + ); + + // Disable the rest of inputs if the max amount is already checked + if (maxCount && count >= maxCount) { + this.setInputsDisabled(true, true); + } else if (maxCount && !this.shouldDisabled) { + this.setInputsDisabled(false); + } + + if (!isValid && maxCount && count > maxCount) { + const message = this.t( + this.component.maxSelectedCountMessage || + 'You can only select up to {{maxCount}} items.', + { maxCount }, + ); + this.setCustomValidity(message, dirty); + return false; + } else if (!isValid && minCount && count < minCount) { + this.setInputsDisabled(false); + const message = this.t( + this.component.minSelectedCountMessage || + 'You must select at least {{minCount}} items.', + { minCount }, + ); + this.setCustomValidity(message, dirty); + return false; + } + } + } + return super.checkComponentValidity(data, dirty, rowData, options); } - const values = this.component.values; - const availableValueKeys = (values || []).map(({ value: optionValue }) => optionValue); - const valueKeys = Object.keys(value); + validateValueAvailability(setting, value) { + if (!boolValue(setting) || !value) { + return true; + } + + const values = this.component.values; + const availableValueKeys = (values || []).map( + ({ value: optionValue }) => optionValue, + ); + const valueKeys = Object.keys(value); - return valueKeys.every((key) => availableValueKeys.includes(key)); - } + return valueKeys.every((key) => availableValueKeys.includes(key)); + } } diff --git a/src/components/selectboxes/SelectBoxes.unit.js b/src/components/selectboxes/SelectBoxes.unit.js index 316ba2df6d..833e802041 100644 --- a/src/components/selectboxes/SelectBoxes.unit.js +++ b/src/components/selectboxes/SelectBoxes.unit.js @@ -4,373 +4,432 @@ import _ from 'lodash'; import SelectBoxesComponent from './SelectBoxes'; import { Formio } from './../../Formio'; -import { - comp1, - comp3, - comp4, - comp5, - comp6, - comp7, -} from './fixtures'; +import { comp1, comp3, comp4, comp5, comp6, comp7 } from './fixtures'; import wizardWithSelectBoxes from '../../../test/forms/wizardWithSelectBoxes'; -describe('SelectBoxes Component', function() { - it('Should build a SelectBoxes component', function() { - return Harness.testCreate(SelectBoxesComponent, comp1).then((component) => { - Harness.testElements(component, 'input[type="checkbox"]', 8); - }); - }); - - it('Should build a SelectBoxes component with URL DataSrc', function(done) { - const form = _.cloneDeep(comp5); - const element = document.createElement('div'); - const originalMakeRequest = Formio.makeRequest; - - Formio.makeRequest = function() { - return new Promise(resolve => { - const values = [ - { name : 'Alabama', abbreviation : 'AL' }, - { name : 'Alaska', abbreviation: 'AK' }, - { name: 'American Samoa', abbreviation: 'AS' } - ]; - resolve(values); - }); - }; - - Formio.createForm(element, form).then(form => { - const selectBoxes = form.getComponent('selectBoxes'); - - setTimeout(()=>{ - assert.equal(selectBoxes.loadedOptions.length, 3); - - Formio.makeRequest = originalMakeRequest; - done(); - }, 200); - }).catch(done); - }); - - it('Should display values in DataTab', function(done) { - Harness.testCreate(SelectBoxesComponent, comp6).then((component) => { - const value1 = component.getView({ Alabama: false, Alaska: true }); - const value2 = component.getView({ Alabama: true, Alaska: true }); - assert.equal(value1, 'Alaska'); - assert.equal(value2, 'Alabama, Alaska'); - done(); +describe('SelectBoxes Component', function () { + it('Should build a SelectBoxes component', function () { + return Harness.testCreate(SelectBoxesComponent, comp1).then( + (component) => { + Harness.testElements(component, 'input[type="checkbox"]', 8); + }, + ); }); - }); - - it('Should provide metadata.selectData for SelectBoxes component with URL DataSrc', function(done) { - const form = _.cloneDeep(comp5); - const element = document.createElement('div'); - const originalMakeRequest = Formio.makeRequest; - - Formio.makeRequest = function() { - return new Promise(resolve => { - const values = [ - { name : 'Alabama', abbreviation : 'AL' }, - { name : 'Alaska', abbreviation: 'AK' }, - { name: 'American Samoa', abbreviation: 'AS' } - ]; - resolve(values); - }); - }; - - Formio.createForm(element, form).then(form => { - const selectBoxes = form.getComponent('selectBoxes'); - - setTimeout(()=>{ - const value = { AL: false, AK: true, AS: true }; - selectBoxes.setValue(value); - setTimeout(() => { - assert.equal(selectBoxes.dataValue, value); - const submit = form.getComponent('submit'); - const clickEvent = new Event('click'); - const submitBtn = submit.refs.button; - submitBtn.dispatchEvent(clickEvent); - setTimeout(() => { - assert.equal(_.isEqual(form.submission.metadata.selectData.selectBoxes, [{ name : 'Alaska' }, { name : 'American Samoa' }]), true); - assert.equal(form.submission.metadata.listData.selectBoxes.length, 3); - Formio.makeRequest = originalMakeRequest; - done(); - },200); - },200); - }, 200); - }).catch(done); - }); - - describe('error messages', function() { - it('Should have a minSelectedCount validation message', function() { - const formJson = { - components: [ - { - type: 'selectboxes', - key: 'options', - values: [ - { label: 'Option 1', value: '1' }, - { label: 'Option 2', value: '2' }, - { label: 'Option 3', value: '3' }, - { label: 'Option 4', value: '4' } - ], - validate: { - minSelectedCount: 2 - } - } - ] - }; - const element = document.createElement('div'); - return Formio.createForm(element, formJson) - .then(async form => { - form.submission = { - data: { - options: { 1: true } - } - }; - const comp = form.getComponent('options'); - setTimeout(() => { - const { messageContainer } = comp.refs; - assert.equal( - messageContainer.textContent.trim(), - 'You must select at least 2 items.' - ); - }, 300); - }); + + it('Should build a SelectBoxes component with URL DataSrc', function (done) { + const form = _.cloneDeep(comp5); + const element = document.createElement('div'); + const originalMakeRequest = Formio.makeRequest; + + Formio.makeRequest = function () { + return new Promise((resolve) => { + const values = [ + { name: 'Alabama', abbreviation: 'AL' }, + { name: 'Alaska', abbreviation: 'AK' }, + { name: 'American Samoa', abbreviation: 'AS' }, + ]; + resolve(values); + }); + }; + + Formio.createForm(element, form) + .then((form) => { + const selectBoxes = form.getComponent('selectBoxes'); + + setTimeout(() => { + assert.equal(selectBoxes.loadedOptions.length, 3); + + Formio.makeRequest = originalMakeRequest; + done(); + }, 200); + }) + .catch(done); }); - it('Should use the minSelectedCountMessage if provided', function() { - const formJson = { - components: [ - { - type: 'selectboxes', - key: 'options', - values: [ - { label: 'Option 1', value: '1' }, - { label: 'Option 2', value: '2' }, - { label: 'Option 3', value: '3' }, - { label: 'Option 4', value: '4' } - ], - validate: { - minSelectedCount: 2, - }, - minSelectedCountMessage: 'Please select at least {{minCount}} items.' - } - ] - }; - const element = document.createElement('div'); - return Formio.createForm(element, formJson) - .then(async form => { - form.submission = { - data: { - options: { 1: true } - } - }; - const comp = form.getComponent('options'); - setTimeout(() => { - const { messageContainer } = comp.refs; - assert.equal( - messageContainer.textContent.trim(), - 'Please select at least 2 items.' - ); - }, 300); + it('Should display values in DataTab', function (done) { + Harness.testCreate(SelectBoxesComponent, comp6).then((component) => { + const value1 = component.getView({ Alabama: false, Alaska: true }); + const value2 = component.getView({ Alabama: true, Alaska: true }); + assert.equal(value1, 'Alaska'); + assert.equal(value2, 'Alabama, Alaska'); + done(); }); }); - it('Hidden SelectBoxes validation should not prevent submission', function(done) { - const element = document.createElement('div'); - Formio.createForm(element, comp4) - .then(async form => { - const submit = form.getComponent('submit'); - const clickEvent = new Event('click'); - const submitBtn = submit.refs.button; - submitBtn.dispatchEvent(clickEvent); - setTimeout(() => { - assert.equal(form.submission.state, 'submitted'); - assert.equal(form.errors.length, 0); - done(); - }, 500); - }) - .catch(done); + it('Should provide metadata.selectData for SelectBoxes component with URL DataSrc', function (done) { + const form = _.cloneDeep(comp5); + const element = document.createElement('div'); + const originalMakeRequest = Formio.makeRequest; + + Formio.makeRequest = function () { + return new Promise((resolve) => { + const values = [ + { name: 'Alabama', abbreviation: 'AL' }, + { name: 'Alaska', abbreviation: 'AK' }, + { name: 'American Samoa', abbreviation: 'AS' }, + ]; + resolve(values); + }); + }; + + Formio.createForm(element, form) + .then((form) => { + const selectBoxes = form.getComponent('selectBoxes'); + + setTimeout(() => { + const value = { AL: false, AK: true, AS: true }; + selectBoxes.setValue(value); + setTimeout(() => { + assert.equal(selectBoxes.dataValue, value); + const submit = form.getComponent('submit'); + const clickEvent = new Event('click'); + const submitBtn = submit.refs.button; + submitBtn.dispatchEvent(clickEvent); + setTimeout(() => { + assert.equal( + _.isEqual( + form.submission.metadata.selectData + .selectBoxes, + [ + { name: 'Alaska' }, + { name: 'American Samoa' }, + ], + ), + true, + ); + assert.equal( + form.submission.metadata.listData.selectBoxes + .length, + 3, + ); + Formio.makeRequest = originalMakeRequest; + done(); + }, 200); + }, 200); + }, 200); + }) + .catch(done); }); - it('Should have a maxSelectedCount validation message', function() { - const formJson = { - components: [ - { - type: 'selectboxes', - key: 'options', - values: [ - { label: 'Option 1', value: '1' }, - { label: 'Option 2', value: '2' }, - { label: 'Option 3', value: '3' }, - { label: 'Option 4', value: '4' } - ], - validate: { - maxSelectedCount: 2 - } - } - ] - }; - const element = document.createElement('div'); - return Formio.createForm(element, formJson) - .then(async form => { - form.submission = { - data: { - options: { 1: true, 2: true, 3: true } - } - }; - const comp = form.getComponent('options'); - setTimeout(() => { - const { messageContainer } = comp.refs; - assert.equal( - messageContainer.textContent.trim(), - 'You can only select up to 2 items.' - ); - }, 300); + describe('error messages', function () { + it('Should have a minSelectedCount validation message', function () { + const formJson = { + components: [ + { + type: 'selectboxes', + key: 'options', + values: [ + { label: 'Option 1', value: '1' }, + { label: 'Option 2', value: '2' }, + { label: 'Option 3', value: '3' }, + { label: 'Option 4', value: '4' }, + ], + validate: { + minSelectedCount: 2, + }, + }, + ], + }; + const element = document.createElement('div'); + return Formio.createForm(element, formJson).then(async (form) => { + form.submission = { + data: { + options: { 1: true }, + }, + }; + const comp = form.getComponent('options'); + setTimeout(() => { + const { messageContainer } = comp.refs; + assert.equal( + messageContainer.textContent.trim(), + 'You must select at least 2 items.', + ); + }, 300); + }); }); - }); - it('Should use the maxSelectedCountMessage if provided', function() { - const formJson = { - components: [ - { - type: 'selectboxes', - key: 'options', - values: [ - { label: 'Option 1', value: '1' }, - { label: 'Option 2', value: '2' }, - { label: 'Option 3', value: '3' }, - { label: 'Option 4', value: '4' } - ], - validate: { - maxSelectedCount: 2, - }, - maxSelectedCountMessage: 'Please select {{maxCount}} items at most.' - } - ] - }; - const element = document.createElement('div'); - return Formio.createForm(element, formJson) - .then(async form => { - form.submission = { - data: { - options: { 1: true, 2: true, 3: true } - } - }; - const comp = form.getComponent('options'); - setTimeout(() => { - const { messageContainer } = comp.refs; - assert.equal( - messageContainer.textContent.trim(), - 'Please select 2 items at most.' - ); - }, 300); + it('Should use the minSelectedCountMessage if provided', function () { + const formJson = { + components: [ + { + type: 'selectboxes', + key: 'options', + values: [ + { label: 'Option 1', value: '1' }, + { label: 'Option 2', value: '2' }, + { label: 'Option 3', value: '3' }, + { label: 'Option 4', value: '4' }, + ], + validate: { + minSelectedCount: 2, + }, + minSelectedCountMessage: + 'Please select at least {{minCount}} items.', + }, + ], + }; + const element = document.createElement('div'); + return Formio.createForm(element, formJson).then(async (form) => { + form.submission = { + data: { + options: { 1: true }, + }, + }; + const comp = form.getComponent('options'); + setTimeout(() => { + const { messageContainer } = comp.refs; + assert.equal( + messageContainer.textContent.trim(), + 'Please select at least 2 items.', + ); + }, 300); + }); }); - }); - it('Should provide validation for ValueProperty', function(done) { - const form = _.cloneDeep(comp5); - const element = document.createElement('div'); - const originalMakeRequest = Formio.makeRequest; - - Formio.makeRequest = function() { - return new Promise(resolve => { - const values = [ - { name : 'Alabama', abbreviation : 'AL' }, - { name : 'Alaska', abbreviation: { a:2, b: 'c' } }, - { name : 'American Samoa', abbreviation: true } - ]; - resolve(values); + it('Hidden SelectBoxes validation should not prevent submission', function (done) { + const element = document.createElement('div'); + Formio.createForm(element, comp4) + .then(async (form) => { + const submit = form.getComponent('submit'); + const clickEvent = new Event('click'); + const submitBtn = submit.refs.button; + submitBtn.dispatchEvent(clickEvent); + setTimeout(() => { + assert.equal(form.submission.state, 'submitted'); + assert.equal(form.errors.length, 0); + done(); + }, 500); + }) + .catch(done); }); - }; - - Formio.createForm(element, form).then(async form => { - const selectBoxes = form.getComponent('selectBoxes'); - - setTimeout(()=>{ - const inputs = selectBoxes.element.querySelectorAll('input'); - inputs[1].checked = true; - inputs[2].checked = true; - - setTimeout(()=>{ - const submit = form.getComponent('submit'); - const clickEvent = new Event('click'); - const submitBtn = submit.refs.button; - submitBtn.dispatchEvent(clickEvent); - - setTimeout(()=>{ - assert.equal(form.errors.length, 1); - assert.equal(selectBoxes.error.message, 'Invalid Value Property'); - selectBoxes.setValue({ 'AL': true }); - - setTimeout(()=>{ - assert.equal(form.errors.length, 0); - assert.equal(!!selectBoxes.error, false); - document.innerHTML = ''; - Formio.makeRequest = originalMakeRequest; - done(); - }, 300); - }, 300); - }, 300); - }, 200); - }).catch(done); - }); - }); - - it('Should set "checked" attribute correctly when value is changed', function(done) { - Formio.createForm(document.createElement('div'), wizardWithSelectBoxes).then((form) => { - const selectBoxes = form.getComponent(['selectBoxes']); - const value = { - five: false, - four: false, - one: true, - three: false, - two: true, - }; - - selectBoxes.setValue(value); - - const checkInputs = (values) => { - Object.entries(values).forEach(([key, value]) => { - const input = selectBoxes.element.querySelector(`input[value="${key}"]`); - assert.equal(input.checked, value, 'Should be checked'); + + it('Should have a maxSelectedCount validation message', function () { + const formJson = { + components: [ + { + type: 'selectboxes', + key: 'options', + values: [ + { label: 'Option 1', value: '1' }, + { label: 'Option 2', value: '2' }, + { label: 'Option 3', value: '3' }, + { label: 'Option 4', value: '4' }, + ], + validate: { + maxSelectedCount: 2, + }, + }, + ], + }; + const element = document.createElement('div'); + return Formio.createForm(element, formJson).then(async (form) => { + form.submission = { + data: { + options: { 1: true, 2: true, 3: true }, + }, + }; + const comp = form.getComponent('options'); + setTimeout(() => { + const { messageContainer } = comp.refs; + assert.equal( + messageContainer.textContent.trim(), + 'You can only select up to 2 items.', + ); + }, 300); + }); }); - }; - setTimeout(() => { - checkInputs(value); - form.setPage(1); + it('Should use the maxSelectedCountMessage if provided', function () { + const formJson = { + components: [ + { + type: 'selectboxes', + key: 'options', + values: [ + { label: 'Option 1', value: '1' }, + { label: 'Option 2', value: '2' }, + { label: 'Option 3', value: '3' }, + { label: 'Option 4', value: '4' }, + ], + validate: { + maxSelectedCount: 2, + }, + maxSelectedCountMessage: + 'Please select {{maxCount}} items at most.', + }, + ], + }; + const element = document.createElement('div'); + return Formio.createForm(element, formJson).then(async (form) => { + form.submission = { + data: { + options: { 1: true, 2: true, 3: true }, + }, + }; + const comp = form.getComponent('options'); + setTimeout(() => { + const { messageContainer } = comp.refs; + assert.equal( + messageContainer.textContent.trim(), + 'Please select 2 items at most.', + ); + }, 300); + }); + }); - setTimeout(() => { - form.setPage(0); + it('Should provide validation for ValueProperty', function (done) { + const form = _.cloneDeep(comp5); + const element = document.createElement('div'); + const originalMakeRequest = Formio.makeRequest; + + Formio.makeRequest = function () { + return new Promise((resolve) => { + const values = [ + { name: 'Alabama', abbreviation: 'AL' }, + { name: 'Alaska', abbreviation: { a: 2, b: 'c' } }, + { name: 'American Samoa', abbreviation: true }, + ]; + resolve(values); + }); + }; + + Formio.createForm(element, form) + .then(async (form) => { + const selectBoxes = form.getComponent('selectBoxes'); + + setTimeout(() => { + const inputs = + selectBoxes.element.querySelectorAll('input'); + inputs[1].checked = true; + inputs[2].checked = true; + + setTimeout(() => { + const submit = form.getComponent('submit'); + const clickEvent = new Event('click'); + const submitBtn = submit.refs.button; + submitBtn.dispatchEvent(clickEvent); + + setTimeout(() => { + assert.equal(form.errors.length, 1); + assert.equal( + selectBoxes.error.message, + 'Invalid Value Property', + ); + selectBoxes.setValue({ AL: true }); + + setTimeout(() => { + assert.equal(form.errors.length, 0); + assert.equal(!!selectBoxes.error, false); + document.innerHTML = ''; + Formio.makeRequest = originalMakeRequest; + done(); + }, 300); + }, 300); + }, 300); + }, 200); + }) + .catch(done); + }); + }); - setTimeout(() => { - checkInputs(value); - done(); - }); - }, 200); - }, 200); - }).catch(done); - }); + it('Should set "checked" attribute correctly when value is changed', function (done) { + Formio.createForm(document.createElement('div'), wizardWithSelectBoxes) + .then((form) => { + const selectBoxes = form.getComponent(['selectBoxes']); + const value = { + five: false, + four: false, + one: true, + three: false, + two: true, + }; + + selectBoxes.setValue(value); + + const checkInputs = (values) => { + Object.entries(values).forEach(([key, value]) => { + const input = selectBoxes.element.querySelector( + `input[value="${key}"]`, + ); + assert.equal(input.checked, value, 'Should be checked'); + }); + }; + + setTimeout(() => { + checkInputs(value); + form.setPage(1); + + setTimeout(() => { + form.setPage(0); + + setTimeout(() => { + checkInputs(value); + done(); + }); + }, 200); + }, 200); + }) + .catch(done); + }); }); -describe('SelectBoxes Component Part Two', function() { - it('should have red asterisk left hand side to the options labels if component is required and label is hidden', function() { - return Harness.testCreate(SelectBoxesComponent, comp3).then(component => { - const options = component.element.querySelectorAll('.form-check-label'); - options.forEach(i => { - assert.deepEqual(!!getComputedStyle(i, ':before'), true); - }); +describe('SelectBoxes Component Part Two', function () { + it('should have red asterisk left hand side to the options labels if component is required and label is hidden', function () { + return Harness.testCreate(SelectBoxesComponent, comp3).then( + (component) => { + const options = + component.element.querySelectorAll('.form-check-label'); + options.forEach((i) => { + assert.deepEqual(!!getComputedStyle(i, ':before'), true); + }); + }, + ); + }); + + it('Should perform OnlyAvailableItems check properly', function (done) { + Harness.testCreate(SelectBoxesComponent, comp7) + .then((component) => { + assert.equal( + component.validateValueAvailability(true, { a: true }), + true, + 'Should be valid', + ); + assert.equal( + component.validateValueAvailability(true, { + a: false, + b: false, + c: false, + }), + true, + 'Should be valid', + ); + assert.equal( + component.validateValueAvailability(true, { + a: false, + newKey: false, + }), + false, + 'Should not be valid', + ); + assert.equal( + component.validateValueAvailability(true, { + newKey: false, + }), + false, + 'Should not be valid', + ); + assert.equal( + component.validateValueAvailability(true, {}), + true, + 'Should be valid', + ); + assert.equal( + component.validateValueAvailability(false, {}), + true, + 'Should be valid', + ); + done(); + }) + .catch(done); }); - }); - - it('Should perform OnlyAvailableItems check properly', function(done) { - Harness.testCreate(SelectBoxesComponent, comp7).then(component => { - assert.equal(component.validateValueAvailability(true, { a: true }), true, 'Should be valid'); - assert.equal(component.validateValueAvailability(true, { a: false, b: false, c: false }), true, 'Should be valid'); - assert.equal(component.validateValueAvailability(true, { a: false, newKey: false }), false, 'Should not be valid'); - assert.equal(component.validateValueAvailability(true, { newKey: false }), false, 'Should not be valid'); - assert.equal(component.validateValueAvailability(true, {}), true, 'Should be valid'); - assert.equal(component.validateValueAvailability(false, {}), true, 'Should be valid'); - done(); - }).catch(done); - }); }); diff --git a/src/components/selectboxes/editForm/SelectBoxes.edit.validation.js b/src/components/selectboxes/editForm/SelectBoxes.edit.validation.js index 5e0ac2ce35..b0b5488c2b 100644 --- a/src/components/selectboxes/editForm/SelectBoxes.edit.validation.js +++ b/src/components/selectboxes/editForm/SelectBoxes.edit.validation.js @@ -1,34 +1,35 @@ export default [ - { - type: 'number', - input: true, - key: 'validate.minSelectedCount', - label: 'Minimum checked number', - tooltip: 'Minimum checkboxes required before form can be submitted.', - weight: 250 - }, - { - type: 'number', - input: true, - key: 'validate.maxSelectedCount', - label: 'Maximum checked number', - tooltip: 'Maximum checkboxes possible before form can be submitted.', - weight: 250 - }, - { - type: 'textfield', - input: true, - key: 'minSelectedCountMessage', - label: 'Minimum checked error message', - tooltip: 'Error message displayed if minimum number of items not checked.', - weight: 250 - }, - { - type: 'textfield', - input: true, - key: 'maxSelectedCountMessage', - label: 'Maximum checked error message', - tooltip: 'Error message displayed if maximum number of items checked.', - weight: 250 - } + { + type: 'number', + input: true, + key: 'validate.minSelectedCount', + label: 'Minimum checked number', + tooltip: 'Minimum checkboxes required before form can be submitted.', + weight: 250, + }, + { + type: 'number', + input: true, + key: 'validate.maxSelectedCount', + label: 'Maximum checked number', + tooltip: 'Maximum checkboxes possible before form can be submitted.', + weight: 250, + }, + { + type: 'textfield', + input: true, + key: 'minSelectedCountMessage', + label: 'Minimum checked error message', + tooltip: + 'Error message displayed if minimum number of items not checked.', + weight: 250, + }, + { + type: 'textfield', + input: true, + key: 'maxSelectedCountMessage', + label: 'Maximum checked error message', + tooltip: 'Error message displayed if maximum number of items checked.', + weight: 250, + }, ]; diff --git a/src/components/selectboxes/fixtures/comp1.js b/src/components/selectboxes/fixtures/comp1.js index 0350d97a4a..c6d223bd99 100644 --- a/src/components/selectboxes/fixtures/comp1.js +++ b/src/components/selectboxes/fixtures/comp1.js @@ -1,52 +1,52 @@ export default { - 'conditional': { - 'eq': '', - 'when': null, - 'show': '' - }, - 'type': 'selectboxes', - 'validate': { - 'required': false - }, - 'persistent': true, - 'protected': false, - 'inline': false, - 'values': [ - { - 'label': 'Red', - 'value': 'red' + conditional: { + eq: '', + when: null, + show: '', }, - { - 'label': 'Blue', - 'value': 'blue' + type: 'selectboxes', + validate: { + required: false, }, - { - 'label': 'Green', - 'value': 'green' - }, - { - 'label': 'Orange', - 'value': 'orange' - }, - { - 'label': 'Yellow', - 'value': 'yellow' - }, - { - 'label': 'Purple', - 'value': 'purple' - }, - { - 'label': 'Black', - 'value': 'black' - }, - { - 'label': 'White', - 'value': 'white' - } - ], - 'key': 'favoriteColors', - 'label': 'Favorite Colors', - 'tableView': true, - 'input': true + persistent: true, + protected: false, + inline: false, + values: [ + { + label: 'Red', + value: 'red', + }, + { + label: 'Blue', + value: 'blue', + }, + { + label: 'Green', + value: 'green', + }, + { + label: 'Orange', + value: 'orange', + }, + { + label: 'Yellow', + value: 'yellow', + }, + { + label: 'Purple', + value: 'purple', + }, + { + label: 'Black', + value: 'black', + }, + { + label: 'White', + value: 'white', + }, + ], + key: 'favoriteColors', + label: 'Favorite Colors', + tableView: true, + input: true, }; diff --git a/src/components/selectboxes/fixtures/comp2.js b/src/components/selectboxes/fixtures/comp2.js index feb46ec1e5..a67d7b4b10 100644 --- a/src/components/selectboxes/fixtures/comp2.js +++ b/src/components/selectboxes/fixtures/comp2.js @@ -1,52 +1,52 @@ export default { - 'conditional': { - 'eq': '', - 'when': null, - 'show': '' - }, - 'type': 'selectboxes', - 'validate': { - 'required': true - }, - 'persistent': true, - 'protected': false, - 'inline': false, - 'values': [ - { - 'label': 'Red', - 'value': 'red' + conditional: { + eq: '', + when: null, + show: '', }, - { - 'label': 'Blue', - 'value': 'blue' + type: 'selectboxes', + validate: { + required: true, }, - { - 'label': 'Green', - 'value': 'green' - }, - { - 'label': 'Orange', - 'value': 'orange' - }, - { - 'label': 'Yellow', - 'value': 'yellow' - }, - { - 'label': 'Purple', - 'value': 'purple' - }, - { - 'label': 'Black', - 'value': 'black' - }, - { - 'label': 'White', - 'value': 'white' - } - ], - 'key': 'favoriteColors', - 'label': 'Favorite Colors', - 'tableView': true, - 'input': true + persistent: true, + protected: false, + inline: false, + values: [ + { + label: 'Red', + value: 'red', + }, + { + label: 'Blue', + value: 'blue', + }, + { + label: 'Green', + value: 'green', + }, + { + label: 'Orange', + value: 'orange', + }, + { + label: 'Yellow', + value: 'yellow', + }, + { + label: 'Purple', + value: 'purple', + }, + { + label: 'Black', + value: 'black', + }, + { + label: 'White', + value: 'white', + }, + ], + key: 'favoriteColors', + label: 'Favorite Colors', + tableView: true, + input: true, }; diff --git a/src/components/selectboxes/fixtures/comp3.js b/src/components/selectboxes/fixtures/comp3.js index 78b78f015a..8d5fed4321 100644 --- a/src/components/selectboxes/fixtures/comp3.js +++ b/src/components/selectboxes/fixtures/comp3.js @@ -1,19 +1,19 @@ export default { - _id: '61135ec3e4188f021c7ad390', - label: 'Select Boxes', - optionsLabelPosition: 'right', - hideLabel: true, - tableView: false, - values: [ - { label: '55', value: '55', shortcut: '' }, - { label: '77', value: '77', shortcut: '' }, - ], - validate: { required: true }, - key: 'selectBoxes', - type: 'selectboxes', - input: true, - inputType: 'checkbox', - title: 'FIO-2959', - name: 'fio2959', - path: 'fio2959', + _id: '61135ec3e4188f021c7ad390', + label: 'Select Boxes', + optionsLabelPosition: 'right', + hideLabel: true, + tableView: false, + values: [ + { label: '55', value: '55', shortcut: '' }, + { label: '77', value: '77', shortcut: '' }, + ], + validate: { required: true }, + key: 'selectBoxes', + type: 'selectboxes', + input: true, + inputType: 'checkbox', + title: 'FIO-2959', + name: 'fio2959', + path: 'fio2959', }; diff --git a/src/components/selectboxes/fixtures/comp4.js b/src/components/selectboxes/fixtures/comp4.js index 9e4e9ecd87..bc77995219 100644 --- a/src/components/selectboxes/fixtures/comp4.js +++ b/src/components/selectboxes/fixtures/comp4.js @@ -1,79 +1,79 @@ export default { - _id: '638743c823671dec2ace618b', - title: '5685', - name: '5685', - path: '5685', - type: 'form', - display: 'form', - components: [ - { - label: 'Checkbox', - tableView: false, - key: 'checkbox', - type: 'checkbox', - input: true - }, - { - label: 'Container', - tableView: false, - key: 'container', - conditional: { - show: true, - conjunction: 'all', - conditions: [ - { - component: 'checkbox', - operator: 'isEqual', - value: true - } - ] - }, - type: 'container', - input: true, - components: [ - { - label: 'Select Boxes', - optionsLabelPosition: 'right', - tableView: false, - defaultValue: - { - '1': false, - '2': false, - '3': false - }, - values: [ - { - label: '1', - value: '1', - shortcut: '' - }, - { - label: '2', - value: '2', - shortcut: '' - }, - { - label: '3', - value: '3', - shortcut: '' - }], - validate: - { - minSelectedCount: 1 - }, - key: 'selectBoxes', - type: 'selectboxes', - input: true, - inputType: 'checkbox' - } - ] - }, - { - type: 'button', - label: 'Submit', - key: 'submit', - disableOnInvalid: true, - input: true, - tableView: false - }] + _id: '638743c823671dec2ace618b', + title: '5685', + name: '5685', + path: '5685', + type: 'form', + display: 'form', + components: [ + { + label: 'Checkbox', + tableView: false, + key: 'checkbox', + type: 'checkbox', + input: true, + }, + { + label: 'Container', + tableView: false, + key: 'container', + conditional: { + show: true, + conjunction: 'all', + conditions: [ + { + component: 'checkbox', + operator: 'isEqual', + value: true, + }, + ], + }, + type: 'container', + input: true, + components: [ + { + label: 'Select Boxes', + optionsLabelPosition: 'right', + tableView: false, + defaultValue: { + 1: false, + 2: false, + 3: false, + }, + values: [ + { + label: '1', + value: '1', + shortcut: '', + }, + { + label: '2', + value: '2', + shortcut: '', + }, + { + label: '3', + value: '3', + shortcut: '', + }, + ], + validate: { + minSelectedCount: 1, + }, + key: 'selectBoxes', + type: 'selectboxes', + input: true, + inputType: 'checkbox', + }, + ], + }, + { + type: 'button', + label: 'Submit', + key: 'submit', + disableOnInvalid: true, + input: true, + tableView: false, + }, + ], }; diff --git a/src/components/selectboxes/fixtures/comp5.js b/src/components/selectboxes/fixtures/comp5.js index fc76a9e025..3700bee842 100644 --- a/src/components/selectboxes/fixtures/comp5.js +++ b/src/components/selectboxes/fixtures/comp5.js @@ -5,7 +5,7 @@ export default { label: 'Select Boxes', dataSrc: 'url', data: { - url: 'https://cdn.rawgit.com/mshafrir/2646763/raw/states_titlecase.json' + url: 'https://cdn.rawgit.com/mshafrir/2646763/raw/states_titlecase.json', }, valueProperty: 'abbreviation', template: '{{ item.name }}', @@ -15,17 +15,17 @@ export default { inputType: 'checkbox', }, { - label: 'Submit', - showValidations: false, - alwaysEnabled: false, - tableView: false, - key: 'submit', - type: 'button', - input: true - } + label: 'Submit', + showValidations: false, + alwaysEnabled: false, + tableView: false, + key: 'submit', + type: 'button', + input: true, + }, ], title: 'test selectBoxes Url', - display: 'form', - name: 'testSelectBoxesUrl', - path: 'testSelectBoxesUrl', + display: 'form', + name: 'testSelectBoxesUrl', + path: 'testSelectBoxesUrl', }; diff --git a/src/components/selectboxes/fixtures/comp6.js b/src/components/selectboxes/fixtures/comp6.js index 38756d5e77..955994c4f0 100644 --- a/src/components/selectboxes/fixtures/comp6.js +++ b/src/components/selectboxes/fixtures/comp6.js @@ -2,7 +2,7 @@ export default { label: 'Select Boxes', dataSrc: 'url', data: { - url: 'https://cdn.rawgit.com/mshafrir/2646763/raw/states_titlecase.json' + url: 'https://cdn.rawgit.com/mshafrir/2646763/raw/states_titlecase.json', }, valueProperty: 'abbreviation', template: '{{ item.name }}', diff --git a/src/components/selectboxes/fixtures/comp7.js b/src/components/selectboxes/fixtures/comp7.js index 4d3439764f..29e308986d 100644 --- a/src/components/selectboxes/fixtures/comp7.js +++ b/src/components/selectboxes/fixtures/comp7.js @@ -1,29 +1,29 @@ export default { - label: 'Select Boxes', - optionsLabelPosition: 'right', - tableView: false, - values: [ - { - label: 'a', - value: 'a', - shortcut: '', + label: 'Select Boxes', + optionsLabelPosition: 'right', + tableView: false, + values: [ + { + label: 'a', + value: 'a', + shortcut: '', + }, + { + label: 'b', + value: 'b', + shortcut: '', + }, + { + label: 'c', + value: 'c', + shortcut: '', + }, + ], + validate: { + onlyAvailableItems: true, }, - { - label: 'b', - value: 'b', - shortcut: '', - }, - { - label: 'c', - value: 'c', - shortcut: '', - }, - ], - validate: { - onlyAvailableItems: true, - }, - key: 'selectBoxes', - type: 'selectboxes', - input: true, - inputType: 'checkbox', + key: 'selectBoxes', + type: 'selectboxes', + input: true, + inputType: 'checkbox', }; diff --git a/src/components/selectboxes/fixtures/values.js b/src/components/selectboxes/fixtures/values.js index d99cad7947..a4c5d1196a 100644 --- a/src/components/selectboxes/fixtures/values.js +++ b/src/components/selectboxes/fixtures/values.js @@ -1,22 +1,22 @@ export default [ - { - one: true, - two: true, - three: true, - }, - { - one: false, - two: false, - three: false, - }, - { - one: true, - two: false, - three: true, - }, - { - one: false, - two: true, - three: false, - }, + { + one: true, + two: true, + three: true, + }, + { + one: false, + two: false, + three: false, + }, + { + one: true, + two: false, + three: true, + }, + { + one: false, + two: true, + three: false, + }, ]; diff --git a/src/components/signature/Signature.form.js b/src/components/signature/Signature.form.js index d7db9e4fb8..aefcf4f677 100644 --- a/src/components/signature/Signature.form.js +++ b/src/components/signature/Signature.form.js @@ -3,19 +3,22 @@ import SignatureEditData from './editForm/Signature.edit.data'; import SignatureEditDisplay from './editForm/Signature.edit.display'; import SignatureEditValidation from './editForm/Signature.edit.validation'; -export default function(...extend) { - return Components.baseEditForm([ - { - key: 'display', - components: SignatureEditDisplay - }, - { - key: 'data', - components: SignatureEditData - }, - { - key: 'validation', - components: SignatureEditValidation - }, - ], ...extend); +export default function (...extend) { + return Components.baseEditForm( + [ + { + key: 'display', + components: SignatureEditDisplay, + }, + { + key: 'data', + components: SignatureEditData, + }, + { + key: 'validation', + components: SignatureEditValidation, + }, + ], + ...extend, + ); } diff --git a/src/components/signature/Signature.js b/src/components/signature/Signature.js index 7549230af2..528bba8d7c 100644 --- a/src/components/signature/Signature.js +++ b/src/components/signature/Signature.js @@ -4,294 +4,336 @@ import _ from 'lodash'; import { componentValueTypes, getComponentSavedTypes } from '../../utils/utils'; export default class SignatureComponent extends Input { - static schema(...extend) { - return Input.schema({ - type: 'signature', - label: 'Signature', - key: 'signature', - footer: 'Sign above', - width: '100%', - height: '150px', - penColor: 'black', - backgroundColor: 'rgb(245,245,235)', - minWidth: '0.5', - maxWidth: '2.5', - keepOverlayRatio: true, - }, ...extend); - } - - static get builderInfo() { - return { - title: 'Signature', - group: 'advanced', - icon: 'pencil', - weight: 120, - documentation: '/developers/integrations/esign/esign-integrations#signature-component', - schema: SignatureComponent.schema() - }; - } - - static get serverConditionSettings() { - return SignatureComponent.conditionOperatorsSettings; - } - - static get conditionOperatorsSettings() { - return { - ...super.conditionOperatorsSettings, - operators: ['isEmpty', 'isNotEmpty'], - }; - } - - static savedValueTypes(schema) { - schema = schema || {}; - return getComponentSavedTypes(schema) || [componentValueTypes.string]; - } - - init() { - super.init(); - this.currentWidth = 0; - this.scale = 1; - - if (!this.component.width) { - this.component.width = '100%'; + static schema(...extend) { + return Input.schema( + { + type: 'signature', + label: 'Signature', + key: 'signature', + footer: 'Sign above', + width: '100%', + height: '150px', + penColor: 'black', + backgroundColor: 'rgb(245,245,235)', + minWidth: '0.5', + maxWidth: '2.5', + keepOverlayRatio: true, + }, + ...extend, + ); } - if (!this.component.height) { - this.component.height = '200px'; + + static get builderInfo() { + return { + title: 'Signature', + group: 'advanced', + icon: 'pencil', + weight: 120, + documentation: + '/developers/integrations/esign/esign-integrations#signature-component', + schema: SignatureComponent.schema(), + }; } - if ( - this.component.keepOverlayRatio - && this.options?.display === 'pdf' - && this.component.overlay?.width - && this.component.overlay?.height - ) { - this.ratio = this.component.overlay?.width / this.component.overlay?.height; - this.component.width = '100%'; - this.component.height = 'auto'; + static get serverConditionSettings() { + return SignatureComponent.conditionOperatorsSettings; } - } - - get emptyValue() { - return ''; - } - - get defaultSchema() { - return SignatureComponent.schema(); - } - - get inputInfo() { - const info = super.inputInfo; - info.type = 'input'; - info.attr.type = 'hidden'; - return info; - } - - get className() { - return `${super.className} signature-pad`; - } - - labelIsHidden() { - return this.component.hideLabel; - } - - setValue(value, flags = {}) { - const changed = super.setValue(value, flags); - if (this.refs.signatureImage && (this.options.readOnly || this.disabled)) { - this.refs.signatureImage.setAttribute('src', value); - this.showCanvas(false); + + static get conditionOperatorsSettings() { + return { + ...super.conditionOperatorsSettings, + operators: ['isEmpty', 'isNotEmpty'], + }; } - if (this.signaturePad) { - if (!value) { - this.signaturePad.clear(); - } - else if (changed) { - this.triggerChange(); - } + + static savedValueTypes(schema) { + schema = schema || {}; + return getComponentSavedTypes(schema) || [componentValueTypes.string]; } - if (this.signaturePad && this.dataValue && this.signaturePad.isEmpty()) { - this.setDataToSigaturePad(); + init() { + super.init(); + this.currentWidth = 0; + this.scale = 1; + + if (!this.component.width) { + this.component.width = '100%'; + } + if (!this.component.height) { + this.component.height = '200px'; + } + + if ( + this.component.keepOverlayRatio && + this.options?.display === 'pdf' && + this.component.overlay?.width && + this.component.overlay?.height + ) { + this.ratio = + this.component.overlay?.width / this.component.overlay?.height; + this.component.width = '100%'; + this.component.height = 'auto'; + } } - return changed; - } - - showCanvas(show) { - if (show) { - if (this.refs.canvas) { - this.refs.canvas.style.display = 'inherit'; - } - if (this.refs.signatureImage) { - this.refs.signatureImage.style.display = 'none'; - } + get emptyValue() { + return ''; } - else { - if (this.refs.canvas) { - this.refs.canvas.style.display = 'none'; - } - if (this.refs.signatureImage) { - this.refs.signatureImage.style.display = 'inherit'; - this.refs.signatureImage.style.maxHeight = '100%'; - } + + get defaultSchema() { + return SignatureComponent.schema(); } - } - - onDisabled() { - this.showCanvas(!super.disabled); - if (this.signaturePad) { - if (super.disabled) { - this.signaturePad.off(); - if (this.refs.refresh) { - this.refs.refresh.classList.add('disabled'); + + get inputInfo() { + const info = super.inputInfo; + info.type = 'input'; + info.attr.type = 'hidden'; + return info; + } + + get className() { + return `${super.className} signature-pad`; + } + + labelIsHidden() { + return this.component.hideLabel; + } + + setValue(value, flags = {}) { + const changed = super.setValue(value, flags); + if ( + this.refs.signatureImage && + (this.options.readOnly || this.disabled) + ) { + this.refs.signatureImage.setAttribute('src', value); + this.showCanvas(false); } - if (this.refs.signatureImage && this.dataValue) { - this.refs.signatureImage.setAttribute('src', this.dataValue); + if (this.signaturePad) { + if (!value) { + this.signaturePad.clear(); + } else if (changed) { + this.triggerChange(); + } } - } - else { - this.signaturePad.on(); - if (this.refs.refresh) { - this.refs.refresh.classList.remove('disabled'); + + if ( + this.signaturePad && + this.dataValue && + this.signaturePad.isEmpty() + ) { + this.setDataToSigaturePad(); } - } + + return changed; } - } - - checkSize(force, scale) { - if (this.refs.padBody && (force || this.refs.padBody && this.refs.padBody.offsetWidth !== this.currentWidth)) { - this.scale = force ? scale : this.scale; - this.currentWidth = this.refs.padBody.offsetWidth; - const width = this.currentWidth * this.scale; - const height = this.ratio ? width / this.ratio : this.refs.padBody.offsetHeight * this.scale; - const maxHeight = this.ratio ? height : this.refs.padBody.offsetHeight * this.scale; - - this.refs.canvas.width = width; - this.refs.canvas.height = height > maxHeight ? maxHeight : height; - this.refs.canvas.style.maxWidth = `${this.currentWidth * this.scale}px`; - this.refs.canvas.style.maxHeight = `${maxHeight}px`; - const ctx = this.refs.canvas.getContext('2d'); - ctx.setTransform(1, 0, 0, 1, 0, 0); - ctx.scale((1 / this.scale), (1 / this.scale)); - ctx.fillStyle = this.signaturePad.backgroundColor; - ctx.fillRect(0, 0, this.refs.canvas.width, this.refs.canvas.height); - this.signaturePad.clear(); - - if (this.dataValue) { - this.setDataToSigaturePad(); - } - - this.showCanvas(true); + + showCanvas(show) { + if (show) { + if (this.refs.canvas) { + this.refs.canvas.style.display = 'inherit'; + } + if (this.refs.signatureImage) { + this.refs.signatureImage.style.display = 'none'; + } + } else { + if (this.refs.canvas) { + this.refs.canvas.style.display = 'none'; + } + if (this.refs.signatureImage) { + this.refs.signatureImage.style.display = 'inherit'; + this.refs.signatureImage.style.maxHeight = '100%'; + } + } + } + + onDisabled() { + this.showCanvas(!super.disabled); + if (this.signaturePad) { + if (super.disabled) { + this.signaturePad.off(); + if (this.refs.refresh) { + this.refs.refresh.classList.add('disabled'); + } + if (this.refs.signatureImage && this.dataValue) { + this.refs.signatureImage.setAttribute( + 'src', + this.dataValue, + ); + } + } else { + this.signaturePad.on(); + if (this.refs.refresh) { + this.refs.refresh.classList.remove('disabled'); + } + } + } + } + + checkSize(force, scale) { + if ( + this.refs.padBody && + (force || + (this.refs.padBody && + this.refs.padBody.offsetWidth !== this.currentWidth)) + ) { + this.scale = force ? scale : this.scale; + this.currentWidth = this.refs.padBody.offsetWidth; + const width = this.currentWidth * this.scale; + const height = this.ratio + ? width / this.ratio + : this.refs.padBody.offsetHeight * this.scale; + const maxHeight = this.ratio + ? height + : this.refs.padBody.offsetHeight * this.scale; + + this.refs.canvas.width = width; + this.refs.canvas.height = height > maxHeight ? maxHeight : height; + this.refs.canvas.style.maxWidth = `${ + this.currentWidth * this.scale + }px`; + this.refs.canvas.style.maxHeight = `${maxHeight}px`; + const ctx = this.refs.canvas.getContext('2d'); + ctx.setTransform(1, 0, 0, 1, 0, 0); + ctx.scale(1 / this.scale, 1 / this.scale); + ctx.fillStyle = this.signaturePad.backgroundColor; + ctx.fillRect(0, 0, this.refs.canvas.width, this.refs.canvas.height); + this.signaturePad.clear(); + + if (this.dataValue) { + this.setDataToSigaturePad(); + } + + this.showCanvas(true); + } + } + + renderElement(value, index) { + return this.renderTemplate('signature', { + element: super.renderElement(value, index), + required: _.get(this.component, 'validate.required', false), + }); } - } - - renderElement(value, index) { - return this.renderTemplate('signature', { - element: super.renderElement(value, index), - required: _.get(this.component, 'validate.required', false), - }); - } - - get hasModalSaveButton() { - return false; - } - - getModalPreviewTemplate() { - return this.renderTemplate('modalPreview', { - previewText: this.dataValue ? - `` : - this.t('Click to Sign') - }); - } - - attach(element) { - this.loadRefs(element, { canvas: 'single', refresh: 'single', padBody: 'single', signatureImage: 'single' }); - const superAttach = super.attach(element); - - if (this.refs.refresh && this.options.readOnly) { - this.refs.refresh.classList.add('disabled'); + + get hasModalSaveButton() { + return false; } - // Create the signature pad. - if (this.refs.canvas) { - this.signaturePad = new SignaturePad(this.refs.canvas, { - minWidth: this.component.minWidth, - maxWidth: this.component.maxWidth, - penColor: this.component.penColor, - backgroundColor: this.component.backgroundColor - }); + getModalPreviewTemplate() { + return this.renderTemplate('modalPreview', { + previewText: this.dataValue + ? `` + : this.t('Click to Sign'), + }); + } - this.signaturePad.addEventListener('endStroke', () => this.setValue(this.signaturePad.toDataURL())); - this.refs.signatureImage.setAttribute('src', this.signaturePad.toDataURL()); + attach(element) { + this.loadRefs(element, { + canvas: 'single', + refresh: 'single', + padBody: 'single', + signatureImage: 'single', + }); + const superAttach = super.attach(element); + + if (this.refs.refresh && this.options.readOnly) { + this.refs.refresh.classList.add('disabled'); + } - this.onDisabled(); + // Create the signature pad. + if (this.refs.canvas) { + this.signaturePad = new SignaturePad(this.refs.canvas, { + minWidth: this.component.minWidth, + maxWidth: this.component.maxWidth, + penColor: this.component.penColor, + backgroundColor: this.component.backgroundColor, + }); + + this.signaturePad.addEventListener('endStroke', () => + this.setValue(this.signaturePad.toDataURL()), + ); + this.refs.signatureImage.setAttribute( + 'src', + this.signaturePad.toDataURL(), + ); + + this.onDisabled(); + + // Ensure the signature is always the size of its container. + if (this.refs.padBody) { + if (!this.refs.padBody.style.maxWidth) { + this.refs.padBody.style.maxWidth = '100%'; + } + + if (!this.builderMode && !this.options.preview) { + this.observer = new ResizeObserver(() => { + this.checkSize(); + }); + + this.observer.observe(this.refs.padBody); + } + + this.addEventListener( + window, + 'resize', + _.debounce(() => this.checkSize(), 10), + ); + + setTimeout( + function checkWidth() { + if ( + this.refs.padBody && + this.refs.padBody.offsetWidth + ) { + this.checkSize(); + } else { + setTimeout(checkWidth.bind(this), 20); + } + }.bind(this), + 20, + ); + } + } + this.addEventListener(this.refs.refresh, 'click', (event) => { + event.preventDefault(); + this.showCanvas(true); + this.signaturePad.clear(); + this.setValue(this.defaultValue); + }); + this.setValue(this.dataValue); + return superAttach; + } + /* eslint-enable max-statements */ - // Ensure the signature is always the size of its container. - if (this.refs.padBody) { - if (!this.refs.padBody.style.maxWidth) { - this.refs.padBody.style.maxWidth = '100%'; + detach() { + if (this.observer) { + this.observer.disconnect(); + this.observer = null; } - if (!this.builderMode && !this.options.preview) { - this.observer = new ResizeObserver(() => { - this.checkSize(); - }); - - this.observer.observe(this.refs.padBody); - } - - this.addEventListener(window, 'resize', _.debounce(() => this.checkSize(), 10)); - - setTimeout(function checkWidth() { - if (this.refs.padBody && this.refs.padBody.offsetWidth) { - this.checkSize(); - } - else { - setTimeout(checkWidth.bind(this), 20); - } - }.bind(this), 20); - } + if (this.signaturePad) { + this.signaturePad.off(); + } + this.signaturePad = null; + this.currentWidth = 0; + super.detach(); } - this.addEventListener(this.refs.refresh, 'click', (event) => { - event.preventDefault(); - this.showCanvas(true); - this.signaturePad.clear(); - this.setValue(this.defaultValue); - }); - this.setValue(this.dataValue); - return superAttach; - } - /* eslint-enable max-statements */ - - detach() { - if (this.observer) { - this.observer.disconnect(); - this.observer = null; + + getValueAsString(value) { + if (_.isUndefined(value) && this.inDataTable) { + return ''; + } + return value ? 'Yes' : 'No'; } - if (this.signaturePad) { - this.signaturePad.off(); + focus() { + this.refs.padBody.focus(); } - this.signaturePad = null; - this.currentWidth = 0; - super.detach(); - } - - getValueAsString(value) { - if (_.isUndefined(value) && this.inDataTable) { - return ''; + + setDataToSigaturePad() { + this.signaturePad.fromDataURL(this.dataValue, { + ratio: 1, + width: this.refs.canvas.width, + height: this.refs.canvas.height, + }); } - return value ? 'Yes' : 'No'; - } - - focus() { - this.refs.padBody.focus(); - } - - setDataToSigaturePad() { - this.signaturePad.fromDataURL(this.dataValue, { - ratio: 1, - width: this.refs.canvas.width, - height: this.refs.canvas.height, - }); - } } diff --git a/src/components/signature/Signature.unit.js b/src/components/signature/Signature.unit.js index 8b13789179..e69de29bb2 100644 --- a/src/components/signature/Signature.unit.js +++ b/src/components/signature/Signature.unit.js @@ -1 +0,0 @@ - diff --git a/src/components/signature/editForm/Signature.edit.data.js b/src/components/signature/editForm/Signature.edit.data.js index 8cb587f673..e9906c273a 100644 --- a/src/components/signature/editForm/Signature.edit.data.js +++ b/src/components/signature/editForm/Signature.edit.data.js @@ -1,14 +1,14 @@ export default [ - { - key: 'multiple', - ignore: true, - }, - { - key: 'defaultValue', - ignore: true, - }, - { - key: 'dbIndex', - ignore: true, - }, + { + key: 'multiple', + ignore: true, + }, + { + key: 'defaultValue', + ignore: true, + }, + { + key: 'dbIndex', + ignore: true, + }, ]; diff --git a/src/components/signature/editForm/Signature.edit.display.js b/src/components/signature/editForm/Signature.edit.display.js index 9a2d296f93..6d58fe2b60 100644 --- a/src/components/signature/editForm/Signature.edit.display.js +++ b/src/components/signature/editForm/Signature.edit.display.js @@ -1,69 +1,72 @@ export default [ - { - type: 'textfield', - input: true, - key: 'footer', - label: 'Footer Label', - tooltip: 'The footer text that appears below the signature area.', - placeholder: 'Footer Label', - weight: 10 - }, - { - type: 'textfield', - input: true, - key: 'width', - label: 'Width', - tooltip: 'The width of the signature area.', - placeholder: 'Width', - conditional: { - json: { '!' : [{ var: 'data.keepOverlayRatio' }] }, + { + type: 'textfield', + input: true, + key: 'footer', + label: 'Footer Label', + tooltip: 'The footer text that appears below the signature area.', + placeholder: 'Footer Label', + weight: 10, }, - weight: 50 - }, - { - type: 'textfield', - input: true, - key: 'height', - label: 'Height', - tooltip: 'The height of the signature area.', - placeholder: 'Height', conditional: { - json: { '!' : [{ var: 'data.keepOverlayRatio' }] }, + { + type: 'textfield', + input: true, + key: 'width', + label: 'Width', + tooltip: 'The width of the signature area.', + placeholder: 'Width', + conditional: { + json: { '!': [{ var: 'data.keepOverlayRatio' }] }, + }, + weight: 50, + }, + { + type: 'textfield', + input: true, + key: 'height', + label: 'Height', + tooltip: 'The height of the signature area.', + placeholder: 'Height', + conditional: { + json: { '!': [{ var: 'data.keepOverlayRatio' }] }, + }, + weight: 51, + }, + { + weight: 52, + type: 'checkbox', + label: 'Keep Overlay Aspect Ratio', + tooltip: + 'If checked, the field will have the same aspect ratio as its preview.', + key: 'keepOverlayRatio', + customConditional: ({ options }) => + options?.editForm?.display === 'pdf', + input: true, + }, + { + type: 'textfield', + input: true, + key: 'backgroundColor', + label: 'Background Color', + tooltip: 'The background color of the signature area.', + placeholder: 'Background Color', + weight: 52, + }, + { + type: 'textfield', + input: true, + key: 'penColor', + label: 'Pen Color', + tooltip: 'The ink color for the signature area.', + placeholder: 'Pen Color', + weight: 53, + }, + { + key: 'placeholder', + ignore: true, + }, + { + key: 'autofocus', + ignore: true, }, - weight: 51 - }, - { - weight: 52, - type: 'checkbox', - label: 'Keep Overlay Aspect Ratio', - tooltip: 'If checked, the field will have the same aspect ratio as its preview.', - key: 'keepOverlayRatio', - customConditional: ({ options }) => (options?.editForm?.display === 'pdf'), - input: true - }, - { - type: 'textfield', - input: true, - key: 'backgroundColor', - label: 'Background Color', - tooltip: 'The background color of the signature area.', - placeholder: 'Background Color', - weight: 52 - }, - { - type: 'textfield', - input: true, - key: 'penColor', - label: 'Pen Color', - tooltip: 'The ink color for the signature area.', - placeholder: 'Pen Color', - weight: 53 - }, - { - key: 'placeholder', - ignore: true, - }, - { - key: 'autofocus', - ignore: true, - }, ]; diff --git a/src/components/signature/editForm/Signature.edit.validation.js b/src/components/signature/editForm/Signature.edit.validation.js index a025ede8c1..0aed28bdfc 100644 --- a/src/components/signature/editForm/Signature.edit.validation.js +++ b/src/components/signature/editForm/Signature.edit.validation.js @@ -1,10 +1,10 @@ export default [ - { - key: 'unique', - ignore: true, - }, - { - key: 'validateOn', - ignore: true, - }, + { + key: 'unique', + ignore: true, + }, + { + key: 'validateOn', + ignore: true, + }, ]; diff --git a/src/components/signature/fixtures/comp1.js b/src/components/signature/fixtures/comp1.js index 5c5a118750..50280fc099 100644 --- a/src/components/signature/fixtures/comp1.js +++ b/src/components/signature/fixtures/comp1.js @@ -1,28 +1,28 @@ export default { - 'lockKey': true, - 'conditional': { - 'eq': '', - 'when': null, - 'show': '' - }, - 'tags': [], - 'hideLabel': true, - 'type': 'signature', - 'validate': { - 'required': false - }, - 'persistent': true, - 'protected': false, - 'maxWidth': '2.5', - 'minWidth': '0.5', - 'backgroundColor': 'rgb(245,245,235)', - 'penColor': 'black', - 'height': '150px', - 'width': '100%', - 'footer': 'Sign above', - 'placeholder': '', - 'key': 'signature1', - 'label': 'Signature', - 'tableView': true, - 'input': true + lockKey: true, + conditional: { + eq: '', + when: null, + show: '', + }, + tags: [], + hideLabel: true, + type: 'signature', + validate: { + required: false, + }, + persistent: true, + protected: false, + maxWidth: '2.5', + minWidth: '0.5', + backgroundColor: 'rgb(245,245,235)', + penColor: 'black', + height: '150px', + width: '100%', + footer: 'Sign above', + placeholder: '', + key: 'signature1', + label: 'Signature', + tableView: true, + input: true, }; diff --git a/src/components/signature/fixtures/values.js b/src/components/signature/fixtures/values.js index 1f2c660986..c7c7ebeca2 100644 --- a/src/components/signature/fixtures/values.js +++ b/src/components/signature/fixtures/values.js @@ -1,5 +1,5 @@ export default [ - 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABgIAAACWCAYAAAAc9JMyAAATVUlEQVR4Xu3dPWvVZxzH4TuCD1AzJEOdjEurIDgU6dKKuBUXqSL4brr4IjrVxRchdJRuQqVvQOpTl8QpEbVpmnKOdXMQ4cs33OdyCUbz+537uv9TPnDO2uvX24fDHwIECBAgQIAAAQIECBAgQIAAAQIECBAgQGBKgTUhYMp7dSgCBAgQIECAAAECBAgQIECAAAECBAgQILAUEAI8CAQIECBAgAABAgQIECBAgAABAgQIECBAYGIBIWDiy3U0AgQIECBAgAABAgQIECBAgAABAgQIECAgBHgGCBAgQIAAAQIECBAgQIAAAQIECBAgQIDAxAJCwMSX62gECBAgQIAAAQIECBAgQIAAAQIECBAgQEAI8AwQIECAAAECBAgQIECAAAECBAgQIECAAIGJBYSAiS/X0QgQIECAAAECBAgQIECAAAECBAgQIECAgBDgGSBAgAABAgQIECBAgAABAgQIECBAgAABAhMLCAETX66jESBAgAABAgQIECBAgAABAgQIECBAgAABIcAzQIAAAQIECBAgQIAAAQIECBAgQIAAAQIEJhYQAia+XEcjQIAAAQIECBAgQIAAAQIECBAgQIAAAQJCgGeAAAECBAgQIECAAAECBAgQIECAAAECBAhMLCAETHy5jkaAAAECBAgQIECAAAECBAgQIECAAAECBIQAzwABAgQIECBAgAABAgQIECBAgAABAgQIEJhYQAiY+HIdjQABAgQIECBAgAABAgQIECBAgAABAgQICAGeAQIECBAgQIAAAQIECBAgQIAAAQIECBAgMLGAEDDx5ToaAQIECBAgQIAAAQIECBAgQIAAAQIECBAQAjwDBAgQIECAAAECBAgQIECAAAECBAgQIEBgYgEhYOLLdTQCBAgQIECAAAECBAgQIECAAAECBAgQICAEeAYIECBAgAABAgQIECBAgAABAgQIECBAgMDEAkLAxJfraAQIECBAgAABAgQIECBAgAABAgQIECBAQAjwDBAgQIAAAQIECBAgQIAAAQIECBAgQIAAgYkFhICJL9fRCBAgQIAAAQIECBAgQIAAAQIECBAgQICAEOAZIECAAAECBAgQIECAAAECBAgQIECAAAECEwsIARNfrqMRIECAAAECBAgQIECAAAECBAgQIECAAAEhwDNAgAABAgQIECBAgAABAgQIECBAgAABAgQmFhACJr5cRyNAgAABAgQIECBAgAABAgQIECBAgAABAkKAZ4AAAQIECBAgQIAAAQIECBAgQIAAAQIECEwsIARMfLmORoAAAQIECBAgQIAAAQIECBAgQIAAAQIEhADPAAECBAgQIECAAAECBAgQIECAAAECBAgQmFhACJj4ch2NAAECBAgQIECAAAECBAgQIECAAAECBAgIAZ4BAgQIECBAgAABAgQIECBAgAABAgQIECAwscDKhoCXL/8ap06dHNvbr8bOzs7yik+fPr38ure3t/x65cp3E1+9oxEgQIAAAQIECBAgQIAAAQIECBAgQIDAKgisZAi4fv3mePjwt4/e79ra2jg8PFz+2/r66XHs2LGxvr4+3r37exkMzp07OzY3N/8PB1+M/f1/xoULX4+Dg4Pl906cODGeP38xdnf3lt/f3d1dxobFz21tnR2PH/8x9vZeL/9++fI34/btH8fGxsYqPGvOSIAAAQIECBAgQIAAAQIECBAgQIAAAQIFgZULAU+e/DkuXfq2QP3xlffu/Tzu3Ll9ZF6PF0KAAAECBAgQIECAAAECBAgQIECAAAECcwmsXAh4+vTZuHjx8pG5xfv3fxm3bt04Mq/HCyFAgAABAgQIECBAgAABAgQIECBAgACBuQRWLgQsru/atR/Go0e/f9JNHj9+fJw58+V4+/bt2Nl5tXx7n83NjbF4C6HFZwrs7++P8+e/GgcH/y7nnTx5Yjx79mL5OQOL7y/eImjxlkJbW1vLtwN6/9ZAe+PFi5fj5s0b4+7dnz7pdfhPBAgQIECAAAECBAgQIECAAAECBAgQIEDgcwRWMgQsoB48+HW8ebP45f7O2N5+/2HBV69+v/zl/ocPC/7wy/vPgfUzBAgQIECAAAECBAgQIECAAAECBAgQIEDgKAisbAg4CvheAwECBAgQIECAAAECBAgQIECAAAECBAgQSAsIAWlh8wkQIECAAAECBAgQIECAAAECBAgQIECAQFFACCjiW02AAAECBAgQIECAAAECBAgQIECAAAECBNICQkBa2HwCBAgQIECAAAECBAgQIECAAAECBAgQIFAUEAKK+FYTIECAAAECBAgQIECAAAECBAgQIECAAIG0gBCQFjafAAECBAgQIECAAAECBAgQIECAAAECBAgUBYSAIr7VBAgQIECAAAECBAgQIECAAAECBAgQIEAgLSAEpIXNJ0CAAAECBAgQIECAAAECBAgQIECAAAECRQEhoIhvNQECBAgQIECAAAECBAgQIECAAAECBAgQSAsIAWlh8wkQIECAAAECBAgQIECAAAECBAgQIECAQFFACCjiW02AAAECBAgQIECAAAECBAgQIECAAAECBNICQkBa2HwCBAgQIECAAAECBAgQIECAAAECBAgQIFAUEAKK+FYTIECAAAECBAgQIECAAAECBAgQIECAAIG0gBCQFjafAAECBAgQIECAAAECBAgQIECAAAECBAgUBYSAIr7VBAgQIECAAAECBAgQIECAAAECBAgQIEAgLSAEpIXNJ0CAAAECBAgQIECAAAECBAgQIECAAAECRQEhoIhvNQECBAgQIECAAAECBAgQIECAAAECBAgQSAsIAWlh8wkQIECAAAECBAgQIECAAAECBAgQIECAQFFACCjiW02AAAECBAgQIECAAAECBAgQIECAAAECBNICQkBa2HwCBAgQIECAAAECBAgQIECAAAECBAgQIFAUEAKK+FYTIECAAAECBAgQIECAAAECBAgQIECAAIG0gBCQFjafAAECBAgQIECAAAECBAgQIECAAAECBAgUBYSAIr7VBAgQIECAAAECBAgQIECAAAECBAgQIEAgLSAEpIXNJ0CAAAECBAgQIECAAAECBAgQIECAAAECRQEhoIhvNQECBAgQIECAAAECBAgQIECAAAECBAgQSAsIAWlh8wkQIECAAAECBAgQIECAAAECBAgQIECAQFFACCjiW02AAAECBAgQIECAAAECBAgQIECAAAECBNICQkBa2HwCBAgQIECAAAECBAgQIECAAAECBAgQIFAUEAKK+FYTIECAAAECBAgQIECAAAECBAgQIECAAIG0gBCQFjafAAECBAgQIECAAAECBAgQIECAAAECBAgUBYSAIr7VBAgQIECAAAECBAgQIECAAAECBAgQIEAgLSAEpIXNJ0CAAAECBAgQIECAAAECBAgQIECAAAECRQEhoIhvNQECBAgQIECAAAECBAgQIECAAAECBAgQSAsIAWlh8wkQIECAAAECBAgQIECAAAECBAgQIECAQFFACCjiW02AAAECBAgQIECAAAECBAgQIECAAAECBNICQkBa2HwCBAgQIECAAAECBAgQIECAAAECBAgQIFAUEAKK+FYTIECAAAECBAgQIECAAAECBAgQIECAAIG0gBCQFjafAAECBAgQIECAAAECBAgQIECAAAECBAgUBYSAIr7VBAgQIECAAAECBAgQIECAAAECBAgQIEAgLSAEpIXNJ0CAAAECBAgQIECAAAECBAgQIECAAAECRQEhoIhvNQECBAgQIECAAAECBAgQIECAAAECBAgQSAsIAWlh8wkQIECAAAECBAgQIECAAAECBAgQIECAQFFACCjiW02AAAECBAgQIECAAAECBAgQIECAAAECBNICQkBa2HwCBAgQIECAAAECBAgQIECAAAECBAgQIFAUEAKK+FYTIECAAAECBAgQIECAAAECBAgQIECAAIG0gBCQFjafAAECBAgQIECAAAECBAgQIECAAAECBAgUBYSAIr7VBAgQIECAAAECBAgQIECAAAECBAgQIEAgLSAEpIXNJ0CAAAECBAgQIECAAAECBAgQIECAAAECRQEhoIhvNQECBAgQIECAAAECBAgQIECAAAECBAgQSAsIAWlh8wkQIECAAAECBAgQIECAAAECBAgQIECAQFFACCjiW02AAAECBAgQIECAAAECBAgQIECAAAECBNICQkBa2HwCBAgQIECAAAECBAgQIECAAAECBAgQIFAUEAKK+FYTIECAAAECBAgQIECAAAECBAgQIECAAIG0gBCQFjafAAECBAgQIECAAAECBAgQIECAAAECBAgUBYSAIr7VBAgQIECAAAECBAgQIECAAAECBAgQIEAgLSAEpIXNJ0CAAAECBAgQIECAAAECBAgQIECAAAECRQEhoIhvNQECBAgQIECAAAECBAgQIECAAAECBAgQSAsIAWlh8wkQIECAAAECBAgQIECAAAECBAgQIECAQFFACCjiW02AAAECBAgQIECAAAECBAgQIECAAAECBNICQkBa2HwCBAgQIECAAAECBAgQIECAAAECBAgQIFAUEAKK+FYTIECAAAECBAgQIECAAAECBAgQIECAAIG0gBCQFjafAAECBAgQIECAAAECBAgQIECAAAECBAgUBYSAIr7VBAgQIECAAAECBAgQIECAAAECBAgQIEAgLSAEpIXNJ0CAAAECBAgQIECAAAECBAgQIECAAAECRQEhoIhvNQECBAgQIECAAAECBAgQIECAAAECBAgQSAsIAWlh8wkQIECAAAECBAgQIECAAAECBAgQIECAQFFACCjiW02AAAECBAgQIECAAAECBAgQIECAAAECBNICQkBa2HwCBAgQIECAAAECBAgQIECAAAECBAgQIFAUEAKK+FYTIECAAAECBAgQIECAAAECBAgQIECAAIG0gBCQFjafAAECBAgQIECAAAECBAgQIECAAAECBAgUBYSAIr7VBAgQIECAAAECBAgQIECAAAECBAgQIEAgLSAEpIXNJ0CAAAECBAgQIECAAAECBAgQIECAAAECRQEhoIhvNQECBAgQIECAAAECBAgQIECAAAECBAgQSAsIAWlh8wkQIECAAAECBAgQIECAAAECBAgQIECAQFFACCjiW02AAAECBAgQIECAAAECBAgQIECAAAECBNICQkBa2HwCBAgQIECAAAECBAgQIECAAAECBAgQIFAUEAKK+FYTIECAAAECBAgQIECAAAECBAgQIECAAIG0gBCQFjafAAECBAgQIECAAAECBAgQIECAAAECBAgUBYSAIr7VBAgQIECAAAECBAgQIECAAAECBAgQIEAgLSAEpIXNJ0CAAAECBAgQIECAAAECBAgQIECAAAECRQEhoIhvNQECBAgQIECAAAECBAgQIECAAAECBAgQSAsIAWlh8wkQIECAAAECBAgQIECAAAECBAgQIECAQFFACCjiW02AAAECBAgQIECAAAECBAgQIECAAAECBNICQkBa2HwCBAgQIECAAAECBAgQIECAAAECBAgQIFAUEAKK+FYTIECAAAECBAgQIECAAAECBAgQIECAAIG0gBCQFjafAAECBAgQIECAAAECBAgQIECAAAECBAgUBYSAIr7VBAgQIECAAAECBAgQIECAAAECBAgQIEAgLSAEpIXNJ0CAAAECBAgQIECAAAECBAgQIECAAAECRQEhoIhvNQECBAgQIECAAAECBAgQIECAAAECBAgQSAsIAWlh8wkQIECAAAECBAgQIECAAAECBAgQIECAQFFACCjiW02AAAECBAgQIECAAAECBAgQIECAAAECBNICQkBa2HwCBAgQIECAAAECBAgQIECAAAECBAgQIFAUEAKK+FYTIECAAAECBAgQIECAAAECBAgQIECAAIG0gBCQFjafAAECBAgQIECAAAECBAgQIECAAAECBAgUBYSAIr7VBAgQIECAAAECBAgQIECAAAECBAgQIEAgLSAEpIXNJ0CAAAECBAgQIECAAAECBAgQIECAAAECRQEhoIhvNQECBAgQIECAAAECBAgQIECAAAECBAgQSAsIAWlh8wkQIECAAAECBAgQIECAAAECBAgQIECAQFFACCjiW02AAAECBAgQIECAAAECBAgQIECAAAECBNICQkBa2HwCBAgQIECAAAECBAgQIECAAAECBAgQIFAUEAKK+FYTIECAAAECBAgQIECAAAECBAgQIECAAIG0gBCQFjafAAECBAgQIECAAAECBAgQIECAAAECBAgUBYSAIr7VBAgQIECAAAECBAgQIECAAAECBAgQIEAgLSAEpIXNJ0CAAAECBAgQIECAAAECBAgQIECAAAECRQEhoIhvNQECBAgQIECAAAECBAgQIECAAAECBAgQSAsIAWlh8wkQIECAAAECBAgQIECAAAECBAgQIECAQFFACCjiW02AAAECBAgQIECAAAECBAgQIECAAAECBNICQkBa2HwCBAgQIECAAAECBAgQIECAAAECBAgQIFAUEAKK+FYTIECAAAECBAgQIECAAAECBAgQIECAAIG0gBCQFjafAAECBAgQIECAAAECBAgQIECAAAECBAgUBYSAIr7VBAgQIECAAAECBAgQIECAAAECBAgQIEAgLfAfpsyz7fPE9GYAAAAASUVORK5CYII=', - 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABgQAAACWCAYAAAAR6uN1AAARFElEQVR4Xu3ZQQ0AMAwDsZU/6E4ajJ3LIE5/md3d4wgQIECAAAECBAgQIECAAAECBAgQIECAAIGvBcYg8HW/whEgQIAAAQIECBAgQIAAAQIECBAgQIAAgSdgEPAIBAgQIECAAAECBAgQIECAAAECBAgQIEAgIGAQCJQsIgECBAgQIECAAAECBAgQIECAAAECBAgQMAj4AQIECBAgQIAAAQIECBAgQIAAAQIECBAgEBAwCARKFpEAAQIECBAgQIAAAQIECBAgQIAAAQIECBgE/AABAgQIECBAgAABAgQIECBAgAABAgQIEAgIGAQCJYtIgAABAgQIECBAgAABAgQIECBAgAABAgQMAn6AAAECBAgQIECAAAECBAgQIECAAAECBAgEBAwCgZJFJECAAAECBAgQIECAAAECBAgQIECAAAECBgE/QIAAAQIECBAgQIAAAQIECBAgQIAAAQIEAgIGgUDJIhIgQIAAAQIECBAgQIAAAQIECBAgQIAAAYOAHyBAgAABAgQIECBAgAABAgQIECBAgAABAgEBg0CgZBEJECBAgAABAgQIECBAgAABAgQIECBAgIBBwA8QIECAAAECBAgQIECAAAECBAgQIECAAIGAgEEgULKIBAgQIECAAAECBAgQIECAAAECBAgQIEDAIOAHCBAgQIAAAQIECBAgQIAAAQIECBAgQIBAQMAgEChZRAIECBAgQIAAAQIECBAgQIAAAQIECBAgYBDwAwQIECBAgAABAgQIECBAgAABAgQIECBAICBgEAiULCIBAgQIECBAgAABAgQIECBAgAABAgQIEDAI+AECBAgQIECAAAECBAgQIECAAAECBAgQIBAQMAgEShaRAAECBAgQIECAAAECBAgQIECAAAECBAgYBPwAAQIECBAgQIAAAQIECBAgQIAAAQIECBAICBgEAiWLSIAAAQIECBAgQIAAAQIECBAgQIAAAQIEDAJ+gAABAgQIECBAgAABAgQIECBAgAABAgQIBAQMAoGSRSRAgAABAgQIECBAgAABAgQIECBAgAABAgYBP0CAAAECBAgQIECAAAECBAgQIECAAAECBAICBoFAySISIECAAAECBAgQIECAAAECBAgQIECAAAGDgB8gQIAAAQIECBAgQIAAAQIECBAgQIAAAQIBAYNAoGQRCRAgQIAAAQIECBAgQIAAAQIECBAgQICAQcAPECBAgAABAgQIECBAgAABAgQIECBAgACBgIBBIFCyiAQIECBAgAABAgQIECBAgAABAgQIECBAwCDgBwgQIECAAAECBAgQIECAAAECBAgQIECAQEDAIBAoWUQCBAgQIECAAAECBAgQIECAAAECBAgQIGAQ8AMECBAgQIAAAQIECBAgQIAAAQIECBAgQCAgYBAIlCwiAQIECBAgQIAAAQIECBAgQIAAAQIECBAwCPgBAgQIECBAgAABAgQIECBAgAABAgQIECAQEDAIBEoWkQABAgQIECBAgAABAgQIECBAgAABAgQIGAT8AAECBAgQIECAAAECBAgQIECAAAECBAgQCAgYBAIli0iAAAECBAgQIECAAAECBAgQIECAAAECBAwCfoAAAQIECBAgQIAAAQIECBAgQIAAAQIECAQEDAKBkkUkQIAAAQIECBAgQIAAAQIECBAgQIAAAQIGAT9AgAABAgQIECBAgAABAgQIECBAgAABAgQCAgaBQMkiEiBAgAABAgQIECBAgAABAgQIECBAgAABg4AfIECAAAECBAgQIECAAAECBAgQIECAAAECAQGDQKBkEQkQIECAAAECBAgQIECAAAECBAgQIECAgEHADxAgQIAAAQIECBAgQIAAAQIECBAgQIAAgYCAQSBQsogECBAgQIAAAQIECBAgQIAAAQIECBAgQMAg4AcIECBAgAABAgQIECBAgAABAgQIECBAgEBAwCAQKFlEAgQIECBAgAABAgQIECBAgAABAgQIECBgEPADBAgQIECAAAECBAgQIECAAAECBAgQIEAgIGAQCJQsIgECBAgQIECAAAECBAgQIECAAAECBAgQMAj4AQIECBAgQIAAAQIECBAgQIAAAQIECBAgEBAwCARKFpEAAQIECBAgQIAAAQIECBAgQIAAAQIECBgE/AABAgQIECBAgAABAgQIECBAgAABAgQIEAgIGAQCJYtIgAABAgQIECBAgAABAgQIECBAgAABAgQMAn6AAAECBAgQIECAAAECBAgQIECAAAECBAgEBAwCgZJFJECAAAECBAgQIECAAAECBAgQIECAAAECBgE/QIAAAQIECBAgQIAAAQIECBAgQIAAAQIEAgIGgUDJIhIgQIAAAQIECBAgQIAAAQIECBAgQIAAAYOAHyBAgAABAgQIECBAgAABAgQIECBAgAABAgEBg0CgZBEJECBAgAABAgQIECBAgAABAgQIECBAgIBBwA8QIECAAAECBAgQIECAAAECBAgQIECAAIGAgEEgULKIBAgQIECAAAECBAgQIECAAAECBAgQIEDAIOAHCBAgQIAAAQIECBAgQIAAAQIECBAgQIBAQMAgEChZRAIECBAgQIAAAQIECBAgQIAAAQIECBAgYBDwAwQIECBAgAABAgQIECBAgAABAgQIECBAICBgEAiULCIBAgQIECBAgAABAgQIECBAgAABAgQIEDAI+AECBAgQIECAAAECBAgQIECAAAECBAgQIBAQMAgEShaRAAECBAgQIECAAAECBAgQIECAAAECBAgYBPwAAQIECBAgQIAAAQIECBAgQIAAAQIECBAICBgEAiWLSIAAAQIECBAgQIAAAQIECBAgQIAAAQIEDAJ+gAABAgQIECBAgAABAgQIECBAgAABAgQIBAQMAoGSRSRAgAABAgQIECBAgAABAgQIECBAgAABAgYBP0CAAAECBAgQIECAAAECBAgQIECAAAECBAICBoFAySISIECAAAECBAgQIECAAAECBAgQIECAAAGDgB8gQIAAAQIECBAgQIAAAQIECBAgQIAAAQIBAYNAoGQRCRAgQIAAAQIECBAgQIAAAQIECBAgQICAQcAPECBAgAABAgQIECBAgAABAgQIECBAgACBgIBBIFCyiAQIECBAgAABAgQIECBAgAABAgQIECBAwCDgBwgQIECAAAECBAgQIECAAAECBAgQIECAQEDAIBAoWUQCBAgQIECAAAECBAgQIECAAAECBAgQIGAQ8AMECBAgQIAAAQIECBAgQIAAAQIECBAgQCAgYBAIlCwiAQIECBAgQIAAAQIECBAgQIAAAQIECBAwCPgBAgQIECBAgAABAgQIECBAgAABAgQIECAQEDAIBEoWkQABAgQIECBAgAABAgQIECBAgAABAgQIGAT8AAECBAgQIECAAAECBAgQIECAAAECBAgQCAgYBAIli0iAAAECBAgQIECAAAECBAgQIECAAAECBAwCfoAAAQIECBAgQIAAAQIECBAgQIAAAQIECAQEDAKBkkUkQIAAAQIECBAgQIAAAQIECBAgQIAAAQIGAT9AgAABAgQIECBAgAABAgQIECBAgAABAgQCAgaBQMkiEiBAgAABAgQIECBAgAABAgQIECBAgAABg4AfIECAAAECBAgQIECAAAECBAgQIECAAAECAQGDQKBkEQkQIECAAAECBAgQIECAAAECBAgQIECAgEHADxAgQIAAAQIECBAgQIAAAQIECBAgQIAAgYCAQSBQsogECBAgQIAAAQIECBAgQIAAAQIECBAgQMAg4AcIECBAgAABAgQIECBAgAABAgQIECBAgEBAwCAQKFlEAgQIECBAgAABAgQIECBAgAABAgQIECBgEPADBAgQIECAAAECBAgQIECAAAECBAgQIEAgIGAQCJQsIgECBAgQIECAAAECBAgQIECAAAECBAgQMAj4AQIECBAgQIAAAQIECBAgQIAAAQIECBAgEBAwCARKFpEAAQIECBAgQIAAAQIECBAgQIAAAQIECBgE/AABAgQIECBAgAABAgQIECBAgAABAgQIEAgIGAQCJYtIgAABAgQIECBAgAABAgQIECBAgAABAgQMAn6AAAECBAgQIECAAAECBAgQIECAAAECBAgEBAwCgZJFJECAAAECBAgQIECAAAECBAgQIECAAAECBgE/QIAAAQIECBAgQIAAAQIECBAgQIAAAQIEAgIGgUDJIhIgQIAAAQIECBAgQIAAAQIECBAgQIAAAYOAHyBAgAABAgQIECBAgAABAgQIECBAgAABAgEBg0CgZBEJECBAgAABAgQIECBAgAABAgQIECBAgIBBwA8QIECAAAECBAgQIECAAAECBAgQIECAAIGAgEEgULKIBAgQIECAAAECBAgQIECAAAECBAgQIEDAIOAHCBAgQIAAAQIECBAgQIAAAQIECBAgQIBAQMAgEChZRAIECBAgQIAAAQIECBAgQIAAAQIECBAgYBDwAwQIECBAgAABAgQIECBAgAABAgQIECBAICBgEAiULCIBAgQIECBAgAABAgQIECBAgAABAgQIEDAI+AECBAgQIECAAAECBAgQIECAAAECBAgQIBAQMAgEShaRAAECBAgQIECAAAECBAgQIECAAAECBAgYBPwAAQIECBAgQIAAAQIECBAgQIAAAQIECBAICBgEAiWLSIAAAQIECBAgQIAAAQIECBAgQIAAAQIEDAJ+gAABAgQIECBAgAABAgQIECBAgAABAgQIBAQMAoGSRSRAgAABAgQIECBAgAABAgQIECBAgAABAgYBP0CAAAECBAgQIECAAAECBAgQIECAAAECBAICBoFAySISIECAAAECBAgQIECAAAECBAgQIECAAAGDgB8gQIAAAQIECBAgQIAAAQIECBAgQIAAAQIBAYNAoGQRCRAgQIAAAQIECBAgQIAAAQIECBAgQICAQcAPECBAgAABAgQIECBAgAABAgQIECBAgACBgIBBIFCyiAQIECBAgAABAgQIECBAgAABAgQIECBAwCDgBwgQIECAAAECBAgQIECAAAECBAgQIECAQEDAIBAoWUQCBAgQIECAAAECBAgQIECAAAECBAgQIGAQ8AMECBAgQIAAAQIECBAgQIAAAQIECBAgQCAgYBAIlCwiAQIECBAgQIAAAQIECBAgQIAAAQIECBAwCPgBAgQIECBAgAABAgQIECBAgAABAgQIECAQEDAIBEoWkQABAgQIECBAgAABAgQIECBAgAABAgQIGAT8AAECBAgQIECAAAECBAgQIECAAAECBAgQCAgYBAIli0iAAAECBAgQIECAAAECBAgQIECAAAECBAwCfoAAAQIECBAgQIAAAQIECBAgQIAAAQIECAQEDAKBkkUkQIAAAQIECBAgQIAAAQIECBAgQIAAAQIGAT9AgAABAgQIECBAgAABAgQIECBAgAABAgQCAgaBQMkiEiBAgAABAgQIECBAgAABAgQIECBAgAABg4AfIECAAAECBAgQIECAAAECBAgQIECAAAECAQGDQKBkEQkQIECAAAECBAgQIECAAAECBAgQIECAgEHADxAgQIAAAQIECBAgQIAAAQIECBAgQIAAgYCAQSBQsogECBAgQIAAAQIECBAgQIAAAQIECBAgQMAg4AcIECBAgAABAgQIECBAgAABAgQIECBAgEBAwCAQKFlEAgQIECBAgAABAgQIECBAgAABAgQIECBgEPADBAgQIECAAAECBAgQIECAAAECBAgQIEAgIGAQCJQsIgECBAgQIECAAAECBAgQIECAAAECBAgQMAj4AQIECBAgQIAAAQIECBAgQIAAAQIECBAgEBAwCARKFpEAAQIECBAgQIAAAQIECBAgQIAAAQIECFxa9FZdos1aNQAAAABJRU5ErkJggg==', - 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABgYAAACWCAYAAAAVHzNIAAATUUlEQVR4Xu3dMU+WZxQG4IdCkQ9DCKUxhdHJRQcNc2Pj5OS/8If4b9x0btKxCybyB1hq1JhUoi2lxdY279dASpM2mJyE3JzrmyC8HM5z3c92Jy8Lu7u7fw4fAgQIECBAgAABAgQIECBAgAABAgQIECBAoIXAgmKgRc4OSYAAAQIECBAgQIAAAQIECBAgQIAAAQIE5gKKAReBAAECBAgQIECAAAECBAgQIECAAAECBAg0ElAMNArbUQkQIECAAAECBAgQIECAAAECBAgQIECAgGLAHSBAgAABAgQIECBAgAABAgQIECBAgAABAo0EFAONwnZUAgQIECBAgAABAgQIECBAgAABAgQIECCgGHAHCBAgQIAAAQIECBAgQIAAAQIECBAgQIBAIwHFQKOwHZUAAQIECBAgQIAAAQIECBAgQIAAAQIECCgG3AECBAgQIECAAAECBAgQIECAAAECBAgQINBIQDHQKGxHJUCAAAECBAgQIECAAAECBAgQIECAAAECigF3gAABAgQIECBAgAABAgQIECBAgAABAgQINBJQDDQK21EJECBAgAABAgQIECBAgAABAgQIECBAgIBiwB0gQIAAAQIECBAgQIAAAQIECBAgQIAAAQKNBBQDjcJ2VAIECBAgQIAAAQIECBAgQIAAAQIECBAgoBhwBwgQIECAAAECBAgQIECAAAECBAgQIECAQCMBxUCjsB2VAAECBAgQIECAAAECBAgQIECAAAECBAgoBtwBAgQIECBAgAABAgQIECBAgAABAgQIECDQSEAx0ChsRyVAgAABAgQIECBAgAABAgQIECBAgAABAooBd4AAAQIECBAgQIAAAQIECBAgQIAAAQIECDQSUAw0CttRCRAgQIAAAQIECBAgQIAAAQIECBAgQICAYsAdIECAAAECBAgQIECAAAECBAgQIECAAAECjQQUA43CdlQCBAgQIECAAAECBAgQIECAAAECBAgQIKAYcAcIECBAgAABAgQIECBAgAABAgQIECBAgEAjAcVAo7AdlQABAgQIECBAgAABAgQIECBAgAABAgQIKAbcAQIECBAgQIAAAQIECBAgQIAAAQIECBAg0EhAMdAobEclQIAAAQIECBAgQIAAAQIECBAgQIAAAQKKAXeAAAECBAgQIECAAAECBAgQIECAAAECBAg0ElAMNArbUQkQIECAAAECBAgQIECAAAECBAgQIECAgGLAHSBAgAABAgQIECBAgAABAgQIECBAgAABAo0EFAONwnZUAgQIECBAgAABAgQIECBAgAABAgQIECCgGHAHCBAgQIAAAQIECBAgQIAAAQIECBAgQIBAIwHFQKOwHZUAAQIECBAgQIAAAQIECBAgQIAAAQIECCgG3AECBAgQIECAAAECBAgQIECAAAECBAgQINBIQDHQKGxHJUCAAAECBAgQIECAAAECBAgQIECAAAECigF3gAABAgQIECBAgAABAgQIECBAgAABAgQINBJQDDQK21EJECBAgAABAgQIECBAgAABAgQIECBAgIBiwB0gQIAAAQIECBAgQIAAAQIECBAgQIAAAQKNBBQDjcJ2VAIECBAgQIAAAQIECBAgQIAAAQIECBAgoBhwBwgQIECAAAECBAgQIECAAAECBAgQIECAQCMBxUCjsB2VAAECBAgQIECAAAECBAgQIECAAAECBAgoBtwBAgQIECBAgAABAgQIECBAgAABAgQIECDQSEAx0ChsRyVAgAABAgQIECBAgAABAgQIECBAgAABAooBd4AAAQIECBAgQIAAAQIECBAgQIAAAQIECDQSUAw0CttRCRAgQIAAAQIECBAgQIAAAQIECBAgQICAYsAdIECAAAECBAgQIECAAAECBAgQIECAAAECjQQUA43CdlQCBAgQIECAAAECBAgQIECAAAECBAgQIKAYcAcIECBAgAABAgQIECBAgAABAgQIECBAgEAjAcVAo7AdlQABAgQIECBAgAABAgQIECBAgAABAgQIKAbcAQIECBAgQIAAAQIECBAgQIAAAQIECBAg0EhAMTDG+PDxwzj8/XDs/7R/JvrVpdVxY/3GeHf8bqwvrze6Fo5KgAABAgQIECBAgAABAgQIECBAgAABApdVoH0x8Gjv0Xjyw5P/zHdhLJz52Z3NO/Pvd77cGTc3bo69t3tj48rGWFlcOX3u1S+vxtbq1tiebY9nPz6bf33ymS3OxsbyxpgtzcbWbGscHB+Mg98Oxsujl/Ny4v3x+/mj0+9cXbo67n5197LePeciQIAAAQIECBAgQIAAAQIECBAgQIAAgQsQaF0MvPn1zbj/7f0LYD/fn9xe3R5Pv3l6voc9RYAAAQIECBAgQIAAAQIECBAgQIAAAQIEziHQuhh4cfhiPPjuwTmYLuaR62vXx+OvH1/MH/dXCRAgQIAAAQIECBAgQIAAAQIECBAgQOBSCrQuBqZEH37/cP66n0/53N68PXY2d8atL26N52+f//1qoMXZ6YjptUDTa4Sm1wFNs6evTz5HfxzNXx90b+ve/PfmrxI6Pjh9fnoN0fT708/WPl8b11aufcpqniVAgAABAgQIECBAgAABAgQIECBAgAABAv8r0L4YmHReH70ey58tj/2fz/7z4elnJ/9T4J+K//6/A+4YAQIECBAgQIAAAQIECBAgQIAAAQIECBBIEVAMpCRlTwIECBAgQIAAAQIECBAgQIAAAQIECBAgUCCgGChANIIAAQIECBAgQIAAAQIECBAgQIAAAQIECKQIKAZSkrInAQIECBAgQIAAAQIECBAgQIAAAQIECBAoEFAMFCAaQYAAAQIECBAgQIAAAQIECBAgQIAAAQIEUgQUAylJ2ZMAAQIECBAgQIAAAQIECBAgQIAAAQIECBQIKAYKEI0gQIAAAQIECBAgQIAAAQIECBAgQIAAAQIpAoqBlKTsSYAAAQIECBAgQIAAAQIECBAgQIAAAQIECgQUAwWIRhAgQIAAAQIECBAgQIAAAQIECBAgQIAAgRQBxUBKUvYkQIAAAQIECBAgQIAAAQIECBAgQIAAAQIFAoqBAkQjCBAgQIAAAQIECBAgQIAAAQIECBAgQIBAioBiICUpexIgQIAAAQIECBAgQIAAAQIECBAgQIAAgQIBxUABohEECBAgQIAAAQIECBAgQIAAAQIECBAgQCBFQDGQkpQ9CRAgQIAAAQIECBAgQIAAAQIECBAgQIBAgYBioADRCAIECBAgQIAAAQIECBAgQIAAAQIECBAgkCKgGEhJyp4ECBAgQIAAAQIECBAgQIAAAQIECBAgQKBAQDFQgGgEAQIECBAgQIAAAQIECBAgQIAAAQIECBBIEVAMpCRlTwIECBAgQIAAAQIECBAgQIAAAQIECBAgUCCgGChANIIAAQIECBAgQIAAAQIECBAgQIAAAQIECKQIKAZSkrInAQIECBAgQIAAAQIECBAgQIAAAQIECBAoEFAMFCAaQYAAAQIECBAgQIAAAQIECBAgQIAAAQIEUgQUAylJ2ZMAAQIECBAgQIAAAQIECBAgQIAAAQIECBQIKAYKEI0gQIAAAQIECBAgQIAAAQIECBAgQIAAAQIpAoqBlKTsSYAAAQIECBAgQIAAAQIECBAgQIAAAQIECgQUAwWIRhAgQIAAAQIECBAgQIAAAQIECBAgQIAAgRQBxUBKUvYkQIAAAQIECBAgQIAAAQIECBAgQIAAAQIFAoqBAkQjCBAgQIAAAQIECBAgQIAAAQIECBAgQIBAioBiICUpexIgQIAAAQIECBAgQIAAAQIECBAgQIAAgQIBxUABohEECBAgQIAAAQIECBAgQIAAAQIECBAgQCBFQDGQkpQ9CRAgQIAAAQIECBAgQIAAAQIECBAgQIBAgYBioADRCAIECBAgQIAAAQIECBAgQIAAAQIECBAgkCKgGEhJyp4ECBAgQIAAAQIECBAgQIAAAQIECBAgQKBAQDFQgGgEAQIECBAgQIAAAQIECBAgQIAAAQIECBBIEVAMpCRlTwIECBAgQIAAAQIECBAgQIAAAQIECBAgUCCgGChANIIAAQIECBAgQIAAAQIECBAgQIAAAQIECKQIKAZSkrInAQIECBAgQIAAAQIECBAgQIAAAQIECBAoEFAMFCAaQYAAAQIECBAgQIAAAQIECBAgQIAAAQIEUgQUAylJ2ZMAAQIECBAgQIAAAQIECBAgQIAAAQIECBQIKAYKEI0gQIAAAQIECBAgQIAAAQIECBAgQIAAAQIpAoqBlKTsSYAAAQIECBAgQIAAAQIECBAgQIAAAQIECgQUAwWIRhAgQIAAAQIECBAgQIAAAQIECBAgQIAAgRQBxUBKUvYkQIAAAQIECBAgQIAAAQIECBAgQIAAAQIFAoqBAkQjCBAgQIAAAQIECBAgQIAAAQIECBAgQIBAioBiICUpexIgQIAAAQIECBAgQIAAAQIECBAgQIAAgQIBxUABohEECBAgQIAAAQIECBAgQIAAAQIECBAgQCBFQDGQkpQ9CRAgQIAAAQIECBAgQIAAAQIECBAgQIBAgYBioADRCAIECBAgQIAAAQIECBAgQIAAAQIECBAgkCKgGEhJyp4ECBAgQIAAAQIECBAgQIAAAQIECBAgQKBAQDFQgGgEAQIECBAgQIAAAQIECBAgQIAAAQIECBBIEVAMpCRlTwIECBAgQIAAAQIECBAgQIAAAQIECBAgUCCgGChANIIAAQIECBAgQIAAAQIECBAgQIAAAQIECKQIKAZSkrInAQIECBAgQIAAAQIECBAgQIAAAQIECBAoEFAMFCAaQYAAAQIECBAgQIAAAQIECBAgQIAAAQIEUgQUAylJ2ZMAAQIECBAgQIAAAQIECBAgQIAAAQIECBQIKAYKEI0gQIAAAQIECBAgQIAAAQIECBAgQIAAAQIpAoqBlKTsSYAAAQIECBAgQIAAAQIECBAgQIAAAQIECgQUAwWIRhAgQIAAAQIECBAgQIAAAQIECBAgQIAAgRQBxUBKUvYkQIAAAQIECBAgQIAAAQIECBAgQIAAAQIFAoqBAkQjCBAgQIAAAQIECBAgQIAAAQIECBAgQIBAioBiICUpexIgQIAAAQIECBAgQIAAAQIECBAgQIAAgQIBxUABohEECBAgQIAAAQIECBAgQIAAAQIECBAgQCBFQDGQkpQ9CRAgQIAAAQIECBAgQIAAAQIECBAgQIBAgYBioADRCAIECBAgQIAAAQIECBAgQIAAAQIECBAgkCKgGEhJyp4ECBAgQIAAAQIECBAgQIAAAQIECBAgQKBAQDFQgGgEAQIECBAgQIAAAQIECBAgQIAAAQIECBBIEVAMpCRlTwIECBAgQIAAAQIECBAgQIAAAQIECBAgUCCgGChANIIAAQIECBAgQIAAAQIECBAgQIAAAQIECKQIKAZSkrInAQIECBAgQIAAAQIECBAgQIAAAQIECBAoEFAMFCAaQYAAAQIECBAgQIAAAQIECBAgQIAAAQIEUgQUAylJ2ZMAAQIECBAgQIAAAQIECBAgQIAAAQIECBQIKAYKEI0gQIAAAQIECBAgQIAAAQIECBAgQIAAAQIpAoqBlKTsSYAAAQIECBAgQIAAAQIECBAgQIAAAQIECgQUAwWIRhAgQIAAAQIECBAgQIAAAQIECBAgQIAAgRQBxUBKUvYkQIAAAQIECBAgQIAAAQIECBAgQIAAAQIFAoqBAkQjCBAgQIAAAQIECBAgQIAAAQIECBAgQIBAioBiICUpexIgQIAAAQIECBAgQIAAAQIECBAgQIAAgQIBxUABohEECBAgQIAAAQIECBAgQIAAAQIECBAgQCBFQDGQkpQ9CRAgQIAAAQIECBAgQIAAAQIECBAgQIBAgYBioADRCAIECBAgQIAAAQIECBAgQIAAAQIECBAgkCKgGEhJyp4ECBAgQIAAAQIECBAgQIAAAQIECBAgQKBAQDFQgGgEAQIECBAgQIAAAQIECBAgQIAAAQIECBBIEVAMpCRlTwIECBAgQIAAAQIECBAgQIAAAQIECBAgUCCgGChANIIAAQIECBAgQIAAAQIECBAgQIAAAQIECKQIKAZSkrInAQIECBAgQIAAAQIECBAgQIAAAQIECBAoEFAMFCAaQYAAAQIECBAgQIAAAQIECBAgQIAAAQIEUgQUAylJ2ZMAAQIECBAgQIAAAQIECBAgQIAAAQIECBQIKAYKEI0gQIAAAQIECBAgQIAAAQIECBAgQIAAAQIpAoqBlKTsSYAAAQIECBAgQIAAAQIECBAgQIAAAQIECgQUAwWIRhAgQIAAAQIECBAgQIAAAQIECBAgQIAAgRQBxUBKUvYkQIAAAQIECBAgQIAAAQIECBAgQIAAAQIFAoqBAkQjCBAgQIAAAQIECBAgQIAAAQIECBAgQIBAioBiICUpexIgQIAAAQIECBAgQIAAAQIECBAgQIAAgQIBxUABohEECBAgQIAAAQIECBAgQIAAAQIECBAgQCBFQDGQkpQ9CRAgQIAAAQIECBAgQIAAAQIECBAgQIBAgYBioADRCAIECBAgQIAAAQIECBAgQIAAAQIECBAgkCKgGEhJyp4ECBAgQIAAAQIECBAgQIAAAQIECBAgQKBA4C+R01m3EQ6wBwAAAABJRU5ErkJggg==', + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABgIAAACWCAYAAAAc9JMyAAATVUlEQVR4Xu3dPWvVZxzH4TuCD1AzJEOdjEurIDgU6dKKuBUXqSL4brr4IjrVxRchdJRuQqVvQOpTl8QpEbVpmnKOdXMQ4cs33OdyCUbz+537uv9TPnDO2uvX24fDHwIECBAgQIAAAQIECBAgQIAAAQIECBAgQGBKgTUhYMp7dSgCBAgQIECAAAECBAgQIECAAAECBAgQILAUEAI8CAQIECBAgAABAgQIECBAgAABAgQIECBAYGIBIWDiy3U0AgQIECBAgAABAgQIECBAgAABAgQIECAgBHgGCBAgQIAAAQIECBAgQIAAAQIECBAgQIDAxAJCwMSX62gECBAgQIAAAQIECBAgQIAAAQIECBAgQEAI8AwQIECAAAECBAgQIECAAAECBAgQIECAAIGJBYSAiS/X0QgQIECAAAECBAgQIECAAAECBAgQIECAgBDgGSBAgAABAgQIECBAgAABAgQIECBAgAABAhMLCAETX66jESBAgAABAgQIECBAgAABAgQIECBAgAABIcAzQIAAAQIECBAgQIAAAQIECBAgQIAAAQIEJhYQAia+XEcjQIAAAQIECBAgQIAAAQIECBAgQIAAAQJCgGeAAAECBAgQIECAAAECBAgQIECAAAECBAhMLCAETHy5jkaAAAECBAgQIECAAAECBAgQIECAAAECBIQAzwABAgQIECBAgAABAgQIECBAgAABAgQIEJhYQAiY+HIdjQABAgQIECBAgAABAgQIECBAgAABAgQICAGeAQIECBAgQIAAAQIECBAgQIAAAQIECBAgMLGAEDDx5ToaAQIECBAgQIAAAQIECBAgQIAAAQIECBAQAjwDBAgQIECAAAECBAgQIECAAAECBAgQIEBgYgEhYOLLdTQCBAgQIECAAAECBAgQIECAAAECBAgQICAEeAYIECBAgAABAgQIECBAgAABAgQIECBAgMDEAkLAxJfraAQIECBAgAABAgQIECBAgAABAgQIECBAQAjwDBAgQIAAAQIECBAgQIAAAQIECBAgQIAAgYkFhICJL9fRCBAgQIAAAQIECBAgQIAAAQIECBAgQICAEOAZIECAAAECBAgQIECAAAECBAgQIECAAAECEwsIARNfrqMRIECAAAECBAgQIECAAAECBAgQIECAAAEhwDNAgAABAgQIECBAgAABAgQIECBAgAABAgQmFhACJr5cRyNAgAABAgQIECBAgAABAgQIECBAgAABAkKAZ4AAAQIECBAgQIAAAQIECBAgQIAAAQIECEwsIARMfLmORoAAAQIECBAgQIAAAQIECBAgQIAAAQIEhADPAAECBAgQIECAAAECBAgQIECAAAECBAgQmFhACJj4ch2NAAECBAgQIECAAAECBAgQIECAAAECBAgIAZ4BAgQIECBAgAABAgQIECBAgAABAgQIECAwscDKhoCXL/8ap06dHNvbr8bOzs7yik+fPr38ure3t/x65cp3E1+9oxEgQIAAAQIECBAgQIAAAQIECBAgQIDAKgisZAi4fv3mePjwt4/e79ra2jg8PFz+2/r66XHs2LGxvr4+3r37exkMzp07OzY3N/8PB1+M/f1/xoULX4+Dg4Pl906cODGeP38xdnf3lt/f3d1dxobFz21tnR2PH/8x9vZeL/9++fI34/btH8fGxsYqPGvOSIAAAQIECBAgQIAAAQIECBAgQIAAAQIFgZULAU+e/DkuXfq2QP3xlffu/Tzu3Ll9ZF6PF0KAAAECBAgQIECAAAECBAgQIECAAAECcwmsXAh4+vTZuHjx8pG5xfv3fxm3bt04Mq/HCyFAgAABAgQIECBAgAABAgQIECBAgACBuQRWLgQsru/atR/Go0e/f9JNHj9+fJw58+V4+/bt2Nl5tXx7n83NjbF4C6HFZwrs7++P8+e/GgcH/y7nnTx5Yjx79mL5OQOL7y/eImjxlkJbW1vLtwN6/9ZAe+PFi5fj5s0b4+7dnz7pdfhPBAgQIECAAAECBAgQIECAAAECBAgQIEDgcwRWMgQsoB48+HW8ebP45f7O2N5+/2HBV69+v/zl/ocPC/7wy/vPgfUzBAgQIECAAAECBAgQIECAAAECBAgQIEDgKAisbAg4CvheAwECBAgQIECAAAECBAgQIECAAAECBAgQSAsIAWlh8wkQIECAAAECBAgQIECAAAECBAgQIECAQFFACCjiW02AAAECBAgQIECAAAECBAgQIECAAAECBNICQkBa2HwCBAgQIECAAAECBAgQIECAAAECBAgQIFAUEAKK+FYTIECAAAECBAgQIECAAAECBAgQIECAAIG0gBCQFjafAAECBAgQIECAAAECBAgQIECAAAECBAgUBYSAIr7VBAgQIECAAAECBAgQIECAAAECBAgQIEAgLSAEpIXNJ0CAAAECBAgQIECAAAECBAgQIECAAAECRQEhoIhvNQECBAgQIECAAAECBAgQIECAAAECBAgQSAsIAWlh8wkQIECAAAECBAgQIECAAAECBAgQIECAQFFACCjiW02AAAECBAgQIECAAAECBAgQIECAAAECBNICQkBa2HwCBAgQIECAAAECBAgQIECAAAECBAgQIFAUEAKK+FYTIECAAAECBAgQIECAAAECBAgQIECAAIG0gBCQFjafAAECBAgQIECAAAECBAgQIECAAAECBAgUBYSAIr7VBAgQIECAAAECBAgQIECAAAECBAgQIEAgLSAEpIXNJ0CAAAECBAgQIECAAAECBAgQIECAAAECRQEhoIhvNQECBAgQIECAAAECBAgQIECAAAECBAgQSAsIAWlh8wkQIECAAAECBAgQIECAAAECBAgQIECAQFFACCjiW02AAAECBAgQIECAAAECBAgQIECAAAECBNICQkBa2HwCBAgQIECAAAECBAgQIECAAAECBAgQIFAUEAKK+FYTIECAAAECBAgQIECAAAECBAgQIECAAIG0gBCQFjafAAECBAgQIECAAAECBAgQIECAAAECBAgUBYSAIr7VBAgQIECAAAECBAgQIECAAAECBAgQIEAgLSAEpIXNJ0CAAAECBAgQIECAAAECBAgQIECAAAECRQEhoIhvNQECBAgQIECAAAECBAgQIECAAAECBAgQSAsIAWlh8wkQIECAAAECBAgQIECAAAECBAgQIECAQFFACCjiW02AAAECBAgQIECAAAECBAgQIECAAAECBNICQkBa2HwCBAgQIECAAAECBAgQIECAAAECBAgQIFAUEAKK+FYTIECAAAECBAgQIECAAAECBAgQIECAAIG0gBCQFjafAAECBAgQIECAAAECBAgQIECAAAECBAgUBYSAIr7VBAgQIECAAAECBAgQIECAAAECBAgQIEAgLSAEpIXNJ0CAAAECBAgQIECAAAECBAgQIECAAAECRQEhoIhvNQECBAgQIECAAAECBAgQIECAAAECBAgQSAsIAWlh8wkQIECAAAECBAgQIECAAAECBAgQIECAQFFACCjiW02AAAECBAgQIECAAAECBAgQIECAAAECBNICQkBa2HwCBAgQIECAAAECBAgQIECAAAECBAgQIFAUEAKK+FYTIECAAAECBAgQIECAAAECBAgQIECAAIG0gBCQFjafAAECBAgQIECAAAECBAgQIECAAAECBAgUBYSAIr7VBAgQIECAAAECBAgQIECAAAECBAgQIEAgLSAEpIXNJ0CAAAECBAgQIECAAAECBAgQIECAAAECRQEhoIhvNQECBAgQIECAAAECBAgQIECAAAECBAgQSAsIAWlh8wkQIECAAAECBAgQIECAAAECBAgQIECAQFFACCjiW02AAAECBAgQIECAAAECBAgQIECAAAECBNICQkBa2HwCBAgQIECAAAECBAgQIECAAAECBAgQIFAUEAKK+FYTIECAAAECBAgQIECAAAECBAgQIECAAIG0gBCQFjafAAECBAgQIECAAAECBAgQIECAAAECBAgUBYSAIr7VBAgQIECAAAECBAgQIECAAAECBAgQIEAgLSAEpIXNJ0CAAAECBAgQIECAAAECBAgQIECAAAECRQEhoIhvNQECBAgQIECAAAECBAgQIECAAAECBAgQSAsIAWlh8wkQIECAAAECBAgQIECAAAECBAgQIECAQFFACCjiW02AAAECBAgQIECAAAECBAgQIECAAAECBNICQkBa2HwCBAgQIECAAAECBAgQIECAAAECBAgQIFAUEAKK+FYTIECAAAECBAgQIECAAAECBAgQIECAAIG0gBCQFjafAAECBAgQIECAAAECBAgQIECAAAECBAgUBYSAIr7VBAgQIECAAAECBAgQIECAAAECBAgQIEAgLSAEpIXNJ0CAAAECBAgQIECAAAECBAgQIECAAAECRQEhoIhvNQECBAgQIECAAAECBAgQIECAAAECBAgQSAsIAWlh8wkQIECAAAECBAgQIECAAAECBAgQIECAQFFACCjiW02AAAECBAgQIECAAAECBAgQIECAAAECBNICQkBa2HwCBAgQIECAAAECBAgQIECAAAECBAgQIFAUEAKK+FYTIECAAAECBAgQIECAAAECBAgQIECAAIG0gBCQFjafAAECBAgQIECAAAECBAgQIECAAAECBAgUBYSAIr7VBAgQIECAAAECBAgQIECAAAECBAgQIEAgLSAEpIXNJ0CAAAECBAgQIECAAAECBAgQIECAAAECRQEhoIhvNQECBAgQIECAAAECBAgQIECAAAECBAgQSAsIAWlh8wkQIECAAAECBAgQIECAAAECBAgQIECAQFFACCjiW02AAAECBAgQIECAAAECBAgQIECAAAECBNICQkBa2HwCBAgQIECAAAECBAgQIECAAAECBAgQIFAUEAKK+FYTIECAAAECBAgQIECAAAECBAgQIECAAIG0gBCQFjafAAECBAgQIECAAAECBAgQIECAAAECBAgUBYSAIr7VBAgQIECAAAECBAgQIECAAAECBAgQIEAgLSAEpIXNJ0CAAAECBAgQIECAAAECBAgQIECAAAECRQEhoIhvNQECBAgQIECAAAECBAgQIECAAAECBAgQSAsIAWlh8wkQIECAAAECBAgQIECAAAECBAgQIECAQFFACCjiW02AAAECBAgQIECAAAECBAgQIECAAAECBNICQkBa2HwCBAgQIECAAAECBAgQIECAAAECBAgQIFAUEAKK+FYTIECAAAECBAgQIECAAAECBAgQIECAAIG0gBCQFjafAAECBAgQIECAAAECBAgQIECAAAECBAgUBYSAIr7VBAgQIECAAAECBAgQIECAAAECBAgQIEAgLSAEpIXNJ0CAAAECBAgQIECAAAECBAgQIECAAAECRQEhoIhvNQECBAgQIECAAAECBAgQIECAAAECBAgQSAsIAWlh8wkQIECAAAECBAgQIECAAAECBAgQIECAQFFACCjiW02AAAECBAgQIECAAAECBAgQIECAAAECBNICQkBa2HwCBAgQIECAAAECBAgQIECAAAECBAgQIFAUEAKK+FYTIECAAAECBAgQIECAAAECBAgQIECAAIG0gBCQFjafAAECBAgQIECAAAECBAgQIECAAAECBAgUBYSAIr7VBAgQIECAAAECBAgQIECAAAECBAgQIEAgLSAEpIXNJ0CAAAECBAgQIECAAAECBAgQIECAAAECRQEhoIhvNQECBAgQIECAAAECBAgQIECAAAECBAgQSAsIAWlh8wkQIECAAAECBAgQIECAAAECBAgQIECAQFFACCjiW02AAAECBAgQIECAAAECBAgQIECAAAECBNICQkBa2HwCBAgQIECAAAECBAgQIECAAAECBAgQIFAUEAKK+FYTIECAAAECBAgQIECAAAECBAgQIECAAIG0gBCQFjafAAECBAgQIECAAAECBAgQIECAAAECBAgUBYSAIr7VBAgQIECAAAECBAgQIECAAAECBAgQIEAgLSAEpIXNJ0CAAAECBAgQIECAAAECBAgQIECAAAECRQEhoIhvNQECBAgQIECAAAECBAgQIECAAAECBAgQSAsIAWlh8wkQIECAAAECBAgQIECAAAECBAgQIECAQFFACCjiW02AAAECBAgQIECAAAECBAgQIECAAAECBNICQkBa2HwCBAgQIECAAAECBAgQIECAAAECBAgQIFAUEAKK+FYTIECAAAECBAgQIECAAAECBAgQIECAAIG0gBCQFjafAAECBAgQIECAAAECBAgQIECAAAECBAgUBYSAIr7VBAgQIECAAAECBAgQIECAAAECBAgQIEAgLSAEpIXNJ0CAAAECBAgQIECAAAECBAgQIECAAAECRQEhoIhvNQECBAgQIECAAAECBAgQIECAAAECBAgQSAsIAWlh8wkQIECAAAECBAgQIECAAAECBAgQIECAQFFACCjiW02AAAECBAgQIECAAAECBAgQIECAAAECBNICQkBa2HwCBAgQIECAAAECBAgQIECAAAECBAgQIFAUEAKK+FYTIECAAAECBAgQIECAAAECBAgQIECAAIG0gBCQFjafAAECBAgQIECAAAECBAgQIECAAAECBAgUBYSAIr7VBAgQIECAAAECBAgQIECAAAECBAgQIEAgLfAfpsyz7fPE9GYAAAAASUVORK5CYII=', + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABgQAAACWCAYAAAAR6uN1AAARFElEQVR4Xu3ZQQ0AMAwDsZU/6E4ajJ3LIE5/md3d4wgQIECAAAECBAgQIECAAAECBAgQIECAAIGvBcYg8HW/whEgQIAAAQIECBAgQIAAAQIECBAgQIAAgSdgEPAIBAgQIECAAAECBAgQIECAAAECBAgQIEAgIGAQCJQsIgECBAgQIECAAAECBAgQIECAAAECBAgQMAj4AQIECBAgQIAAAQIECBAgQIAAAQIECBAgEBAwCARKFpEAAQIECBAgQIAAAQIECBAgQIAAAQIECBgE/AABAgQIECBAgAABAgQIECBAgAABAgQIEAgIGAQCJYtIgAABAgQIECBAgAABAgQIECBAgAABAgQMAn6AAAECBAgQIECAAAECBAgQIECAAAECBAgEBAwCgZJFJECAAAECBAgQIECAAAECBAgQIECAAAECBgE/QIAAAQIECBAgQIAAAQIECBAgQIAAAQIEAgIGgUDJIhIgQIAAAQIECBAgQIAAAQIECBAgQIAAAYOAHyBAgAABAgQIECBAgAABAgQIECBAgAABAgEBg0CgZBEJECBAgAABAgQIECBAgAABAgQIECBAgIBBwA8QIECAAAECBAgQIECAAAECBAgQIECAAIGAgEEgULKIBAgQIECAAAECBAgQIECAAAECBAgQIEDAIOAHCBAgQIAAAQIECBAgQIAAAQIECBAgQIBAQMAgEChZRAIECBAgQIAAAQIECBAgQIAAAQIECBAgYBDwAwQIECBAgAABAgQIECBAgAABAgQIECBAICBgEAiULCIBAgQIECBAgAABAgQIECBAgAABAgQIEDAI+AECBAgQIECAAAECBAgQIECAAAECBAgQIBAQMAgEShaRAAECBAgQIECAAAECBAgQIECAAAECBAgYBPwAAQIECBAgQIAAAQIECBAgQIAAAQIECBAICBgEAiWLSIAAAQIECBAgQIAAAQIECBAgQIAAAQIEDAJ+gAABAgQIECBAgAABAgQIECBAgAABAgQIBAQMAoGSRSRAgAABAgQIECBAgAABAgQIECBAgAABAgYBP0CAAAECBAgQIECAAAECBAgQIECAAAECBAICBoFAySISIECAAAECBAgQIECAAAECBAgQIECAAAGDgB8gQIAAAQIECBAgQIAAAQIECBAgQIAAAQIBAYNAoGQRCRAgQIAAAQIECBAgQIAAAQIECBAgQICAQcAPECBAgAABAgQIECBAgAABAgQIECBAgACBgIBBIFCyiAQIECBAgAABAgQIECBAgAABAgQIECBAwCDgBwgQIECAAAECBAgQIECAAAECBAgQIECAQEDAIBAoWUQCBAgQIECAAAECBAgQIECAAAECBAgQIGAQ8AMECBAgQIAAAQIECBAgQIAAAQIECBAgQCAgYBAIlCwiAQIECBAgQIAAAQIECBAgQIAAAQIECBAwCPgBAgQIECBAgAABAgQIECBAgAABAgQIECAQEDAIBEoWkQABAgQIECBAgAABAgQIECBAgAABAgQIGAT8AAECBAgQIECAAAECBAgQIECAAAECBAgQCAgYBAIli0iAAAECBAgQIECAAAECBAgQIECAAAECBAwCfoAAAQIECBAgQIAAAQIECBAgQIAAAQIECAQEDAKBkkUkQIAAAQIECBAgQIAAAQIECBAgQIAAAQIGAT9AgAABAgQIECBAgAABAgQIECBAgAABAgQCAgaBQMkiEiBAgAABAgQIECBAgAABAgQIECBAgAABg4AfIECAAAECBAgQIECAAAECBAgQIECAAAECAQGDQKBkEQkQIECAAAECBAgQIECAAAECBAgQIECAgEHADxAgQIAAAQIECBAgQIAAAQIECBAgQIAAgYCAQSBQsogECBAgQIAAAQIECBAgQIAAAQIECBAgQMAg4AcIECBAgAABAgQIECBAgAABAgQIECBAgEBAwCAQKFlEAgQIECBAgAABAgQIECBAgAABAgQIECBgEPADBAgQIECAAAECBAgQIECAAAECBAgQIEAgIGAQCJQsIgECBAgQIECAAAECBAgQIECAAAECBAgQMAj4AQIECBAgQIAAAQIECBAgQIAAAQIECBAgEBAwCARKFpEAAQIECBAgQIAAAQIECBAgQIAAAQIECBgE/AABAgQIECBAgAABAgQIECBAgAABAgQIEAgIGAQCJYtIgAABAgQIECBAgAABAgQIECBAgAABAgQMAn6AAAECBAgQIECAAAECBAgQIECAAAECBAgEBAwCgZJFJECAAAECBAgQIECAAAECBAgQIECAAAECBgE/QIAAAQIECBAgQIAAAQIECBAgQIAAAQIEAgIGgUDJIhIgQIAAAQIECBAgQIAAAQIECBAgQIAAAYOAHyBAgAABAgQIECBAgAABAgQIECBAgAABAgEBg0CgZBEJECBAgAABAgQIECBAgAABAgQIECBAgIBBwA8QIECAAAECBAgQIECAAAECBAgQIECAAIGAgEEgULKIBAgQIECAAAECBAgQIECAAAECBAgQIEDAIOAHCBAgQIAAAQIECBAgQIAAAQIECBAgQIBAQMAgEChZRAIECBAgQIAAAQIECBAgQIAAAQIECBAgYBDwAwQIECBAgAABAgQIECBAgAABAgQIECBAICBgEAiULCIBAgQIECBAgAABAgQIECBAgAABAgQIEDAI+AECBAgQIECAAAECBAgQIECAAAECBAgQIBAQMAgEShaRAAECBAgQIECAAAECBAgQIECAAAECBAgYBPwAAQIECBAgQIAAAQIECBAgQIAAAQIECBAICBgEAiWLSIAAAQIECBAgQIAAAQIECBAgQIAAAQIEDAJ+gAABAgQIECBAgAABAgQIECBAgAABAgQIBAQMAoGSRSRAgAABAgQIECBAgAABAgQIECBAgAABAgYBP0CAAAECBAgQIECAAAECBAgQIECAAAECBAICBoFAySISIECAAAECBAgQIECAAAECBAgQIECAAAGDgB8gQIAAAQIECBAgQIAAAQIECBAgQIAAAQIBAYNAoGQRCRAgQIAAAQIECBAgQIAAAQIECBAgQICAQcAPECBAgAABAgQIECBAgAABAgQIECBAgACBgIBBIFCyiAQIECBAgAABAgQIECBAgAABAgQIECBAwCDgBwgQIECAAAECBAgQIECAAAECBAgQIECAQEDAIBAoWUQCBAgQIECAAAECBAgQIECAAAECBAgQIGAQ8AMECBAgQIAAAQIECBAgQIAAAQIECBAgQCAgYBAIlCwiAQIECBAgQIAAAQIECBAgQIAAAQIECBAwCPgBAgQIECBAgAABAgQIECBAgAABAgQIECAQEDAIBEoWkQABAgQIECBAgAABAgQIECBAgAABAgQIGAT8AAECBAgQIECAAAECBAgQIECAAAECBAgQCAgYBAIli0iAAAECBAgQIECAAAECBAgQIECAAAECBAwCfoAAAQIECBAgQIAAAQIECBAgQIAAAQIECAQEDAKBkkUkQIAAAQIECBAgQIAAAQIECBAgQIAAAQIGAT9AgAABAgQIECBAgAABAgQIECBAgAABAgQCAgaBQMkiEiBAgAABAgQIECBAgAABAgQIECBAgAABg4AfIECAAAECBAgQIECAAAECBAgQIECAAAECAQGDQKBkEQkQIECAAAECBAgQIECAAAECBAgQIECAgEHADxAgQIAAAQIECBAgQIAAAQIECBAgQIAAgYCAQSBQsogECBAgQIAAAQIECBAgQIAAAQIECBAgQMAg4AcIECBAgAABAgQIECBAgAABAgQIECBAgEBAwCAQKFlEAgQIECBAgAABAgQIECBAgAABAgQIECBgEPADBAgQIECAAAECBAgQIECAAAECBAgQIEAgIGAQCJQsIgECBAgQIECAAAECBAgQIECAAAECBAgQMAj4AQIECBAgQIAAAQIECBAgQIAAAQIECBAgEBAwCARKFpEAAQIECBAgQIAAAQIECBAgQIAAAQIECBgE/AABAgQIECBAgAABAgQIECBAgAABAgQIEAgIGAQCJYtIgAABAgQIECBAgAABAgQIECBAgAABAgQMAn6AAAECBAgQIECAAAECBAgQIECAAAECBAgEBAwCgZJFJECAAAECBAgQIECAAAECBAgQIECAAAECBgE/QIAAAQIECBAgQIAAAQIECBAgQIAAAQIEAgIGgUDJIhIgQIAAAQIECBAgQIAAAQIECBAgQIAAAYOAHyBAgAABAgQIECBAgAABAgQIECBAgAABAgEBg0CgZBEJECBAgAABAgQIECBAgAABAgQIECBAgIBBwA8QIECAAAECBAgQIECAAAECBAgQIECAAIGAgEEgULKIBAgQIECAAAECBAgQIECAAAECBAgQIEDAIOAHCBAgQIAAAQIECBAgQIAAAQIECBAgQIBAQMAgEChZRAIECBAgQIAAAQIECBAgQIAAAQIECBAgYBDwAwQIECBAgAABAgQIECBAgAABAgQIECBAICBgEAiULCIBAgQIECBAgAABAgQIECBAgAABAgQIEDAI+AECBAgQIECAAAECBAgQIECAAAECBAgQIBAQMAgEShaRAAECBAgQIECAAAECBAgQIECAAAECBAgYBPwAAQIECBAgQIAAAQIECBAgQIAAAQIECBAICBgEAiWLSIAAAQIECBAgQIAAAQIECBAgQIAAAQIEDAJ+gAABAgQIECBAgAABAgQIECBAgAABAgQIBAQMAoGSRSRAgAABAgQIECBAgAABAgQIECBAgAABAgYBP0CAAAECBAgQIECAAAECBAgQIECAAAECBAICBoFAySISIECAAAECBAgQIECAAAECBAgQIECAAAGDgB8gQIAAAQIECBAgQIAAAQIECBAgQIAAAQIBAYNAoGQRCRAgQIAAAQIECBAgQIAAAQIECBAgQICAQcAPECBAgAABAgQIECBAgAABAgQIECBAgACBgIBBIFCyiAQIECBAgAABAgQIECBAgAABAgQIECBAwCDgBwgQIECAAAECBAgQIECAAAECBAgQIECAQEDAIBAoWUQCBAgQIECAAAECBAgQIECAAAECBAgQIGAQ8AMECBAgQIAAAQIECBAgQIAAAQIECBAgQCAgYBAIlCwiAQIECBAgQIAAAQIECBAgQIAAAQIECBAwCPgBAgQIECBAgAABAgQIECBAgAABAgQIECAQEDAIBEoWkQABAgQIECBAgAABAgQIECBAgAABAgQIGAT8AAECBAgQIECAAAECBAgQIECAAAECBAgQCAgYBAIli0iAAAECBAgQIECAAAECBAgQIECAAAECBAwCfoAAAQIECBAgQIAAAQIECBAgQIAAAQIECAQEDAKBkkUkQIAAAQIECBAgQIAAAQIECBAgQIAAAQIGAT9AgAABAgQIECBAgAABAgQIECBAgAABAgQCAgaBQMkiEiBAgAABAgQIECBAgAABAgQIECBAgAABg4AfIECAAAECBAgQIECAAAECBAgQIECAAAECAQGDQKBkEQkQIECAAAECBAgQIECAAAECBAgQIECAgEHADxAgQIAAAQIECBAgQIAAAQIECBAgQIAAgYCAQSBQsogECBAgQIAAAQIECBAgQIAAAQIECBAgQMAg4AcIECBAgAABAgQIECBAgAABAgQIECBAgEBAwCAQKFlEAgQIECBAgAABAgQIECBAgAABAgQIECBgEPADBAgQIECAAAECBAgQIECAAAECBAgQIEAgIGAQCJQsIgECBAgQIECAAAECBAgQIECAAAECBAgQMAj4AQIECBAgQIAAAQIECBAgQIAAAQIECBAgEBAwCARKFpEAAQIECBAgQIAAAQIECBAgQIAAAQIECFxa9FZdos1aNQAAAABJRU5ErkJggg==', + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABgYAAACWCAYAAAAVHzNIAAATUUlEQVR4Xu3dMU+WZxQG4IdCkQ9DCKUxhdHJRQcNc2Pj5OS/8If4b9x0btKxCybyB1hq1JhUoi2lxdY279dASpM2mJyE3JzrmyC8HM5z3c92Jy8Lu7u7fw4fAgQIECBAgAABAgQIECBAgAABAgQIECBAoIXAgmKgRc4OSYAAAQIECBAgQIAAAQIECBAgQIAAAQIE5gKKAReBAAECBAgQIECAAAECBAgQIECAAAECBAg0ElAMNArbUQkQIECAAAECBAgQIECAAAECBAgQIECAgGLAHSBAgAABAgQIECBAgAABAgQIECBAgAABAo0EFAONwnZUAgQIECBAgAABAgQIECBAgAABAgQIECCgGHAHCBAgQIAAAQIECBAgQIAAAQIECBAgQIBAIwHFQKOwHZUAAQIECBAgQIAAAQIECBAgQIAAAQIECCgG3AECBAgQIECAAAECBAgQIECAAAECBAgQINBIQDHQKGxHJUCAAAECBAgQIECAAAECBAgQIECAAAECigF3gAABAgQIECBAgAABAgQIECBAgAABAgQINBJQDDQK21EJECBAgAABAgQIECBAgAABAgQIECBAgIBiwB0gQIAAAQIECBAgQIAAAQIECBAgQIAAAQKNBBQDjcJ2VAIECBAgQIAAAQIECBAgQIAAAQIECBAgoBhwBwgQIECAAAECBAgQIECAAAECBAgQIECAQCMBxUCjsB2VAAECBAgQIECAAAECBAgQIECAAAECBAgoBtwBAgQIECBAgAABAgQIECBAgAABAgQIECDQSEAx0ChsRyVAgAABAgQIECBAgAABAgQIECBAgAABAooBd4AAAQIECBAgQIAAAQIECBAgQIAAAQIECDQSUAw0CttRCRAgQIAAAQIECBAgQIAAAQIECBAgQICAYsAdIECAAAECBAgQIECAAAECBAgQIECAAAECjQQUA43CdlQCBAgQIECAAAECBAgQIECAAAECBAgQIKAYcAcIECBAgAABAgQIECBAgAABAgQIECBAgEAjAcVAo7AdlQABAgQIECBAgAABAgQIECBAgAABAgQIKAbcAQIECBAgQIAAAQIECBAgQIAAAQIECBAg0EhAMdAobEclQIAAAQIECBAgQIAAAQIECBAgQIAAAQKKAXeAAAECBAgQIECAAAECBAgQIECAAAECBAg0ElAMNArbUQkQIECAAAECBAgQIECAAAECBAgQIECAgGLAHSBAgAABAgQIECBAgAABAgQIECBAgAABAo0EFAONwnZUAgQIECBAgAABAgQIECBAgAABAgQIECCgGHAHCBAgQIAAAQIECBAgQIAAAQIECBAgQIBAIwHFQKOwHZUAAQIECBAgQIAAAQIECBAgQIAAAQIECCgG3AECBAgQIECAAAECBAgQIECAAAECBAgQINBIQDHQKGxHJUCAAAECBAgQIECAAAECBAgQIECAAAECigF3gAABAgQIECBAgAABAgQIECBAgAABAgQINBJQDDQK21EJECBAgAABAgQIECBAgAABAgQIECBAgIBiwB0gQIAAAQIECBAgQIAAAQIECBAgQIAAAQKNBBQDjcJ2VAIECBAgQIAAAQIECBAgQIAAAQIECBAgoBhwBwgQIECAAAECBAgQIECAAAECBAgQIECAQCMBxUCjsB2VAAECBAgQIECAAAECBAgQIECAAAECBAgoBtwBAgQIECBAgAABAgQIECBAgAABAgQIECDQSEAx0ChsRyVAgAABAgQIECBAgAABAgQIECBAgAABAooBd4AAAQIECBAgQIAAAQIECBAgQIAAAQIECDQSUAw0CttRCRAgQIAAAQIECBAgQIAAAQIECBAgQICAYsAdIECAAAECBAgQIECAAAECBAgQIECAAAECjQQUA43CdlQCBAgQIECAAAECBAgQIECAAAECBAgQIKAYcAcIECBAgAABAgQIECBAgAABAgQIECBAgEAjAcVAo7AdlQABAgQIECBAgAABAgQIECBAgAABAgQIKAbcAQIECBAgQIAAAQIECBAgQIAAAQIECBAg0EhAMTDG+PDxwzj8/XDs/7R/JvrVpdVxY/3GeHf8bqwvrze6Fo5KgAABAgQIECBAgAABAgQIECBAgAABApdVoH0x8Gjv0Xjyw5P/zHdhLJz52Z3NO/Pvd77cGTc3bo69t3tj48rGWFlcOX3u1S+vxtbq1tiebY9nPz6bf33ymS3OxsbyxpgtzcbWbGscHB+Mg98Oxsujl/Ny4v3x+/mj0+9cXbo67n5197LePeciQIAAAQIECBAgQIAAAQIECBAgQIAAgQsQaF0MvPn1zbj/7f0LYD/fn9xe3R5Pv3l6voc9RYAAAQIECBAgQIAAAQIECBAgQIAAAQIEziHQuhh4cfhiPPjuwTmYLuaR62vXx+OvH1/MH/dXCRAgQIAAAQIECBAgQIAAAQIECBAgQOBSCrQuBqZEH37/cP66n0/53N68PXY2d8atL26N52+f//1qoMXZ6YjptUDTa4Sm1wFNs6evTz5HfxzNXx90b+ve/PfmrxI6Pjh9fnoN0fT708/WPl8b11aufcpqniVAgAABAgQIECBAgAABAgQIECBAgAABAv8r0L4YmHReH70ey58tj/2fz/7z4elnJ/9T4J+K//6/A+4YAQIECBAgQIAAAQIECBAgQIAAAQIECBBIEVAMpCRlTwIECBAgQIAAAQIECBAgQIAAAQIECBAgUCCgGChANIIAAQIECBAgQIAAAQIECBAgQIAAAQIECKQIKAZSkrInAQIECBAgQIAAAQIECBAgQIAAAQIECBAoEFAMFCAaQYAAAQIECBAgQIAAAQIECBAgQIAAAQIEUgQUAylJ2ZMAAQIECBAgQIAAAQIECBAgQIAAAQIECBQIKAYKEI0gQIAAAQIECBAgQIAAAQIECBAgQIAAAQIpAoqBlKTsSYAAAQIECBAgQIAAAQIECBAgQIAAAQIECgQUAwWIRhAgQIAAAQIECBAgQIAAAQIECBAgQIAAgRQBxUBKUvYkQIAAAQIECBAgQIAAAQIECBAgQIAAAQIFAoqBAkQjCBAgQIAAAQIECBAgQIAAAQIECBAgQIBAioBiICUpexIgQIAAAQIECBAgQIAAAQIECBAgQIAAgQIBxUABohEECBAgQIAAAQIECBAgQIAAAQIECBAgQCBFQDGQkpQ9CRAgQIAAAQIECBAgQIAAAQIECBAgQIBAgYBioADRCAIECBAgQIAAAQIECBAgQIAAAQIECBAgkCKgGEhJyp4ECBAgQIAAAQIECBAgQIAAAQIECBAgQKBAQDFQgGgEAQIECBAgQIAAAQIECBAgQIAAAQIECBBIEVAMpCRlTwIECBAgQIAAAQIECBAgQIAAAQIECBAgUCCgGChANIIAAQIECBAgQIAAAQIECBAgQIAAAQIECKQIKAZSkrInAQIECBAgQIAAAQIECBAgQIAAAQIECBAoEFAMFCAaQYAAAQIECBAgQIAAAQIECBAgQIAAAQIEUgQUAylJ2ZMAAQIECBAgQIAAAQIECBAgQIAAAQIECBQIKAYKEI0gQIAAAQIECBAgQIAAAQIECBAgQIAAAQIpAoqBlKTsSYAAAQIECBAgQIAAAQIECBAgQIAAAQIECgQUAwWIRhAgQIAAAQIECBAgQIAAAQIECBAgQIAAgRQBxUBKUvYkQIAAAQIECBAgQIAAAQIECBAgQIAAAQIFAoqBAkQjCBAgQIAAAQIECBAgQIAAAQIECBAgQIBAioBiICUpexIgQIAAAQIECBAgQIAAAQIECBAgQIAAgQIBxUABohEECBAgQIAAAQIECBAgQIAAAQIECBAgQCBFQDGQkpQ9CRAgQIAAAQIECBAgQIAAAQIECBAgQIBAgYBioADRCAIECBAgQIAAAQIECBAgQIAAAQIECBAgkCKgGEhJyp4ECBAgQIAAAQIECBAgQIAAAQIECBAgQKBAQDFQgGgEAQIECBAgQIAAAQIECBAgQIAAAQIECBBIEVAMpCRlTwIECBAgQIAAAQIECBAgQIAAAQIECBAgUCCgGChANIIAAQIECBAgQIAAAQIECBAgQIAAAQIECKQIKAZSkrInAQIECBAgQIAAAQIECBAgQIAAAQIECBAoEFAMFCAaQYAAAQIECBAgQIAAAQIECBAgQIAAAQIEUgQUAylJ2ZMAAQIECBAgQIAAAQIECBAgQIAAAQIECBQIKAYKEI0gQIAAAQIECBAgQIAAAQIECBAgQIAAAQIpAoqBlKTsSYAAAQIECBAgQIAAAQIECBAgQIAAAQIECgQUAwWIRhAgQIAAAQIECBAgQIAAAQIECBAgQIAAgRQBxUBKUvYkQIAAAQIECBAgQIAAAQIECBAgQIAAAQIFAoqBAkQjCBAgQIAAAQIECBAgQIAAAQIECBAgQIBAioBiICUpexIgQIAAAQIECBAgQIAAAQIECBAgQIAAgQIBxUABohEECBAgQIAAAQIECBAgQIAAAQIECBAgQCBFQDGQkpQ9CRAgQIAAAQIECBAgQIAAAQIECBAgQIBAgYBioADRCAIECBAgQIAAAQIECBAgQIAAAQIECBAgkCKgGEhJyp4ECBAgQIAAAQIECBAgQIAAAQIECBAgQKBAQDFQgGgEAQIECBAgQIAAAQIECBAgQIAAAQIECBBIEVAMpCRlTwIECBAgQIAAAQIECBAgQIAAAQIECBAgUCCgGChANIIAAQIECBAgQIAAAQIECBAgQIAAAQIECKQIKAZSkrInAQIECBAgQIAAAQIECBAgQIAAAQIECBAoEFAMFCAaQYAAAQIECBAgQIAAAQIECBAgQIAAAQIEUgQUAylJ2ZMAAQIECBAgQIAAAQIECBAgQIAAAQIECBQIKAYKEI0gQIAAAQIECBAgQIAAAQIECBAgQIAAAQIpAoqBlKTsSYAAAQIECBAgQIAAAQIECBAgQIAAAQIECgQUAwWIRhAgQIAAAQIECBAgQIAAAQIECBAgQIAAgRQBxUBKUvYkQIAAAQIECBAgQIAAAQIECBAgQIAAAQIFAoqBAkQjCBAgQIAAAQIECBAgQIAAAQIECBAgQIBAioBiICUpexIgQIAAAQIECBAgQIAAAQIECBAgQIAAgQIBxUABohEECBAgQIAAAQIECBAgQIAAAQIECBAgQCBFQDGQkpQ9CRAgQIAAAQIECBAgQIAAAQIECBAgQIBAgYBioADRCAIECBAgQIAAAQIECBAgQIAAAQIECBAgkCKgGEhJyp4ECBAgQIAAAQIECBAgQIAAAQIECBAgQKBAQDFQgGgEAQIECBAgQIAAAQIECBAgQIAAAQIECBBIEVAMpCRlTwIECBAgQIAAAQIECBAgQIAAAQIECBAgUCCgGChANIIAAQIECBAgQIAAAQIECBAgQIAAAQIECKQIKAZSkrInAQIECBAgQIAAAQIECBAgQIAAAQIECBAoEFAMFCAaQYAAAQIECBAgQIAAAQIECBAgQIAAAQIEUgQUAylJ2ZMAAQIECBAgQIAAAQIECBAgQIAAAQIECBQIKAYKEI0gQIAAAQIECBAgQIAAAQIECBAgQIAAAQIpAoqBlKTsSYAAAQIECBAgQIAAAQIECBAgQIAAAQIECgQUAwWIRhAgQIAAAQIECBAgQIAAAQIECBAgQIAAgRQBxUBKUvYkQIAAAQIECBAgQIAAAQIECBAgQIAAAQIFAoqBAkQjCBAgQIAAAQIECBAgQIAAAQIECBAgQIBAioBiICUpexIgQIAAAQIECBAgQIAAAQIECBAgQIAAgQIBxUABohEECBAgQIAAAQIECBAgQIAAAQIECBAgQCBFQDGQkpQ9CRAgQIAAAQIECBAgQIAAAQIECBAgQIBAgYBioADRCAIECBAgQIAAAQIECBAgQIAAAQIECBAgkCKgGEhJyp4ECBAgQIAAAQIECBAgQIAAAQIECBAgQKBAQDFQgGgEAQIECBAgQIAAAQIECBAgQIAAAQIECBBIEVAMpCRlTwIECBAgQIAAAQIECBAgQIAAAQIECBAgUCCgGChANIIAAQIECBAgQIAAAQIECBAgQIAAAQIECKQIKAZSkrInAQIECBAgQIAAAQIECBAgQIAAAQIECBAoEFAMFCAaQYAAAQIECBAgQIAAAQIECBAgQIAAAQIEUgQUAylJ2ZMAAQIECBAgQIAAAQIECBAgQIAAAQIECBQIKAYKEI0gQIAAAQIECBAgQIAAAQIECBAgQIAAAQIpAoqBlKTsSYAAAQIECBAgQIAAAQIECBAgQIAAAQIECgQUAwWIRhAgQIAAAQIECBAgQIAAAQIECBAgQIAAgRQBxUBKUvYkQIAAAQIECBAgQIAAAQIECBAgQIAAAQIFAoqBAkQjCBAgQIAAAQIECBAgQIAAAQIECBAgQIBAioBiICUpexIgQIAAAQIECBAgQIAAAQIECBAgQIAAgQIBxUABohEECBAgQIAAAQIECBAgQIAAAQIECBAgQCBFQDGQkpQ9CRAgQIAAAQIECBAgQIAAAQIECBAgQIBAgYBioADRCAIECBAgQIAAAQIECBAgQIAAAQIECBAgkCKgGEhJyp4ECBAgQIAAAQIECBAgQIAAAQIECBAgQKBA4C+R01m3EQ6wBwAAAABJRU5ErkJggg==', ]; diff --git a/src/components/survey/Survey.form.js b/src/components/survey/Survey.form.js index 0db2c40aaf..7fb45b7d77 100644 --- a/src/components/survey/Survey.form.js +++ b/src/components/survey/Survey.form.js @@ -3,19 +3,22 @@ import SurveyEditData from './editForm/Survey.edit.data'; import SurveyEditDisplay from './editForm/Survey.edit.display'; import SurveyEditValidation from './editForm/Survey.edit.validation'; -export default function(...extend) { - return Components.baseEditForm([ - { - key: 'display', - components: SurveyEditDisplay - }, - { - key: 'data', - components: SurveyEditData - }, - { - key: 'validation', - components: SurveyEditValidation - }, - ], ...extend); +export default function (...extend) { + return Components.baseEditForm( + [ + { + key: 'display', + components: SurveyEditDisplay, + }, + { + key: 'data', + components: SurveyEditData, + }, + { + key: 'validation', + components: SurveyEditValidation, + }, + ], + ...extend, + ); } diff --git a/src/components/survey/Survey.js b/src/components/survey/Survey.js index db2274bcbe..1c56185bcc 100644 --- a/src/components/survey/Survey.js +++ b/src/components/survey/Survey.js @@ -1,153 +1,171 @@ import _ from 'lodash'; import Field from '../_classes/field/Field'; -import { boolValue, componentValueTypes, getComponentSavedTypes } from '../../utils/utils'; +import { + boolValue, + componentValueTypes, + getComponentSavedTypes, +} from '../../utils/utils'; export default class SurveyComponent extends Field { - static schema(...extend) { - return Field.schema({ - type: 'survey', - label: 'Survey', - key: 'survey', - questions: [], - values: [] - }, ...extend); - } - - static get builderInfo() { - return { - title: 'Survey', - group: 'advanced', - icon: 'list', - weight: 110, - documentation: '/userguide/form-building/advanced-components#survey', - schema: SurveyComponent.schema() - }; - } - - static get serverConditionSettings() { - return SurveyComponent.conditionOperatorsSettings; - } - - static get conditionOperatorsSettings() { - return { - ...super.conditionOperatorsSettings, - operators: ['isEmpty', 'isNotEmpty'], - }; - } - - static savedValueTypes(schema) { - return getComponentSavedTypes(schema) || [componentValueTypes.object]; - } - - get defaultSchema() { - return SurveyComponent.schema(); - } - - render() { - return super.render(this.renderTemplate('survey')); - } - - attach(element) { - this.loadRefs(element, { input: 'multiple' }); - const superAttach = super.attach(element); - this.refs.input.forEach((input) => { - if (this.disabled) { - input.setAttribute('disabled', 'disabled'); - } - else { - this.addEventListener(input, 'change', () => this.updateValue(null, { - modified: true - })); - } - }); - this.setValue(this.dataValue); - return superAttach; - } - - setValue(value, flags = {}) { - if (!value) { - return false; - } - - _.each(this.component.questions, (question) => { - _.each(this.refs.input, (input) => { - if (input.name === this.getInputName(question)) { - input.checked = (input.value === value[question.value]); - } - }); - }); + static schema(...extend) { + return Field.schema( + { + type: 'survey', + label: 'Survey', + key: 'survey', + questions: [], + values: [], + }, + ...extend, + ); + } + + static get builderInfo() { + return { + title: 'Survey', + group: 'advanced', + icon: 'list', + weight: 110, + documentation: + '/userguide/form-building/advanced-components#survey', + schema: SurveyComponent.schema(), + }; + } + + static get serverConditionSettings() { + return SurveyComponent.conditionOperatorsSettings; + } + + static get conditionOperatorsSettings() { + return { + ...super.conditionOperatorsSettings, + operators: ['isEmpty', 'isNotEmpty'], + }; + } + + static savedValueTypes(schema) { + return getComponentSavedTypes(schema) || [componentValueTypes.object]; + } - const changed = this.updateValue(value, flags); + get defaultSchema() { + return SurveyComponent.schema(); + } - if (changed && this.isHtmlRenderMode()) { - this.redraw(); + render() { + return super.render(this.renderTemplate('survey')); } - return changed; - } + attach(element) { + this.loadRefs(element, { input: 'multiple' }); + const superAttach = super.attach(element); + this.refs.input.forEach((input) => { + if (this.disabled) { + input.setAttribute('disabled', 'disabled'); + } else { + this.addEventListener(input, 'change', () => + this.updateValue(null, { + modified: true, + }), + ); + } + }); + this.setValue(this.dataValue); + return superAttach; + } + + setValue(value, flags = {}) { + if (!value) { + return false; + } + + _.each(this.component.questions, (question) => { + _.each(this.refs.input, (input) => { + if (input.name === this.getInputName(question)) { + input.checked = input.value === value[question.value]; + } + }); + }); + + const changed = this.updateValue(value, flags); + + if (changed && this.isHtmlRenderMode()) { + this.redraw(); + } + + return changed; + } - get emptyValue() { - return {}; - } + get emptyValue() { + return {}; + } - get defaultValue() { - const defaultValue = super.defaultValue; - //support for default values created in old formio.js versions - if (defaultValue && !_.isObject(defaultValue) && this.component.values.some(value => value.value === defaultValue)) { - const adoptedDefaultValue = {}; + get defaultValue() { + const defaultValue = super.defaultValue; + //support for default values created in old formio.js versions + if ( + defaultValue && + !_.isObject(defaultValue) && + this.component.values.some((value) => value.value === defaultValue) + ) { + const adoptedDefaultValue = {}; + + this.component.questions.forEach((question) => { + adoptedDefaultValue[question.value] = defaultValue; + }); + + return adoptedDefaultValue; + } - this.component.questions.forEach(question => { - adoptedDefaultValue[question.value] = defaultValue; - }); + return defaultValue; + } - return adoptedDefaultValue; + getValue() { + if (this.viewOnly || !this.refs.input || !this.refs.input.length) { + return this.dataValue; + } + const value = {}; + _.each(this.component.questions, (question) => { + _.each(this.refs.input, (input) => { + if ( + input.checked && + input.name === this.getInputName(question) + ) { + value[question.value] = input.value; + return false; + } + }); + }); + return value; } - return defaultValue; - } + set disabled(disabled) { + super.disabled = disabled; + _.each(this.refs.input, (input) => { + input.disabled = true; + }); + } - getValue() { - if (this.viewOnly || !this.refs.input || !this.refs.input.length) { - return this.dataValue; + get disabled() { + return super.disabled; } - const value = {}; - _.each(this.component.questions, (question) => { - _.each(this.refs.input, (input) => { - if (input.checked && (input.name === this.getInputName(question))) { - value[question.value] = input.value; - return false; + + validateRequired(setting, value) { + if (!boolValue(setting)) { + return true; } - }); - }); - return value; - } - - set disabled(disabled) { - super.disabled = disabled; - _.each(this.refs.input, (input) => { - input.disabled = true; - }); - } - - get disabled() { - return super.disabled; - } - - validateRequired(setting, value) { - if (!boolValue(setting)) { - return true; - } - return this.component.questions.reduce((result, question) => - result && Boolean(value[question.value]), true); - } - - getInputName(question) { - return `${this.options.name}[${question.value}]`; - } - - getValueAsString(value, options) { - if (options?.email) { - let result = (` + return this.component.questions.reduce( + (result, question) => result && Boolean(value[question.value]), + true, + ); + } + + getInputName(question) { + return `${this.options.name}[${question.value}]`; + } + + getValueAsString(value, options) { + if (options?.email) { + let result = ` @@ -156,40 +174,53 @@ export default class SurveyComponent extends Field { - `); + `; - _.forIn(value, (value, key) => { - const question = _.find(this.component.questions, ['value', key]); - const answer = _.find(this.component.values, ['value', value]); + _.forIn(value, (value, key) => { + const question = _.find(this.component.questions, [ + 'value', + key, + ]); + const answer = _.find(this.component.values, ['value', value]); - if (!question || !answer) { - return; - } + if (!question || !answer) { + return; + } - result += (` + result += ` - `); - }); + `; + }); - result += '
    ${question.label} ${answer.label}
    '; + result += ''; - return result; - } + return result; + } - if (_.isPlainObject(value)) { - const { values = [], questions = [] } = this.component; - return _.isEmpty(value) - ? '' - : _.map(value,(v, q) => { - const valueLabel = _.get(_.find(values, val => _.isEqual(val.value, v)), 'label', v); - const questionLabel = _.get(_.find(questions, quest => _.isEqual(quest.value, q)), 'label', q); - return `${questionLabel}: ${valueLabel}`; - }).join('; '); - } + if (_.isPlainObject(value)) { + const { values = [], questions = [] } = this.component; + return _.isEmpty(value) + ? '' + : _.map(value, (v, q) => { + const valueLabel = _.get( + _.find(values, (val) => _.isEqual(val.value, v)), + 'label', + v, + ); + const questionLabel = _.get( + _.find(questions, (quest) => + _.isEqual(quest.value, q), + ), + 'label', + q, + ); + return `${questionLabel}: ${valueLabel}`; + }).join('; '); + } - return super.getValueAsString(value, options); - } + return super.getValueAsString(value, options); + } } diff --git a/src/components/survey/Survey.unit.js b/src/components/survey/Survey.unit.js index a5356d20e6..93c45ce43c 100644 --- a/src/components/survey/Survey.unit.js +++ b/src/components/survey/Survey.unit.js @@ -3,55 +3,73 @@ import assert from 'power-assert'; import Harness from '../../../test/harness'; import SurveyComponent from './Survey'; -import { - comp1, - comp2 -} from './fixtures'; +import { comp1, comp2 } from './fixtures'; -describe('Survey Component', function() { - it('Should build a survey component', function() { - return Harness.testCreate(SurveyComponent, comp1).then((component) => { - const inputs = Harness.testElements(component, 'input[type="radio"]', 10); - inputs.forEach((input, index) => { - if (index < 5) { - // Service - assert.equal(input.name, 'data[surveyQuestions][service]'); - assert.equal(input.id, `${component.key}-service-${comp1.values[index].value}`); - } - else { - // How do you like our service? - assert.equal(input.name, 'data[surveyQuestions][howWouldYouRateTheTechnology]'); - assert.equal(input.id, `${component.key}-howWouldYouRateTheTechnology-${comp1.values[index - 5].value}`); - } - }); +describe('Survey Component', function () { + it('Should build a survey component', function () { + return Harness.testCreate(SurveyComponent, comp1).then((component) => { + const inputs = Harness.testElements( + component, + 'input[type="radio"]', + 10, + ); + inputs.forEach((input, index) => { + if (index < 5) { + // Service + assert.equal(input.name, 'data[surveyQuestions][service]'); + assert.equal( + input.id, + `${component.key}-service-${comp1.values[index].value}`, + ); + } else { + // How do you like our service? + assert.equal( + input.name, + 'data[surveyQuestions][howWouldYouRateTheTechnology]', + ); + assert.equal( + input.id, + `${component.key}-howWouldYouRateTheTechnology-${ + comp1.values[index - 5].value + }`, + ); + } + }); + }); }); - }); - it('Should set the value of surveys.', function() { - return Harness.testCreate(SurveyComponent, comp1).then((component) => { - Harness.testSetGet(component, { service: 'bad', howWouldYouRateTheTechnology: 'good' }); - const inputs = Harness.testElements(component, 'input[type="radio"]', 10); - for (const input of inputs) { - if ( - (input.id === `${component.key}-service-bad`) || - (input.id === `${component.key}-howWouldYouRateTheTechnology-good`) - ) { - assert.equal(input.checked, true); - } - else { - assert.equal(input.checked, false); - } - } + it('Should set the value of surveys.', function () { + return Harness.testCreate(SurveyComponent, comp1).then((component) => { + Harness.testSetGet(component, { + service: 'bad', + howWouldYouRateTheTechnology: 'good', + }); + const inputs = Harness.testElements( + component, + 'input[type="radio"]', + 10, + ); + for (const input of inputs) { + if ( + input.id === `${component.key}-service-bad` || + input.id === + `${component.key}-howWouldYouRateTheTechnology-good` + ) { + assert.equal(input.checked, true); + } else { + assert.equal(input.checked, false); + } + } + }); }); - }); - it('Should require all questions for required Survey', function(done) { - Harness.testCreate(SurveyComponent, comp2).then((component) => { - Harness.testSetGet(component, { service: 'bad' }); - component.on('componentChange', () => { - done(); - }); - // assert(component.element) + it('Should require all questions for required Survey', function (done) { + Harness.testCreate(SurveyComponent, comp2).then((component) => { + Harness.testSetGet(component, { service: 'bad' }); + component.on('componentChange', () => { + done(); + }); + // assert(component.element) + }); }); - }); }); diff --git a/src/components/survey/editForm/Survey.edit.data.js b/src/components/survey/editForm/Survey.edit.data.js index ffb60b364b..171b3380ac 100644 --- a/src/components/survey/editForm/Survey.edit.data.js +++ b/src/components/survey/editForm/Survey.edit.data.js @@ -1,70 +1,71 @@ export default [ - { - key: 'multiple', - ignore: true - }, - { - type: 'datagrid', - input: true, - label: 'Questions', - key: 'questions', - tooltip: 'The questions you would like to ask in this survey question.', - weight: 0, - reorder: true, - defaultValue: [{ label: '', value: '' }], - components: [ - { - label: 'Label', - key: 'label', + { + key: 'multiple', + ignore: true, + }, + { + type: 'datagrid', input: true, - type: 'textfield' - }, - { - label: 'Value', - key: 'value', + label: 'Questions', + key: 'questions', + tooltip: 'The questions you would like to ask in this survey question.', + weight: 0, + reorder: true, + defaultValue: [{ label: '', value: '' }], + components: [ + { + label: 'Label', + key: 'label', + input: true, + type: 'textfield', + }, + { + label: 'Value', + key: 'value', + input: true, + type: 'textfield', + allowCalculateOverride: true, + calculateValue: { _camelCase: [{ var: 'row.label' }] }, + }, + { + label: 'Tooltip', + key: 'tooltip', + input: true, + type: 'textfield', + }, + ], + }, + { + type: 'datagrid', input: true, - type: 'textfield', - allowCalculateOverride: true, - calculateValue: { _camelCase: [{ var: 'row.label' }] } - }, - { - label: 'Tooltip', - key: 'tooltip', - input: true, - type: 'textfield', - }, - ] - }, - { - type: 'datagrid', - input: true, - label: 'Values', - key: 'values', - tooltip: 'The values that can be selected per question. Example: \'Satisfied\', \'Very Satisfied\', etc.', - weight: 1, - reorder: true, - defaultValue: [{ label: '', value: '' }], - components: [ - { - label: 'Label', - key: 'label', - input: true, - type: 'textfield' - }, - { - label: 'Value', - key: 'value', - input: true, - type: 'textfield', - allowCalculateOverride: true, - calculateValue: { _camelCase: [{ var: 'row.label' }] } - }, - { - label: 'Tooltip', - key: 'tooltip', - input: true, - type: 'textfield', - }, - ] - } + label: 'Values', + key: 'values', + tooltip: + "The values that can be selected per question. Example: 'Satisfied', 'Very Satisfied', etc.", + weight: 1, + reorder: true, + defaultValue: [{ label: '', value: '' }], + components: [ + { + label: 'Label', + key: 'label', + input: true, + type: 'textfield', + }, + { + label: 'Value', + key: 'value', + input: true, + type: 'textfield', + allowCalculateOverride: true, + calculateValue: { _camelCase: [{ var: 'row.label' }] }, + }, + { + label: 'Tooltip', + key: 'tooltip', + input: true, + type: 'textfield', + }, + ], + }, ]; diff --git a/src/components/survey/editForm/Survey.edit.display.js b/src/components/survey/editForm/Survey.edit.display.js index 2e7810dd7b..7ca4d1b37c 100644 --- a/src/components/survey/editForm/Survey.edit.display.js +++ b/src/components/survey/editForm/Survey.edit.display.js @@ -1,6 +1,6 @@ export default [ - { - key: 'placeholder', - ignore: true - }, + { + key: 'placeholder', + ignore: true, + }, ]; diff --git a/src/components/survey/editForm/Survey.edit.validation.js b/src/components/survey/editForm/Survey.edit.validation.js index bbb7e1349d..ab094d6027 100644 --- a/src/components/survey/editForm/Survey.edit.validation.js +++ b/src/components/survey/editForm/Survey.edit.validation.js @@ -1,6 +1,6 @@ export default [ - { - key: 'validateOn', - ignore: true - }, + { + key: 'validateOn', + ignore: true, + }, ]; diff --git a/src/components/survey/fixtures/comp1.js b/src/components/survey/fixtures/comp1.js index f80510ab54..3fe9cd4f56 100644 --- a/src/components/survey/fixtures/comp1.js +++ b/src/components/survey/fixtures/comp1.js @@ -1,53 +1,53 @@ export default { - 'conditional': { - 'eq': '', - 'when': null, - 'show': '' - }, - 'tags': [], - 'type': 'survey', - 'validate': { - 'customPrivate': false, - 'custom': '', - 'required': false - }, - 'persistent': true, - 'protected': false, - 'defaultValue': '', - 'values': [ - { - 'label': 'Excellent', - 'value': 'excellent' + conditional: { + eq: '', + when: null, + show: '', }, - { - 'label': 'Good', - 'value': 'good' + tags: [], + type: 'survey', + validate: { + customPrivate: false, + custom: '', + required: false, }, - { - 'label': 'Average', - 'value': 'average' - }, - { - 'label': 'Inadequate', - 'value': 'inadequate' - }, - { - 'label': 'Bad', - 'value': 'bad' - } - ], - 'questions': [ - { - 'label': 'How do you like our service?', - 'value': 'service' - }, - { - 'label': 'How would you rate the technology?', - 'value': 'howWouldYouRateTheTechnology' - } - ], - 'key': 'surveyQuestions', - 'label': 'Survey Questions', - 'tableView': true, - 'input': true + persistent: true, + protected: false, + defaultValue: '', + values: [ + { + label: 'Excellent', + value: 'excellent', + }, + { + label: 'Good', + value: 'good', + }, + { + label: 'Average', + value: 'average', + }, + { + label: 'Inadequate', + value: 'inadequate', + }, + { + label: 'Bad', + value: 'bad', + }, + ], + questions: [ + { + label: 'How do you like our service?', + value: 'service', + }, + { + label: 'How would you rate the technology?', + value: 'howWouldYouRateTheTechnology', + }, + ], + key: 'surveyQuestions', + label: 'Survey Questions', + tableView: true, + input: true, }; diff --git a/src/components/survey/fixtures/comp2.js b/src/components/survey/fixtures/comp2.js index fa05acdb12..30c794f6ad 100644 --- a/src/components/survey/fixtures/comp2.js +++ b/src/components/survey/fixtures/comp2.js @@ -1,53 +1,53 @@ export default { - 'conditional': { - 'eq': '', - 'when': null, - 'show': '' - }, - 'tags': [], - 'type': 'survey', - 'validate': { - 'customPrivate': false, - 'custom': '', - 'required': true - }, - 'persistent': true, - 'protected': false, - 'defaultValue': '', - 'values': [ - { - 'label': 'Excellent', - 'value': 'excellent' + conditional: { + eq: '', + when: null, + show: '', }, - { - 'label': 'Good', - 'value': 'good' + tags: [], + type: 'survey', + validate: { + customPrivate: false, + custom: '', + required: true, }, - { - 'label': 'Average', - 'value': 'average' - }, - { - 'label': 'Inadequate', - 'value': 'inadequate' - }, - { - 'label': 'Bad', - 'value': 'bad' - } - ], - 'questions': [ - { - 'label': 'How do you like our service?', - 'value': 'service' - }, - { - 'label': 'How would you rate the technology?', - 'value': 'howWouldYouRateTheTechnology' - } - ], - 'key': 'surveyQuestions', - 'label': 'Survey Questions', - 'tableView': true, - 'input': true + persistent: true, + protected: false, + defaultValue: '', + values: [ + { + label: 'Excellent', + value: 'excellent', + }, + { + label: 'Good', + value: 'good', + }, + { + label: 'Average', + value: 'average', + }, + { + label: 'Inadequate', + value: 'inadequate', + }, + { + label: 'Bad', + value: 'bad', + }, + ], + questions: [ + { + label: 'How do you like our service?', + value: 'service', + }, + { + label: 'How would you rate the technology?', + value: 'howWouldYouRateTheTechnology', + }, + ], + key: 'surveyQuestions', + label: 'Survey Questions', + tableView: true, + input: true, }; diff --git a/src/components/survey/fixtures/values.js b/src/components/survey/fixtures/values.js index bc0622b359..fc3a81d5ab 100644 --- a/src/components/survey/fixtures/values.js +++ b/src/components/survey/fixtures/values.js @@ -1,10 +1,10 @@ export default [ - { - one: 'a', - two: 'b', - }, - { - one: 'b', - two: 'a', - }, + { + one: 'a', + two: 'b', + }, + { + one: 'b', + two: 'a', + }, ]; diff --git a/src/components/table/Table.form.js b/src/components/table/Table.form.js index a0e1155f6a..f0cc51c40b 100644 --- a/src/components/table/Table.form.js +++ b/src/components/table/Table.form.js @@ -2,11 +2,14 @@ import nestedComponentForm from '../_classes/nested/NestedComponent.form'; import TableEditDisplay from './editForm/Table.edit.display'; -export default function(...extend) { - return nestedComponentForm([ - { - key: 'display', - components: TableEditDisplay - }, - ], ...extend); +export default function (...extend) { + return nestedComponentForm( + [ + { + key: 'display', + components: TableEditDisplay, + }, + ], + ...extend, + ); } diff --git a/src/components/table/Table.js b/src/components/table/Table.js index 33a9269ae8..283b52c259 100644 --- a/src/components/table/Table.js +++ b/src/components/table/Table.js @@ -3,189 +3,221 @@ import BuilderUtils from '../../utils/builder'; import NestedComponent from '../_classes/nested/NestedComponent'; export default class TableComponent extends NestedComponent { - static emptyTable(numRows, numCols) { - const rows = []; - for (let i = 0; i < numRows; i++) { - const cols = []; - for (let j = 0; j < numCols; j++) { - cols.push({ components: [] }); - } - rows.push(cols); + static emptyTable(numRows, numCols) { + const rows = []; + for (let i = 0; i < numRows; i++) { + const cols = []; + for (let j = 0; j < numCols; j++) { + cols.push({ components: [] }); + } + rows.push(cols); + } + return rows; + } + + static schema(...extend) { + return NestedComponent.schema( + { + label: 'Table', + type: 'table', + input: false, + key: 'table', + numRows: 3, + numCols: 3, + rows: TableComponent.emptyTable(3, 3), + header: [], + caption: '', + cloneRows: false, + striped: false, + bordered: false, + hover: false, + condensed: false, + persistent: false, + }, + ...extend, + ); } - return rows; - } - - static schema(...extend) { - return NestedComponent.schema({ - label: 'Table', - type: 'table', - input: false, - key: 'table', - numRows: 3, - numCols: 3, - rows: TableComponent.emptyTable(3, 3), - header: [], - caption: '', - cloneRows: false, - striped: false, - bordered: false, - hover: false, - condensed: false, - persistent: false - }, ...extend); - } - - static get builderInfo() { - return { - title: 'Table', - group: 'layout', - icon: 'table', - weight: 40, - documentation: '/userguide/form-building/layout-components#table', - showPreview: false, - schema: TableComponent.schema() - }; - } - - static savedValueTypes() { - return []; - } - - get defaultSchema() { - return TableComponent.schema(); - } - - get schema() { - const schema = _.omit(super.schema, 'components'); - schema.rows = []; - this.eachComponent((component) => { - if (!schema.rows || !schema.rows.length) { - schema.rows = TableComponent.emptyTable(this.component.numRows, this.component.numCols); - } - if (!schema.rows[component.tableRow]) { - schema.rows[component.tableRow] = []; - } - if (!schema.rows[component.tableRow][component.tableColumn]) { - schema.rows[component.tableRow][component.column] = { components: [] }; - } - schema.rows[component.tableRow][component.tableColumn].components.push(component.schema); - }); - if (!schema.rows.length) { - schema.rows = TableComponent.emptyTable(this.component.numRows, this.component.numCols); + + static get builderInfo() { + return { + title: 'Table', + group: 'layout', + icon: 'table', + weight: 40, + documentation: '/userguide/form-building/layout-components#table', + showPreview: false, + schema: TableComponent.schema(), + }; } - return schema; - } - get className() { - let name = `table-responsive ${super.className}`; - if (!this.component.bordered) { - name += ' no-top-border-table'; + static savedValueTypes() { + return []; + } + + get defaultSchema() { + return TableComponent.schema(); + } + + get schema() { + const schema = _.omit(super.schema, 'components'); + schema.rows = []; + this.eachComponent((component) => { + if (!schema.rows || !schema.rows.length) { + schema.rows = TableComponent.emptyTable( + this.component.numRows, + this.component.numCols, + ); + } + if (!schema.rows[component.tableRow]) { + schema.rows[component.tableRow] = []; + } + if (!schema.rows[component.tableRow][component.tableColumn]) { + schema.rows[component.tableRow][component.column] = { + components: [], + }; + } + schema.rows[component.tableRow][ + component.tableColumn + ].components.push(component.schema); + }); + if (!schema.rows.length) { + schema.rows = TableComponent.emptyTable( + this.component.numRows, + this.component.numCols, + ); + } + return schema; + } + + get className() { + let name = `table-responsive ${super.className}`; + if (!this.component.bordered) { + name += ' no-top-border-table'; + } + return name; } - return name; - } - get cellClassName() { - let name = ''; - if (this.component.cellAlignment) { - name = `cell-align-${this.component.cellAlignment}`; + get cellClassName() { + let name = ''; + if (this.component.cellAlignment) { + name = `cell-align-${this.component.cellAlignment}`; + } + return name; } - return name; - } - get tableKey() { - return `table-${this.key}`; - } + get tableKey() { + return `table-${this.key}`; + } - get colWidth() { - const { numCols } = this.component; - if (!numCols || typeof numCols !== 'number') { - return ''; + get colWidth() { + const { numCols } = this.component; + if (!numCols || typeof numCols !== 'number') { + return ''; + } + return Math.floor(12 / numCols).toString(); } - return Math.floor(12 / numCols).toString(); - } - - constructor(...args) { - super(...args); - this.noField = true; - } - - init() { - super.init(); - // Ensure component.rows has the correct number of rows and columns. - for (let rowIndex = 0; rowIndex < this.component.numRows; rowIndex++) { - this.component.rows[rowIndex] = this.component.rows[rowIndex] || []; - for (let colIndex = 0; colIndex < this.component.numCols; colIndex++) { - this.component.rows[rowIndex][colIndex] = this.component.rows[rowIndex][colIndex] || { components: [] }; - } - this.component.rows[rowIndex] = this.component.rows[rowIndex].slice(0, this.component.numCols); + + constructor(...args) { + super(...args); + this.noField = true; } - this.component.rows = this.component.rows.slice(0, this.component.numRows); - - const lastNonEmptyRow = []; - this.table = []; - _.each(this.component.rows, (row, rowIndex) => { - this.table[rowIndex] = []; - _.each(row, (column, colIndex) => { - this.table[rowIndex][colIndex] = []; - if (this.component.cloneRows) { - if (column.components.length) { - lastNonEmptyRow[colIndex] = column; - } - else if (lastNonEmptyRow[colIndex]) { - column.components = _.cloneDeep(lastNonEmptyRow[colIndex].components); - BuilderUtils.uniquify(this.root._form.components, column); - } + + init() { + super.init(); + // Ensure component.rows has the correct number of rows and columns. + for (let rowIndex = 0; rowIndex < this.component.numRows; rowIndex++) { + this.component.rows[rowIndex] = this.component.rows[rowIndex] || []; + for ( + let colIndex = 0; + colIndex < this.component.numCols; + colIndex++ + ) { + this.component.rows[rowIndex][colIndex] = this.component.rows[ + rowIndex + ][colIndex] || { components: [] }; + } + this.component.rows[rowIndex] = this.component.rows[rowIndex].slice( + 0, + this.component.numCols, + ); } - _.each(column.components, (comp) => { - let columnComponent; - - if (this.builderMode) { - comp.id = comp.id + rowIndex; - columnComponent = comp; - } - else { - columnComponent = { ...comp, id: (comp.id + rowIndex) }; - } - - const component = this.createComponent(columnComponent); - component.tableRow = rowIndex; - component.tableColumn = colIndex; - this.table[rowIndex][colIndex].push(component); + this.component.rows = this.component.rows.slice( + 0, + this.component.numRows, + ); + + const lastNonEmptyRow = []; + this.table = []; + _.each(this.component.rows, (row, rowIndex) => { + this.table[rowIndex] = []; + _.each(row, (column, colIndex) => { + this.table[rowIndex][colIndex] = []; + if (this.component.cloneRows) { + if (column.components.length) { + lastNonEmptyRow[colIndex] = column; + } else if (lastNonEmptyRow[colIndex]) { + column.components = _.cloneDeep( + lastNonEmptyRow[colIndex].components, + ); + BuilderUtils.uniquify( + this.root._form.components, + column, + ); + } + } + _.each(column.components, (comp) => { + let columnComponent; + + if (this.builderMode) { + comp.id = comp.id + rowIndex; + columnComponent = comp; + } else { + columnComponent = { ...comp, id: comp.id + rowIndex }; + } + + const component = this.createComponent(columnComponent); + component.tableRow = rowIndex; + component.tableColumn = colIndex; + this.table[rowIndex][colIndex].push(component); + }); + }); }); - }); - }); - } - - render() { - return super.render(this.renderTemplate('table', { - cellClassName: this.cellClassName, - tableKey: this.tableKey, - colWidth: this.colWidth, - tableComponents: this.table.map(row => - row.map(column => - this.renderComponents(column) - ) - ) - })); - } - - attach(element) { - const keys = this.table.reduce((prev, row, rowIndex) => { - prev[`${this.tableKey}-${rowIndex}`] = 'multiple'; - return prev; - }, {}); - this.loadRefs(element, keys); - const superAttach = super.attach(element); - this.table.forEach((row, rowIndex) => { - row.forEach((column, columnIndex) => { - this.attachComponents(this.refs[`${this.tableKey}-${rowIndex}`][columnIndex], this.table[rowIndex][columnIndex], this.component.rows[rowIndex][columnIndex].components); - }); - }); - return superAttach; - } - - destroy(all = false) { - super.destroy(all); - delete this.table; - } + } + + render() { + return super.render( + this.renderTemplate('table', { + cellClassName: this.cellClassName, + tableKey: this.tableKey, + colWidth: this.colWidth, + tableComponents: this.table.map((row) => + row.map((column) => this.renderComponents(column)), + ), + }), + ); + } + + attach(element) { + const keys = this.table.reduce((prev, row, rowIndex) => { + prev[`${this.tableKey}-${rowIndex}`] = 'multiple'; + return prev; + }, {}); + this.loadRefs(element, keys); + const superAttach = super.attach(element); + this.table.forEach((row, rowIndex) => { + row.forEach((column, columnIndex) => { + this.attachComponents( + this.refs[`${this.tableKey}-${rowIndex}`][columnIndex], + this.table[rowIndex][columnIndex], + this.component.rows[rowIndex][columnIndex].components, + ); + }); + }); + return superAttach; + } + + destroy(all = false) { + super.destroy(all); + delete this.table; + } } diff --git a/src/components/table/Table.unit.js b/src/components/table/Table.unit.js index 490ca6bc51..7990e9d292 100644 --- a/src/components/table/Table.unit.js +++ b/src/components/table/Table.unit.js @@ -1,14 +1,12 @@ import Harness from '../../../test/harness'; import TableComponent from './Table'; -import { - comp1 -} from './fixtures'; +import { comp1 } from './fixtures'; -describe('Table Component', function() { - it('Should build a Table component', function() { - return Harness.testCreate(TableComponent, comp1).then((component) => { - Harness.testElements(component, 'input[type="text"]', 6); +describe('Table Component', function () { + it('Should build a Table component', function () { + return Harness.testCreate(TableComponent, comp1).then((component) => { + Harness.testElements(component, 'input[type="text"]', 6); + }); }); - }); }); diff --git a/src/components/table/editForm/Table.edit.display.js b/src/components/table/editForm/Table.edit.display.js index 634e000e4b..0d65851c35 100644 --- a/src/components/table/editForm/Table.edit.display.js +++ b/src/components/table/editForm/Table.edit.display.js @@ -1,131 +1,134 @@ export default [ - { - key: 'labelPosition', - ignore: true - }, - { - key: 'placeholder', - ignore: true - }, - { - key: 'description', - ignore: true - }, - { - key: 'autofocus', - ignore: true - }, - { - key: 'tooltip', - ignore: true - }, - { - key: 'tabindex', - ignore: true - }, - { - key: 'disabled', - ignore: true - }, - { - key: 'tableView', - ignore: true - }, - { - key: 'hideLabel', - ignore: true - }, - { - weight: 0, - type: 'textfield', - input: true, - key: 'label', - label: 'Label', - placeholder: 'Field Label', - tooltip: 'The label for this field.', - validate: { - required: true - }, - autofocus: true, - overrideEditForm: true - }, - { - type: 'number', - label: 'Number of Rows', - key: 'numRows', - input: true, - weight: 1, - placeholder: 'Number of Rows', - tooltip: 'Enter the number or rows that should be displayed by this table.' - }, - { - type: 'number', - label: 'Number of Columns', - key: 'numCols', - input: true, - weight: 2, - placeholder: 'Number of Columns', - tooltip: 'Enter the number or columns that should be displayed by this table.' - }, - { - type: 'checkbox', - label: 'Clone Row Components', - key: 'cloneRows', - input: true, - weight: 3, - tooltip: 'Check this if you would like to \'clone\' the first row of components to all additional empty rows of the table.' - }, - { - type: 'select', - label: 'Cell Alignment', - key: 'cellAlignment', - input: true, - tooltip: 'Horizontal alignment for cells of the table.', - dataSrc: 'values', - data: { - values: [ - { label: 'Left', value: 'left' }, - { label: 'Center', value: 'center' }, - { label: 'Right', value: 'right' } - ] - }, - defaultValue: 'left', - weight: 3 - }, - { - type: 'checkbox', - label: 'Striped', - key: 'striped', - tooltip: 'This will stripe the table if checked.', - input: true, - weight: 701 - }, - { - type: 'checkbox', - label: 'Bordered', - key: 'bordered', - input: true, - tooltip: 'This will border the table if checked.', - weight: 702 - }, - { - type: 'checkbox', - label: 'Hover', - key: 'hover', - input: true, - tooltip: 'Highlight a row on hover.', - weight: 703 - }, - { - type: 'checkbox', - label: 'Condensed', - key: 'condensed', - input: true, - tooltip: 'Condense the size of the table.', - weight: 704 - }, - { - key: 'hideLabel', - ignore: true - }, + { + key: 'labelPosition', + ignore: true, + }, + { + key: 'placeholder', + ignore: true, + }, + { + key: 'description', + ignore: true, + }, + { + key: 'autofocus', + ignore: true, + }, + { + key: 'tooltip', + ignore: true, + }, + { + key: 'tabindex', + ignore: true, + }, + { + key: 'disabled', + ignore: true, + }, + { + key: 'tableView', + ignore: true, + }, + { + key: 'hideLabel', + ignore: true, + }, + { + weight: 0, + type: 'textfield', + input: true, + key: 'label', + label: 'Label', + placeholder: 'Field Label', + tooltip: 'The label for this field.', + validate: { + required: true, + }, + autofocus: true, + overrideEditForm: true, + }, + { + type: 'number', + label: 'Number of Rows', + key: 'numRows', + input: true, + weight: 1, + placeholder: 'Number of Rows', + tooltip: + 'Enter the number or rows that should be displayed by this table.', + }, + { + type: 'number', + label: 'Number of Columns', + key: 'numCols', + input: true, + weight: 2, + placeholder: 'Number of Columns', + tooltip: + 'Enter the number or columns that should be displayed by this table.', + }, + { + type: 'checkbox', + label: 'Clone Row Components', + key: 'cloneRows', + input: true, + weight: 3, + tooltip: + "Check this if you would like to 'clone' the first row of components to all additional empty rows of the table.", + }, + { + type: 'select', + label: 'Cell Alignment', + key: 'cellAlignment', + input: true, + tooltip: 'Horizontal alignment for cells of the table.', + dataSrc: 'values', + data: { + values: [ + { label: 'Left', value: 'left' }, + { label: 'Center', value: 'center' }, + { label: 'Right', value: 'right' }, + ], + }, + defaultValue: 'left', + weight: 3, + }, + { + type: 'checkbox', + label: 'Striped', + key: 'striped', + tooltip: 'This will stripe the table if checked.', + input: true, + weight: 701, + }, + { + type: 'checkbox', + label: 'Bordered', + key: 'bordered', + input: true, + tooltip: 'This will border the table if checked.', + weight: 702, + }, + { + type: 'checkbox', + label: 'Hover', + key: 'hover', + input: true, + tooltip: 'Highlight a row on hover.', + weight: 703, + }, + { + type: 'checkbox', + label: 'Condensed', + key: 'condensed', + input: true, + tooltip: 'Condense the size of the table.', + weight: 704, + }, + { + key: 'hideLabel', + ignore: true, + }, ]; diff --git a/src/components/table/fixtures/comp1.js b/src/components/table/fixtures/comp1.js index 3a4026797a..98f27e0c60 100644 --- a/src/components/table/fixtures/comp1.js +++ b/src/components/table/fixtures/comp1.js @@ -1,251 +1,235 @@ export default { - 'conditional': { - 'eq': '', - 'when': null, - 'show': '' - }, - 'tags': [ - - ], - 'type': 'table', - 'condensed': false, - 'hover': false, - 'bordered': false, - 'striped': false, - 'caption': '', - 'header': [ - - ], - 'rows': [ - [ - { - 'components': [ - { - 'tags': [ - - ], - 'type': 'textfield', - 'conditional': { - 'eq': '', - 'when': null, - 'show': '' + conditional: { + eq: '', + when: null, + show: '', + }, + tags: [], + type: 'table', + condensed: false, + hover: false, + bordered: false, + striped: false, + caption: '', + header: [], + rows: [ + [ + { + components: [ + { + tags: [], + type: 'textfield', + conditional: { + eq: '', + when: null, + show: '', + }, + validate: { + customPrivate: false, + custom: '', + pattern: '', + maxLength: '', + minLength: '', + required: false, + }, + persistent: true, + unique: false, + protected: false, + defaultValue: '', + multiple: false, + suffix: '', + prefix: '', + placeholder: '', + key: 'a', + label: 'a', + inputMask: '', + inputType: 'text', + tableView: true, + input: true, + }, + ], }, - 'validate': { - 'customPrivate': false, - 'custom': '', - 'pattern': '', - 'maxLength': '', - 'minLength': '', - 'required': false + { + components: [ + { + tags: [], + type: 'textfield', + conditional: { + eq: '', + when: null, + show: '', + }, + validate: { + customPrivate: false, + custom: '', + pattern: '', + maxLength: '', + minLength: '', + required: false, + }, + persistent: true, + unique: false, + protected: false, + defaultValue: '', + multiple: false, + suffix: '', + prefix: '', + placeholder: '', + key: 'b', + label: 'b', + inputMask: '', + inputType: 'text', + tableView: true, + input: true, + }, + ], }, - 'persistent': true, - 'unique': false, - 'protected': false, - 'defaultValue': '', - 'multiple': false, - 'suffix': '', - 'prefix': '', - 'placeholder': '', - 'key': 'a', - 'label': 'a', - 'inputMask': '', - 'inputType': 'text', - 'tableView': true, - 'input': true - } - ] - }, - { - 'components': [ - { - 'tags': [ - - ], - 'type': 'textfield', - 'conditional': { - 'eq': '', - 'when': null, - 'show': '' + { + components: [ + { + tags: [], + type: 'textfield', + conditional: { + eq: '', + when: null, + show: '', + }, + validate: { + customPrivate: false, + custom: '', + pattern: '', + maxLength: '', + minLength: '', + required: false, + }, + persistent: true, + unique: false, + protected: false, + defaultValue: '', + multiple: false, + suffix: '', + prefix: '', + placeholder: '', + key: 'c', + label: 'c', + inputMask: '', + inputType: 'text', + tableView: true, + input: true, + }, + ], }, - 'validate': { - 'customPrivate': false, - 'custom': '', - 'pattern': '', - 'maxLength': '', - 'minLength': '', - 'required': false + ], + [ + { + components: [ + { + tags: [], + type: 'textfield', + conditional: { + eq: '', + when: null, + show: '', + }, + validate: { + customPrivate: false, + custom: '', + pattern: '', + maxLength: '', + minLength: '', + required: false, + }, + persistent: true, + unique: false, + protected: false, + defaultValue: '', + multiple: false, + suffix: '', + prefix: '', + placeholder: '', + key: 'd', + label: 'd', + inputMask: '', + inputType: 'text', + tableView: true, + input: true, + }, + ], }, - 'persistent': true, - 'unique': false, - 'protected': false, - 'defaultValue': '', - 'multiple': false, - 'suffix': '', - 'prefix': '', - 'placeholder': '', - 'key': 'b', - 'label': 'b', - 'inputMask': '', - 'inputType': 'text', - 'tableView': true, - 'input': true - } - ] - }, - { - 'components': [ - { - 'tags': [ - - ], - 'type': 'textfield', - 'conditional': { - 'eq': '', - 'when': null, - 'show': '' + { + components: [ + { + tags: [], + type: 'textfield', + conditional: { + eq: '', + when: null, + show: '', + }, + validate: { + customPrivate: false, + custom: '', + pattern: '', + maxLength: '', + minLength: '', + required: false, + }, + persistent: true, + unique: false, + protected: false, + defaultValue: '', + multiple: false, + suffix: '', + prefix: '', + placeholder: '', + key: 'e', + label: 'e', + inputMask: '', + inputType: 'text', + tableView: true, + input: true, + }, + ], }, - 'validate': { - 'customPrivate': false, - 'custom': '', - 'pattern': '', - 'maxLength': '', - 'minLength': '', - 'required': false + { + components: [ + { + tags: [], + type: 'textfield', + conditional: { + eq: '', + when: null, + show: '', + }, + validate: { + customPrivate: false, + custom: '', + pattern: '', + maxLength: '', + minLength: '', + required: false, + }, + persistent: true, + unique: false, + protected: false, + defaultValue: '', + multiple: false, + suffix: '', + prefix: '', + placeholder: '', + key: 'f', + label: 'f', + inputMask: '', + inputType: 'text', + tableView: true, + input: true, + }, + ], }, - 'persistent': true, - 'unique': false, - 'protected': false, - 'defaultValue': '', - 'multiple': false, - 'suffix': '', - 'prefix': '', - 'placeholder': '', - 'key': 'c', - 'label': 'c', - 'inputMask': '', - 'inputType': 'text', - 'tableView': true, - 'input': true - } - ] - } + ], ], - [ - { - 'components': [ - { - 'tags': [ - - ], - 'type': 'textfield', - 'conditional': { - 'eq': '', - 'when': null, - 'show': '' - }, - 'validate': { - 'customPrivate': false, - 'custom': '', - 'pattern': '', - 'maxLength': '', - 'minLength': '', - 'required': false - }, - 'persistent': true, - 'unique': false, - 'protected': false, - 'defaultValue': '', - 'multiple': false, - 'suffix': '', - 'prefix': '', - 'placeholder': '', - 'key': 'd', - 'label': 'd', - 'inputMask': '', - 'inputType': 'text', - 'tableView': true, - 'input': true - } - ] - }, - { - 'components': [ - { - 'tags': [ - - ], - 'type': 'textfield', - 'conditional': { - 'eq': '', - 'when': null, - 'show': '' - }, - 'validate': { - 'customPrivate': false, - 'custom': '', - 'pattern': '', - 'maxLength': '', - 'minLength': '', - 'required': false - }, - 'persistent': true, - 'unique': false, - 'protected': false, - 'defaultValue': '', - 'multiple': false, - 'suffix': '', - 'prefix': '', - 'placeholder': '', - 'key': 'e', - 'label': 'e', - 'inputMask': '', - 'inputType': 'text', - 'tableView': true, - 'input': true - } - ] - }, - { - 'components': [ - { - 'tags': [ - - ], - 'type': 'textfield', - 'conditional': { - 'eq': '', - 'when': null, - 'show': '' - }, - 'validate': { - 'customPrivate': false, - 'custom': '', - 'pattern': '', - 'maxLength': '', - 'minLength': '', - 'required': false - }, - 'persistent': true, - 'unique': false, - 'protected': false, - 'defaultValue': '', - 'multiple': false, - 'suffix': '', - 'prefix': '', - 'placeholder': '', - 'key': 'f', - 'label': 'f', - 'inputMask': '', - 'inputType': 'text', - 'tableView': true, - 'input': true - } - ] - } - ] - ], - 'numCols': 3, - 'numRows': 2, - 'key': 'table1', - 'input': false + numCols: 3, + numRows: 2, + key: 'table1', + input: false, }; diff --git a/src/components/tabs/Tabs.form.js b/src/components/tabs/Tabs.form.js index 2a02192ac0..c19eca27ea 100644 --- a/src/components/tabs/Tabs.form.js +++ b/src/components/tabs/Tabs.form.js @@ -2,11 +2,14 @@ import nestedComponentForm from '../_classes/nested/NestedComponent.form'; import TabsEditDisplay from './editForm/Tabs.edit.display'; -export default function(...extend) { - return nestedComponentForm([ - { - key: 'display', - components: TabsEditDisplay - }, - ], ...extend); +export default function (...extend) { + return nestedComponentForm( + [ + { + key: 'display', + components: TabsEditDisplay, + }, + ], + ...extend, + ); } diff --git a/src/components/tabs/Tabs.js b/src/components/tabs/Tabs.js index 865ae35205..bc37e66d30 100644 --- a/src/components/tabs/Tabs.js +++ b/src/components/tabs/Tabs.js @@ -2,240 +2,292 @@ import _ from 'lodash'; import NestedComponent from '../_classes/nested/NestedComponent'; export default class TabsComponent extends NestedComponent { - static schema(...extend) { - return NestedComponent.schema({ - label: 'Tabs', - type: 'tabs', - input: false, - key: 'tabs', - persistent: false, - tableView: false, - components: [ - { - label: 'Tab 1', - key: 'tab1', - components: [], - }, - ], - verticalLayout: false, - }, ...extend); - } - - static get builderInfo() { - return { - title: 'Tabs', - group: 'layout', - icon: 'folder-o', - weight: 50, - documentation: '/userguide/form-building/layout-components#tabs', - showPreview: false, - schema: TabsComponent.schema(), - }; - } - - static savedValueTypes() { - return []; - } - - get defaultSchema() { - return TabsComponent.schema(); - } - - get schema() { - const schema = super.schema; - // We need to clone this because the builder uses the "components" reference and this would reset that reference. - const components = _.cloneDeep(this.component.components); - schema.components = components.map((tab, index) => { - tab.components = this.tabs[index].map((component) => component.schema); - return tab; - }); - - return schema; - } - - get tabKey() { - return `tab-${this.key}`; - } - - get tabLikey() { - return `tabLi-${this.key}`; - } - - get tabLinkKey() { - return `tabLink-${this.key}`; - } - - constructor(...args) { - super(...args); - this.currentTab = 0; - this.noField = true; - } - - init() { - this.components = []; - this.tabs = []; - _.each(this.component.components, (tab, index) => { - this.tabs[index] = []; - // Initialize empty tabs. - tab.components = tab.components || []; - _.each(tab.components, (comp) => { - const component = this.createComponent(comp); - component.tab = index; - this.tabs[index].push(component); - }); - }); - } - - render() { - return super.render(this.renderTemplate( - 'tab', - { - tabKey: this.tabKey, - tabLikey: this.tabLikey, - tabLinkKey: this.tabLinkKey, - currentTab: this.currentTab, - tabComponents: this.tabs.map(tab => this.renderComponents(tab)), - }, - ( - this.options.flatten || this.options.pdf ? 'flat' : null - ), - )); - } - - attach(element) { - this.loadRefs( - element, - { - [this.tabLinkKey]: 'multiple', - [this.tabKey]: 'multiple', - [this.tabLikey]: 'multiple', - }, - ); - ['change', 'error'].forEach(event => this.on(event, this.handleTabsValidation.bind(this))); - const superAttach = super.attach(element); - this.refs[this.tabLinkKey].forEach((tabLink, index) => { - this.addEventListener(tabLink, 'click', (event) => { - event.preventDefault(); - this.setTab(index); - }); - }); - this.refs[this.tabKey].forEach((tab, index) => { - this.attachComponents(tab, this.tabs[index], this.component.components[index].components); - }); - return superAttach; - } - - detach(all) { - super.detach(all); - } - - /** - * Set the current tab. - * - * @param index - */ - setTab(index) { - if (!this.tabs || !this.tabs[index] || !this.refs[this.tabKey] || !this.refs[this.tabKey][index]) { - return; - } - - this.currentTab = index; - - _.each(this.refs[this.tabKey], (tab) => { - this.removeClass(tab, 'formio-tab-panel-active'); - tab.style.display = 'none'; - }); - this.addClass(this.refs[this.tabKey][index], 'formio-tab-panel-active'); - this.refs[this.tabKey][index].style.display = 'block'; - - _.each(this.refs[this.tabLinkKey], (tabLink, tabIndex) => { - if (this.refs[this.tabLinkKey][tabIndex]) { - this.removeClass(tabLink, 'active'); - this.removeClass(tabLink, 'formio-tab-link-active'); - } - if (this.refs[this.tabLikey][tabIndex]) { - this.removeClass(this.refs[this.tabLikey][tabIndex], 'active'); - this.removeClass(this.refs[this.tabLikey][tabIndex], 'formio-tab-link-container-active'); - } - }); - if (this.refs[this.tabLikey][index]) { - this.addClass(this.refs[this.tabLikey][index], 'active'); - this.addClass(this.refs[this.tabLikey][index], 'formio-tab-link-container-active'); - } - if (this.refs[this.tabLinkKey][index]) { - this.addClass(this.refs[this.tabLinkKey][index], 'active'); - this.addClass(this.refs[this.tabLinkKey][index], 'formio-tab-link-active'); - } - this.triggerChange(); - } - - beforeFocus(component) { - if ('beforeFocus' in this.parent) { - this.parent.beforeFocus(this); - } - const tabIndex = this.tabs.findIndex((tab) => { - return tab.some((comp) => comp === component); - }); - if (tabIndex !== -1 && this.currentTab !== tabIndex) { - this.setTab(tabIndex); - } - } - - setErrorClasses(elements, dirty, hasErrors, hasMessages, element = this.element) { - if (this.component.modalEdit) { - super.setErrorClasses(elements, dirty, hasErrors, hasMessages, element); - } - - elements.forEach((element) => { - this.addClass(element, 'is-invalid'); - - if (element.getAttribute('ref') !== 'openModal') { - if (this.options.highlightErrors) { - this.addClass(element, 'tab-error'); - } - else { - this.addClass(element, 'has-error'); + static schema(...extend) { + return NestedComponent.schema( + { + label: 'Tabs', + type: 'tabs', + input: false, + key: 'tabs', + persistent: false, + tableView: false, + components: [ + { + label: 'Tab 1', + key: 'tab1', + components: [], + }, + ], + verticalLayout: false, + }, + ...extend, + ); + } + + static get builderInfo() { + return { + title: 'Tabs', + group: 'layout', + icon: 'folder-o', + weight: 50, + documentation: '/userguide/form-building/layout-components#tabs', + showPreview: false, + schema: TabsComponent.schema(), + }; + } + + static savedValueTypes() { + return []; + } + + get defaultSchema() { + return TabsComponent.schema(); + } + + get schema() { + const schema = super.schema; + // We need to clone this because the builder uses the "components" reference and this would reset that reference. + const components = _.cloneDeep(this.component.components); + schema.components = components.map((tab, index) => { + tab.components = this.tabs[index].map( + (component) => component.schema, + ); + return tab; + }); + + return schema; + } + + get tabKey() { + return `tab-${this.key}`; + } + + get tabLikey() { + return `tabLi-${this.key}`; + } + + get tabLinkKey() { + return `tabLink-${this.key}`; + } + + constructor(...args) { + super(...args); + this.currentTab = 0; + this.noField = true; + } + + init() { + this.components = []; + this.tabs = []; + _.each(this.component.components, (tab, index) => { + this.tabs[index] = []; + // Initialize empty tabs. + tab.components = tab.components || []; + _.each(tab.components, (comp) => { + const component = this.createComponent(comp); + component.tab = index; + this.tabs[index].push(component); + }); + }); + } + + render() { + return super.render( + this.renderTemplate( + 'tab', + { + tabKey: this.tabKey, + tabLikey: this.tabLikey, + tabLinkKey: this.tabLinkKey, + currentTab: this.currentTab, + tabComponents: this.tabs.map((tab) => + this.renderComponents(tab), + ), + }, + this.options.flatten || this.options.pdf ? 'flat' : null, + ), + ); + } + + attach(element) { + this.loadRefs(element, { + [this.tabLinkKey]: 'multiple', + [this.tabKey]: 'multiple', + [this.tabLikey]: 'multiple', + }); + ['change', 'error'].forEach((event) => + this.on(event, this.handleTabsValidation.bind(this)), + ); + const superAttach = super.attach(element); + this.refs[this.tabLinkKey].forEach((tabLink, index) => { + this.addEventListener(tabLink, 'click', (event) => { + event.preventDefault(); + this.setTab(index); + }); + }); + this.refs[this.tabKey].forEach((tab, index) => { + this.attachComponents( + tab, + this.tabs[index], + this.component.components[index].components, + ); + }); + return superAttach; + } + + detach(all) { + super.detach(all); + } + + /** + * Set the current tab. + * + * @param index + */ + setTab(index) { + if ( + !this.tabs || + !this.tabs[index] || + !this.refs[this.tabKey] || + !this.refs[this.tabKey][index] + ) { + return; } - } - }); - } - clearErrorClasses(elements) { - if (this.options.server || !this.rendered) { - return; + this.currentTab = index; + + _.each(this.refs[this.tabKey], (tab) => { + this.removeClass(tab, 'formio-tab-panel-active'); + tab.style.display = 'none'; + }); + this.addClass(this.refs[this.tabKey][index], 'formio-tab-panel-active'); + this.refs[this.tabKey][index].style.display = 'block'; + + _.each(this.refs[this.tabLinkKey], (tabLink, tabIndex) => { + if (this.refs[this.tabLinkKey][tabIndex]) { + this.removeClass(tabLink, 'active'); + this.removeClass(tabLink, 'formio-tab-link-active'); + } + if (this.refs[this.tabLikey][tabIndex]) { + this.removeClass(this.refs[this.tabLikey][tabIndex], 'active'); + this.removeClass( + this.refs[this.tabLikey][tabIndex], + 'formio-tab-link-container-active', + ); + } + }); + if (this.refs[this.tabLikey][index]) { + this.addClass(this.refs[this.tabLikey][index], 'active'); + this.addClass( + this.refs[this.tabLikey][index], + 'formio-tab-link-container-active', + ); + } + if (this.refs[this.tabLinkKey][index]) { + this.addClass(this.refs[this.tabLinkKey][index], 'active'); + this.addClass( + this.refs[this.tabLinkKey][index], + 'formio-tab-link-active', + ); + } + this.triggerChange(); } - if (this.component.modalEdit) { - const element = Array.isArray(elements) || elements instanceof NodeList ? this.element : elements; - super.clearErrorClasses(element); + beforeFocus(component) { + if ('beforeFocus' in this.parent) { + this.parent.beforeFocus(this); + } + const tabIndex = this.tabs.findIndex((tab) => { + return tab.some((comp) => comp === component); + }); + if (tabIndex !== -1 && this.currentTab !== tabIndex) { + this.setTab(tabIndex); + } } - elements = Array.isArray(elements) || elements instanceof NodeList ? elements : [elements]; + setErrorClasses( + elements, + dirty, + hasErrors, + hasMessages, + element = this.element, + ) { + if (this.component.modalEdit) { + super.setErrorClasses( + elements, + dirty, + hasErrors, + hasMessages, + element, + ); + } - elements.forEach((element) => { - this.removeClass(element, 'is-invalid'); - this.removeClass(element, 'tab-error'); - this.removeClass(element, 'has-error'); - }); - } + elements.forEach((element) => { + this.addClass(element, 'is-invalid'); - handleTabsValidation() { - if (!this.refs[this.tabLinkKey] || !this.refs[this.tabLinkKey].length || !this.tabs.length) { - return; + if (element.getAttribute('ref') !== 'openModal') { + if (this.options.highlightErrors) { + this.addClass(element, 'tab-error'); + } else { + this.addClass(element, 'has-error'); + } + } + }); } - this.clearErrorClasses(this.refs[this.tabLinkKey]); + clearErrorClasses(elements) { + if (this.options.server || !this.rendered) { + return; + } + + if (this.component.modalEdit) { + const element = + Array.isArray(elements) || elements instanceof NodeList + ? this.element + : elements; + super.clearErrorClasses(element); + } - const invalidTabsIndexes = this.tabs.reduce((invalidTabs, tab, tabIndex) => { - const hasComponentWithError = tab.some(comp => !!comp.error); - return hasComponentWithError ? [...invalidTabs, tabIndex] : invalidTabs; - }, []); + elements = + Array.isArray(elements) || elements instanceof NodeList + ? elements + : [elements]; - if (!invalidTabsIndexes.length) { - return; + elements.forEach((element) => { + this.removeClass(element, 'is-invalid'); + this.removeClass(element, 'tab-error'); + this.removeClass(element, 'has-error'); + }); } - const invalidTabs = [...this.refs[this.tabLinkKey]].filter((_, tabIndex) => invalidTabsIndexes.includes(tabIndex)); - this.setErrorClasses(invalidTabs); - } + handleTabsValidation() { + if ( + !this.refs[this.tabLinkKey] || + !this.refs[this.tabLinkKey].length || + !this.tabs.length + ) { + return; + } + + this.clearErrorClasses(this.refs[this.tabLinkKey]); + + const invalidTabsIndexes = this.tabs.reduce( + (invalidTabs, tab, tabIndex) => { + const hasComponentWithError = tab.some((comp) => !!comp.error); + return hasComponentWithError + ? [...invalidTabs, tabIndex] + : invalidTabs; + }, + [], + ); + + if (!invalidTabsIndexes.length) { + return; + } + + const invalidTabs = [...this.refs[this.tabLinkKey]].filter( + (_, tabIndex) => invalidTabsIndexes.includes(tabIndex), + ); + this.setErrorClasses(invalidTabs); + } } diff --git a/src/components/tabs/Tabs.unit.js b/src/components/tabs/Tabs.unit.js index 4a1f265060..c771f394ab 100644 --- a/src/components/tabs/Tabs.unit.js +++ b/src/components/tabs/Tabs.unit.js @@ -2,41 +2,71 @@ import assert from 'power-assert'; import { Formio } from '../../Formio'; import { comp1 } from './fixtures'; -describe('Tabs Component', function() { - it('Test setting error classes when set to modalEdit', function(done) { - const formElement = document.createElement('div'); - Formio.createForm(formElement, { display: 'form', type: 'form', components: [comp1] }).then((form) => { - const comp = form.components[0]; +describe('Tabs Component', function () { + it('Test setting error classes when set to modalEdit', function (done) { + const formElement = document.createElement('div'); + Formio.createForm(formElement, { + display: 'form', + type: 'form', + components: [comp1], + }) + .then((form) => { + const comp = form.components[0]; - const data = { - textField: '', - }; + const data = { + textField: '', + }; - form.checkValidity(data, true, data); + form.checkValidity(data, true, data); - setTimeout(() => { - const openModalWrapper = form.element.querySelector('[ref="openModalWrapper"]'); - assert(openModalWrapper.className.includes('formio-error-wrapper'), 'Should have error class'); - assert(openModalWrapper.className.includes('has-message'), 'Should have class indicating that the component has a message'); + setTimeout(() => { + const openModalWrapper = form.element.querySelector( + '[ref="openModalWrapper"]', + ); + assert( + openModalWrapper.className.includes( + 'formio-error-wrapper', + ), + 'Should have error class', + ); + assert( + openModalWrapper.className.includes('has-message'), + 'Should have class indicating that the component has a message', + ); - const openModalButton = comp.element.querySelector('[ref="openModal"]'); + const openModalButton = + comp.element.querySelector('[ref="openModal"]'); - assert(!openModalButton.className.includes('tab-error'), 'Open modal element should not have a tab-error class'); + assert( + !openModalButton.className.includes('tab-error'), + 'Open modal element should not have a tab-error class', + ); - const validData = { - textField: 'Text', - }; + const validData = { + textField: 'Text', + }; - form.setSubmission({ data: validData }); + form.setSubmission({ data: validData }); - setTimeout(() => { - const openModalWrapper = form.element.querySelector('[ref="openModalWrapper"]'); - assert(!openModalWrapper.className.includes('formio-error-wrapper'), 'Should not have error class'); - assert(!openModalWrapper.className.includes('has-message'), 'Should not have class indicating that the component has a message'); + setTimeout(() => { + const openModalWrapper = form.element.querySelector( + '[ref="openModalWrapper"]', + ); + assert( + !openModalWrapper.className.includes( + 'formio-error-wrapper', + ), + 'Should not have error class', + ); + assert( + !openModalWrapper.className.includes('has-message'), + 'Should not have class indicating that the component has a message', + ); - done(); - }, 250); - }, 200); - }).catch(done); - }); + done(); + }, 250); + }, 200); + }) + .catch(done); + }); }); diff --git a/src/components/tabs/editForm/Tabs.edit.display.js b/src/components/tabs/editForm/Tabs.edit.display.js index 1f1fb19b0d..d7545ade20 100644 --- a/src/components/tabs/editForm/Tabs.edit.display.js +++ b/src/components/tabs/editForm/Tabs.edit.display.js @@ -1,88 +1,88 @@ export default [ - { - key: 'labelPosition', - ignore: true - }, - { - key: 'placeholder', - ignore: true - }, - { - key: 'description', - ignore: true - }, - { - key: 'autofocus', - ignore: true - }, - { - key: 'tooltip', - ignore: true - }, - { - key: 'tabindex', - ignore: true - }, - { - key: 'disabled', - ignore: true - }, - { - key: 'tableView', - ignore: true - }, - { - key: 'hideLabel', - ignore: true - }, - { - weight: 0, - type: 'textfield', - input: true, - key: 'label', - label: 'Label', - placeholder: 'Field Label', - tooltip: 'The label for this field.', - validate: { - required: true + { + key: 'labelPosition', + ignore: true, }, - autofocus: true, - overrideEditForm: true - }, - { - key: 'components', - type: 'datagrid', - input: true, - label: 'Tabs', - weight: 50, - reorder: true, - components: [ - { + { + key: 'placeholder', + ignore: true, + }, + { + key: 'description', + ignore: true, + }, + { + key: 'autofocus', + ignore: true, + }, + { + key: 'tooltip', + ignore: true, + }, + { + key: 'tabindex', + ignore: true, + }, + { + key: 'disabled', + ignore: true, + }, + { + key: 'tableView', + ignore: true, + }, + { + key: 'hideLabel', + ignore: true, + }, + { + weight: 0, type: 'textfield', input: true, key: 'label', - label: 'Label' - }, - { - type: 'textfield', + label: 'Label', + placeholder: 'Field Label', + tooltip: 'The label for this field.', + validate: { + required: true, + }, + autofocus: true, + overrideEditForm: true, + }, + { + key: 'components', + type: 'datagrid', input: true, - key: 'key', - label: 'Key', - allowCalculateOverride: true, - calculateValue: { _camelCase: [{ var: 'row.label' }] } - } - ] - }, - { - weight: 1100, - type: 'checkbox', - label: 'Vertical Layout', - tooltip: 'Make this field display in vertical orientation.', - key: 'verticalLayout', - input: true - }, - { - key: 'hideLabel', - ignore: true - }, + label: 'Tabs', + weight: 50, + reorder: true, + components: [ + { + type: 'textfield', + input: true, + key: 'label', + label: 'Label', + }, + { + type: 'textfield', + input: true, + key: 'key', + label: 'Key', + allowCalculateOverride: true, + calculateValue: { _camelCase: [{ var: 'row.label' }] }, + }, + ], + }, + { + weight: 1100, + type: 'checkbox', + label: 'Vertical Layout', + tooltip: 'Make this field display in vertical orientation.', + key: 'verticalLayout', + input: true, + }, + { + key: 'hideLabel', + ignore: true, + }, ]; diff --git a/src/components/tabs/fixtures/comp1.js b/src/components/tabs/fixtures/comp1.js index f1b0a431d5..53a5f311b3 100644 --- a/src/components/tabs/fixtures/comp1.js +++ b/src/components/tabs/fixtures/comp1.js @@ -1,31 +1,31 @@ -export default { - label: 'Tabs', - components: [ - { - label: 'Tab 1', - key: 'tab1', - components: [] - }, - { - label: 'Tab 2', - key: 'tab2', - components: [ +export default { + label: 'Tabs', + components: [ { - label: 'Text Field', - tableView: true, - validate: { - required: true - }, - key: 'textField', - type: 'textfield', - input: true - } - ] - } - ], - key: 'tabs', - type: 'tabs', - input: false, - tableView: false, - modalEdit: true, + label: 'Tab 1', + key: 'tab1', + components: [], + }, + { + label: 'Tab 2', + key: 'tab2', + components: [ + { + label: 'Text Field', + tableView: true, + validate: { + required: true, + }, + key: 'textField', + type: 'textfield', + input: true, + }, + ], + }, + ], + key: 'tabs', + type: 'tabs', + input: false, + tableView: false, + modalEdit: true, }; diff --git a/src/components/tags/Tags.form.js b/src/components/tags/Tags.form.js index 40fb75a39f..2cf8a526a7 100644 --- a/src/components/tags/Tags.form.js +++ b/src/components/tags/Tags.form.js @@ -2,11 +2,14 @@ import Components from '../Components'; import TagsEditData from './editForm/Tags.edit.data'; -export default function(...extend) { - return Components.baseEditForm([ - { - key: 'data', - components: TagsEditData - }, - ], ...extend); +export default function (...extend) { + return Components.baseEditForm( + [ + { + key: 'data', + components: TagsEditData, + }, + ], + ...extend, + ); } diff --git a/src/components/tags/Tags.js b/src/components/tags/Tags.js index f9ea80e59f..7ce4731ce6 100644 --- a/src/components/tags/Tags.js +++ b/src/components/tags/Tags.js @@ -3,194 +3,217 @@ import Input from '../_classes/input/Input'; import Choices from '@formio/choices.js'; export default class TagsComponent extends Input { - static schema(...extend) { - return Input.schema({ - type: 'tags', - label: 'Tags', - key: 'tags', - delimeter: ',', - storeas: 'string', - maxTags: 0 - }, ...extend); - } - - static get builderInfo() { - return { - title: 'Tags', - icon: 'tags', - group: 'advanced', - documentation: '/userguide/form-building/advanced-components#tags', - weight: 30, - schema: TagsComponent.schema() - }; - } - - static get serverConditionSettings() { - return TagsComponent.conditionOperatorsSettings; - } - - static get conditionOperatorsSettings() { - return { - ...super.conditionOperatorsSettings, - operators: [...super.conditionOperatorsSettings.operators, 'includes', 'notIncludes'], - }; - } - - static savedValueTypes(schema) { - schema = schema || {}; - - return getComponentSavedTypes(schema) ||[componentValueTypes[schema.storeas] || componentValueTypes.string]; - } - - init() { - super.init(); - } - - get emptyValue() { - return (this.component.storeas === 'string') ? '' : []; - } - - get defaultSchema() { - return TagsComponent.schema(); - } - - get inputInfo() { - const info = super.inputInfo; - info.type = 'input'; - info.attr.type = 'text'; - info.changeEvent = 'change'; - return info; - } - - get delimiter() { - return this.component.delimeter || ','; - } - - attachElement(element, index) { - super.attachElement(element, index); - if (!element) { - return; - } - if (this.i18next) { - element.setAttribute('dir', this.i18next.dir()); - } - if (this.choices) { - this.choices.destroy(); - } - - if (!Choices) { - return; - } - - const hasPlaceholder = !!this.component.placeholder; - - this.choices = new Choices(element, { - delimiter: this.delimiter, - editItems: true, - allowHTML: true, - maxItemCount: this.component.maxTags, - removeItemButton: true, - duplicateItemsAllowed: false, - shadowRoot: this.root ? this.root.shadowRoot : null, - placeholder: hasPlaceholder, - placeholderValue: hasPlaceholder ? this.t(this.component.placeholder, { _userInput: true }) : null, - }); - this.choices.itemList.element.tabIndex = element.tabIndex; - this.addEventListener(this.choices.input.element, 'blur', () => { - // Emit event to the native Formio input, so the listener attached in the Input.js will be invoked - element.dispatchEvent(new Event('blur')); - const value = this.choices.input.value; - const maxTagsNumber = this.component.maxTags; - const valuesCount = this.choices.getValue(true).length; - const isRepeatedValue = this.choices.getValue(true).some(existingValue => existingValue.trim() === value.trim()); - - if (value) { - if (maxTagsNumber && valuesCount === maxTagsNumber) { - this.choices.addItems = false; - this.choices.clearInput(); + static schema(...extend) { + return Input.schema( + { + type: 'tags', + label: 'Tags', + key: 'tags', + delimeter: ',', + storeas: 'string', + maxTags: 0, + }, + ...extend, + ); + } + + static get builderInfo() { + return { + title: 'Tags', + icon: 'tags', + group: 'advanced', + documentation: '/userguide/form-building/advanced-components#tags', + weight: 30, + schema: TagsComponent.schema(), + }; + } + + static get serverConditionSettings() { + return TagsComponent.conditionOperatorsSettings; + } + + static get conditionOperatorsSettings() { + return { + ...super.conditionOperatorsSettings, + operators: [ + ...super.conditionOperatorsSettings.operators, + 'includes', + 'notIncludes', + ], + }; + } + + static savedValueTypes(schema) { + schema = schema || {}; + + return ( + getComponentSavedTypes(schema) || [ + componentValueTypes[schema.storeas] || + componentValueTypes.string, + ] + ); + } + + init() { + super.init(); + } + + get emptyValue() { + return this.component.storeas === 'string' ? '' : []; + } + + get defaultSchema() { + return TagsComponent.schema(); + } + + get inputInfo() { + const info = super.inputInfo; + info.type = 'input'; + info.attr.type = 'text'; + info.changeEvent = 'change'; + return info; + } + + get delimiter() { + return this.component.delimeter || ','; + } + + attachElement(element, index) { + super.attachElement(element, index); + if (!element) { + return; } - else if (isRepeatedValue) { - this.choices.clearInput(); + if (this.i18next) { + element.setAttribute('dir', this.i18next.dir()); } - else { - this.choices.setValue([value]); - this.choices.clearInput(); - this.choices.hideDropdown(true); - this.updateValue(null, { - modified: true - }); + if (this.choices) { + this.choices.destroy(); } - } - }); - } - - detach() { - if (this.choices) { - this.choices.destroy(); - this.choices = null; - } - super.detach(); - } - - normalizeValue(value) { - if (this.component.storeas === 'string' && Array.isArray(value)) { - return value.join(this.delimiter); - } - else if (this.component.storeas === 'array' && typeof value === 'string') { - return value.split(this.delimiter).filter(result => result); - } - return value; - } - - setValue(value, flags = {}) { - const changed = super.setValue(value, flags); - if (this.choices) { - let dataValue = this.dataValue; - this.choices.removeActiveItems(); - if (dataValue) { - if (typeof dataValue === 'string') { - dataValue = dataValue.split(this.delimiter).filter(result => result); + + if (!Choices) { + return; } - const value = Array.isArray(dataValue) ? dataValue : [dataValue]; - this.choices.setValue(value.map((val) => this.sanitize(val, this.shouldSanitizeValue))); - } - } - return changed; - } - set disabled(disabled) { - super.disabled = disabled; - if (!this.choices) { - return; + const hasPlaceholder = !!this.component.placeholder; + + this.choices = new Choices(element, { + delimiter: this.delimiter, + editItems: true, + allowHTML: true, + maxItemCount: this.component.maxTags, + removeItemButton: true, + duplicateItemsAllowed: false, + shadowRoot: this.root ? this.root.shadowRoot : null, + placeholder: hasPlaceholder, + placeholderValue: hasPlaceholder + ? this.t(this.component.placeholder, { _userInput: true }) + : null, + }); + this.choices.itemList.element.tabIndex = element.tabIndex; + this.addEventListener(this.choices.input.element, 'blur', () => { + // Emit event to the native Formio input, so the listener attached in the Input.js will be invoked + element.dispatchEvent(new Event('blur')); + const value = this.choices.input.value; + const maxTagsNumber = this.component.maxTags; + const valuesCount = this.choices.getValue(true).length; + const isRepeatedValue = this.choices + .getValue(true) + .some((existingValue) => existingValue.trim() === value.trim()); + + if (value) { + if (maxTagsNumber && valuesCount === maxTagsNumber) { + this.choices.addItems = false; + this.choices.clearInput(); + } else if (isRepeatedValue) { + this.choices.clearInput(); + } else { + this.choices.setValue([value]); + this.choices.clearInput(); + this.choices.hideDropdown(true); + this.updateValue(null, { + modified: true, + }); + } + } + }); } - if (disabled) { - this.choices.disable(); + + detach() { + if (this.choices) { + this.choices.destroy(); + this.choices = null; + } + super.detach(); } - else { - this.choices.enable(); + + normalizeValue(value) { + if (this.component.storeas === 'string' && Array.isArray(value)) { + return value.join(this.delimiter); + } else if ( + this.component.storeas === 'array' && + typeof value === 'string' + ) { + return value.split(this.delimiter).filter((result) => result); + } + return value; } - } - get disabled() { - return super.disabled; - } + setValue(value, flags = {}) { + const changed = super.setValue(value, flags); + if (this.choices) { + let dataValue = this.dataValue; + this.choices.removeActiveItems(); + if (dataValue) { + if (typeof dataValue === 'string') { + dataValue = dataValue + .split(this.delimiter) + .filter((result) => result); + } + const value = Array.isArray(dataValue) + ? dataValue + : [dataValue]; + this.choices.setValue( + value.map((val) => + this.sanitize(val, this.shouldSanitizeValue), + ), + ); + } + } + return changed; + } - focus() { - if (this.refs.input && this.refs.input.length) { - this.refs.input[0].parentNode.lastChild.focus(); + set disabled(disabled) { + super.disabled = disabled; + if (!this.choices) { + return; + } + if (disabled) { + this.choices.disable(); + } else { + this.choices.enable(); + } } - } - getValueAsString(value) { - if (!value) { - return ''; + get disabled() { + return super.disabled; } - if (Array.isArray(value)) { - return value.join(`${this.delimiter || ','} `); + focus() { + if (this.refs.input && this.refs.input.length) { + this.refs.input[0].parentNode.lastChild.focus(); + } } - const stringValue = value.toString(); - return this.sanitize(stringValue, this.shouldSanitizeValue); - } + getValueAsString(value) { + if (!value) { + return ''; + } + + if (Array.isArray(value)) { + return value.join(`${this.delimiter || ','} `); + } + + const stringValue = value.toString(); + return this.sanitize(stringValue, this.shouldSanitizeValue); + } } diff --git a/src/components/tags/Tags.unit.js b/src/components/tags/Tags.unit.js index c2efa03bdb..f2306b4682 100644 --- a/src/components/tags/Tags.unit.js +++ b/src/components/tags/Tags.unit.js @@ -4,159 +4,199 @@ import assert from 'power-assert'; import modalTagsComponent from '../../../test/formtest/modalTagsComponent'; import _ from 'lodash'; -import { - comp1, - comp2, - comp3, - comp4, - comp5, - comp6, -} from './fixtures'; +import { comp1, comp2, comp3, comp4, comp5, comp6 } from './fixtures'; import { Formio } from '../../Formio'; -describe('Tags Component', function() { - it('Should build a tags component', function() { - return Harness.testCreate(TagsComponent, comp1); - }); - - it('Should set placeholder', function(done) { - Harness.testCreate(TagsComponent, comp4).then((component) => { - assert.equal(component.choices.config.placeholder, true); - assert.equal(component.choices.config.placeholderValue, component.component.placeholder); - assert.equal(component.choices.input.element.attributes.placeholder.value, component.component.placeholder); - done(); - }).catch(done); - }); - - it('Should not allow to add non-unique tags on blur', function(done) { - const element = document.createElement('div'); - Formio.createForm(element, { type: 'form', display: 'form', components: [comp2] }).then(form => { - const component = form.getComponent(['tags']); - const values = ['test', 'test1', 'test']; - Harness.setTagsValue(values, component); - assert.equal(component.choices.getValue(true).length, 2); - done(); - }).catch(done); - }); - - it('Should not exceed maxTags limit', function(done) { - const element = document.createElement('div'); - Formio.createForm(element, { type: 'form', display: 'form', components: [comp2] }).then(form => { - const component = form.getComponent(['tags']); - const values = ['1', '2', '3', '4', '5']; - Harness.setTagsValue(values, component); - - setTimeout(() => { - assert.equal(component.choices.getValue(true).length, 4); - done(); - }, 400); - }).catch(done); - }); - - it('Check getValueAsString', function(done) { - const element = document.createElement('div'); - Formio.createForm(element, modalTagsComponent) - .then(form => { - const component = form.getComponent(['tags']); - const modalWindow = component.componentModal.refs.modalContents; - - Harness.setTagsValue(['test', 'test1', 'test2'], component); - Harness.dispatchEvent('click', modalWindow, '[ref="modalSave"]'); - - setTimeout(() => { - const modalPreview = component.element.querySelector('[ref="openModal"]'); - assert.equal(modalPreview.textContent.trim(), 'test, test1, test2', 'All tags should be rendered inside Modal Preview'); - form.destroy(); - done(); - }, 250); - }) - .catch(done); - }); - - it('Should use correct delimiter for value', function(done) { - const form = _.cloneDeep(comp3); - const element = document.createElement('div'); - form.components[0].delimeter = '-'; - - Formio.createForm(element, form).then(form => { - const tags = form.getComponent('tags'); - const value = ['tag1','tag2', 'tag3']; - - tags.setValue(value); - - setTimeout(() => { - assert.equal(tags.getValue(), value.join('-')); - assert.equal(tags.dataValue, value.join('-')); - assert.equal(form.submission.data.tags, value.join('-')); - - document.innerHTML = ''; - done(); - }, 200); - }).catch(done); - }); - - it('Should use store value as array', function(done) { - const form = _.cloneDeep(comp3); - const element = document.createElement('div'); - form.components[0].storeas = 'array'; - - Formio.createForm(element, form).then(form => { - const tags = form.getComponent('tags'); - const value = ['tag1','tag2', 'tag3']; - - tags.setValue(value); - - setTimeout(() => { - assert.deepEqual(tags.getValue(), value.join(',')); - assert.deepEqual(form.submission.data.tags, value); - assert.equal(tags.dataValue, value); - - document.innerHTML = ''; - done(); - }, 200); - }).catch(done); - }); - - it('Should show the specified delimiter when get value as string', function(done) { - const form = _.cloneDeep(comp5); - const element = document.createElement('div'); - - Formio.createForm(element, form).then(form => { - const tags = form.getComponent('tags'); - const value = ['tag1', 'tag2', 'tag3']; - - tags.setValue(value); - - setTimeout(() => { - assert.deepEqual(tags.getValue(), value.join(tags.component.delimeter)); - assert.deepEqual(form.submission.data.tags, value); - assert.equal(tags.dataValue, value); - assert.equal(tags.getValueAsString(value), value.join(`${tags.component.delimeter} `)); - - document.innerHTML = ''; - done(); - }, 200); - }).catch(done); - }); - - it('OnBlur validation should work properly with Tags component', function(done) { - const element = document.createElement('div'); - - Formio.createForm(element, comp6).then(form => { - const tags = form.getComponent('tags'); - // tags.setValue(['1', '2', '3']); - Harness.setTagsValue(['test', 'test1', 'test2'], tags); - tags.choices.input.element.focus(); - - setTimeout(() => { - assert(!tags.error, 'Tags should be valid while changing'); - tags.choices.input.element.dispatchEvent(new Event('blur')); - - setTimeout(() => { - assert(tags.error, 'Should set error after Tags component was blurred'); - done(); - }, 500); - }, 300); - }).catch(done); - }); +describe('Tags Component', function () { + it('Should build a tags component', function () { + return Harness.testCreate(TagsComponent, comp1); + }); + + it('Should set placeholder', function (done) { + Harness.testCreate(TagsComponent, comp4) + .then((component) => { + assert.equal(component.choices.config.placeholder, true); + assert.equal( + component.choices.config.placeholderValue, + component.component.placeholder, + ); + assert.equal( + component.choices.input.element.attributes.placeholder + .value, + component.component.placeholder, + ); + done(); + }) + .catch(done); + }); + + it('Should not allow to add non-unique tags on blur', function (done) { + const element = document.createElement('div'); + Formio.createForm(element, { + type: 'form', + display: 'form', + components: [comp2], + }) + .then((form) => { + const component = form.getComponent(['tags']); + const values = ['test', 'test1', 'test']; + Harness.setTagsValue(values, component); + assert.equal(component.choices.getValue(true).length, 2); + done(); + }) + .catch(done); + }); + + it('Should not exceed maxTags limit', function (done) { + const element = document.createElement('div'); + Formio.createForm(element, { + type: 'form', + display: 'form', + components: [comp2], + }) + .then((form) => { + const component = form.getComponent(['tags']); + const values = ['1', '2', '3', '4', '5']; + Harness.setTagsValue(values, component); + + setTimeout(() => { + assert.equal(component.choices.getValue(true).length, 4); + done(); + }, 400); + }) + .catch(done); + }); + + it('Check getValueAsString', function (done) { + const element = document.createElement('div'); + Formio.createForm(element, modalTagsComponent) + .then((form) => { + const component = form.getComponent(['tags']); + const modalWindow = component.componentModal.refs.modalContents; + + Harness.setTagsValue(['test', 'test1', 'test2'], component); + Harness.dispatchEvent( + 'click', + modalWindow, + '[ref="modalSave"]', + ); + + setTimeout(() => { + const modalPreview = + component.element.querySelector('[ref="openModal"]'); + assert.equal( + modalPreview.textContent.trim(), + 'test, test1, test2', + 'All tags should be rendered inside Modal Preview', + ); + form.destroy(); + done(); + }, 250); + }) + .catch(done); + }); + + it('Should use correct delimiter for value', function (done) { + const form = _.cloneDeep(comp3); + const element = document.createElement('div'); + form.components[0].delimeter = '-'; + + Formio.createForm(element, form) + .then((form) => { + const tags = form.getComponent('tags'); + const value = ['tag1', 'tag2', 'tag3']; + + tags.setValue(value); + + setTimeout(() => { + assert.equal(tags.getValue(), value.join('-')); + assert.equal(tags.dataValue, value.join('-')); + assert.equal(form.submission.data.tags, value.join('-')); + + document.innerHTML = ''; + done(); + }, 200); + }) + .catch(done); + }); + + it('Should use store value as array', function (done) { + const form = _.cloneDeep(comp3); + const element = document.createElement('div'); + form.components[0].storeas = 'array'; + + Formio.createForm(element, form) + .then((form) => { + const tags = form.getComponent('tags'); + const value = ['tag1', 'tag2', 'tag3']; + + tags.setValue(value); + + setTimeout(() => { + assert.deepEqual(tags.getValue(), value.join(',')); + assert.deepEqual(form.submission.data.tags, value); + assert.equal(tags.dataValue, value); + + document.innerHTML = ''; + done(); + }, 200); + }) + .catch(done); + }); + + it('Should show the specified delimiter when get value as string', function (done) { + const form = _.cloneDeep(comp5); + const element = document.createElement('div'); + + Formio.createForm(element, form) + .then((form) => { + const tags = form.getComponent('tags'); + const value = ['tag1', 'tag2', 'tag3']; + + tags.setValue(value); + + setTimeout(() => { + assert.deepEqual( + tags.getValue(), + value.join(tags.component.delimeter), + ); + assert.deepEqual(form.submission.data.tags, value); + assert.equal(tags.dataValue, value); + assert.equal( + tags.getValueAsString(value), + value.join(`${tags.component.delimeter} `), + ); + + document.innerHTML = ''; + done(); + }, 200); + }) + .catch(done); + }); + + it('OnBlur validation should work properly with Tags component', function (done) { + const element = document.createElement('div'); + + Formio.createForm(element, comp6) + .then((form) => { + const tags = form.getComponent('tags'); + // tags.setValue(['1', '2', '3']); + Harness.setTagsValue(['test', 'test1', 'test2'], tags); + tags.choices.input.element.focus(); + + setTimeout(() => { + assert(!tags.error, 'Tags should be valid while changing'); + tags.choices.input.element.dispatchEvent(new Event('blur')); + + setTimeout(() => { + assert( + tags.error, + 'Should set error after Tags component was blurred', + ); + done(); + }, 500); + }, 300); + }) + .catch(done); + }); }); diff --git a/src/components/tags/editForm/Tags.edit.data.js b/src/components/tags/editForm/Tags.edit.data.js index 0f3fb73f8f..ab4e4f80c5 100644 --- a/src/components/tags/editForm/Tags.edit.data.js +++ b/src/components/tags/editForm/Tags.edit.data.js @@ -1,37 +1,38 @@ export default [ - { - key: 'multiple', - ignore: true - }, - { - weight: 20, - type: 'textfield', - input: true, - key: 'delimeter', - label: 'Delimiter', - tooltip: 'What is used to separate the tags.' - }, - { - weight: 22, - type: 'number', - input: true, - key: 'maxTags', - label: 'Max Tags', - defaultValue: 0, - tooltip: 'The maximum amount of tags that can be added. 0 for infinity.' - }, - { - weight: 24, - type: 'select', - input: true, - key: 'storeas', - label: 'Store As', - dataSrc: 'values', - data: { - values: [ - { label: 'String (CSV)', value: 'string' }, - { label: 'Array of Tags', value: 'array' } - ] - } - } + { + key: 'multiple', + ignore: true, + }, + { + weight: 20, + type: 'textfield', + input: true, + key: 'delimeter', + label: 'Delimiter', + tooltip: 'What is used to separate the tags.', + }, + { + weight: 22, + type: 'number', + input: true, + key: 'maxTags', + label: 'Max Tags', + defaultValue: 0, + tooltip: + 'The maximum amount of tags that can be added. 0 for infinity.', + }, + { + weight: 24, + type: 'select', + input: true, + key: 'storeas', + label: 'Store As', + dataSrc: 'values', + data: { + values: [ + { label: 'String (CSV)', value: 'string' }, + { label: 'Array of Tags', value: 'array' }, + ], + }, + }, ]; diff --git a/src/components/tags/fixtures/comp1.js b/src/components/tags/fixtures/comp1.js index f339187e70..26a4b25227 100644 --- a/src/components/tags/fixtures/comp1.js +++ b/src/components/tags/fixtures/comp1.js @@ -1,20 +1,18 @@ export default { - 'input': true, - 'tableView': false, - 'label': 'Tags', - 'key': 'tags', - 'placeholder': '', - 'prefix': '', - 'suffix': '', - 'protected': true, - 'persistent': true, - 'type': 'tags', - 'tags': [ - - ], - 'conditional': { - 'show': '', - 'when': null, - 'eq': '' - } + input: true, + tableView: false, + label: 'Tags', + key: 'tags', + placeholder: '', + prefix: '', + suffix: '', + protected: true, + persistent: true, + type: 'tags', + tags: [], + conditional: { + show: '', + when: null, + eq: '', + }, }; diff --git a/src/components/tags/fixtures/comp2.js b/src/components/tags/fixtures/comp2.js index b98c3e63df..6a344263dc 100644 --- a/src/components/tags/fixtures/comp2.js +++ b/src/components/tags/fixtures/comp2.js @@ -1,10 +1,9 @@ export default { - 'label': 'Tags', - 'tableView': false, - 'maxTags': 4, - 'calculateServer': false, - 'key': 'tags', - 'type': 'tags', - 'input': true + label: 'Tags', + tableView: false, + maxTags: 4, + calculateServer: false, + key: 'tags', + type: 'tags', + input: true, }; - diff --git a/src/components/tags/fixtures/comp3.js b/src/components/tags/fixtures/comp3.js index 4c72da4694..a96c654880 100644 --- a/src/components/tags/fixtures/comp3.js +++ b/src/components/tags/fixtures/comp3.js @@ -1,18 +1,24 @@ export default { - type: 'form', - components: [ - { label: 'Tags', tableView: false, key: 'tags', type: 'tags', input: true }, - { - label: 'Submit', - showValidations: false, - tableView: false, - key: 'submit', - type: 'button', - input: true - } - ], - title: 'test11', - display: 'form', - name: 'test11', - path: 'test11', + type: 'form', + components: [ + { + label: 'Tags', + tableView: false, + key: 'tags', + type: 'tags', + input: true, + }, + { + label: 'Submit', + showValidations: false, + tableView: false, + key: 'submit', + type: 'button', + input: true, + }, + ], + title: 'test11', + display: 'form', + name: 'test11', + path: 'test11', }; diff --git a/src/components/tags/fixtures/comp4.js b/src/components/tags/fixtures/comp4.js index f85c9d1ebe..e070bcd585 100644 --- a/src/components/tags/fixtures/comp4.js +++ b/src/components/tags/fixtures/comp4.js @@ -1,8 +1,8 @@ export default { - label: 'Tags', - placeholder: 'some text', - tableView: false, - key: 'tags', - type: 'tags', - input: true + label: 'Tags', + placeholder: 'some text', + tableView: false, + key: 'tags', + type: 'tags', + input: true, }; diff --git a/src/components/tags/fixtures/comp5.js b/src/components/tags/fixtures/comp5.js index 0bd1865646..2481b203e1 100644 --- a/src/components/tags/fixtures/comp5.js +++ b/src/components/tags/fixtures/comp5.js @@ -1,22 +1,23 @@ export default { - 'type': 'form', - 'display': 'form', - 'components': [ - { - 'label': 'Tags', - 'tableView': true, - 'delimeter': ';', - 'storeas': 'array', - 'key': 'tags', - 'type': 'tags', - 'input': true, - }, { - 'type': 'button', - 'label': 'Submit', - 'key': 'submit', - 'disableOnInvalid': true, - 'input': true, - 'tableView': false, - }, - ], + type: 'form', + display: 'form', + components: [ + { + label: 'Tags', + tableView: true, + delimeter: ';', + storeas: 'array', + key: 'tags', + type: 'tags', + input: true, + }, + { + type: 'button', + label: 'Submit', + key: 'submit', + disableOnInvalid: true, + input: true, + tableView: false, + }, + ], }; diff --git a/src/components/tags/fixtures/comp6.js b/src/components/tags/fixtures/comp6.js index d3baa5ff8d..be12520cd3 100644 --- a/src/components/tags/fixtures/comp6.js +++ b/src/components/tags/fixtures/comp6.js @@ -1,18 +1,18 @@ export default { - type: 'form', - display: 'form', - components: [ - { - label: 'Tags', - tableView: false, - storeas: 'array', - validate: { - custom: "valid = data && data.tags.length <= 2 ? true : 'You cannot add more than 2 items'" - }, - validateOn: 'blur', - key: 'tags', - type: 'tags', - input: true - }, - ] + type: 'form', + display: 'form', + components: [ + { + label: 'Tags', + tableView: false, + storeas: 'array', + validate: { + custom: "valid = data && data.tags.length <= 2 ? true : 'You cannot add more than 2 items'", + }, + validateOn: 'blur', + key: 'tags', + type: 'tags', + input: true, + }, + ], }; diff --git a/src/components/tags/fixtures/values.js b/src/components/tags/fixtures/values.js index 1518fab4a1..dd376426fc 100644 --- a/src/components/tags/fixtures/values.js +++ b/src/components/tags/fixtures/values.js @@ -1,4 +1 @@ -export default [ - 'a, b, c', - ['a', 'b', 'c'], -]; +export default ['a, b, c', ['a', 'b', 'c']]; diff --git a/src/components/textarea/TextArea.form.js b/src/components/textarea/TextArea.form.js index b8cf874c10..98c2d3bc0e 100644 --- a/src/components/textarea/TextArea.form.js +++ b/src/components/textarea/TextArea.form.js @@ -2,15 +2,18 @@ import textEditForm from '../textfield/TextField.form'; import TextAreaEditDisplay from './editForm/TextArea.edit.display'; import TextAreaEditValidation from './editForm/TextArea.edit.validation'; -export default function(...extend) { - return textEditForm([ - { - key: 'display', - components: TextAreaEditDisplay - }, - { - key: 'validation', - components: TextAreaEditValidation - }, - ], ...extend); +export default function (...extend) { + return textEditForm( + [ + { + key: 'display', + components: TextAreaEditDisplay, + }, + { + key: 'validation', + components: TextAreaEditValidation, + }, + ], + ...extend, + ); } diff --git a/src/components/textarea/TextArea.js b/src/components/textarea/TextArea.js index b9fd93b366..9db79b834c 100644 --- a/src/components/textarea/TextArea.js +++ b/src/components/textarea/TextArea.js @@ -4,611 +4,747 @@ import _ from 'lodash'; import { uniqueName, getBrowserInfo } from '../../utils/utils'; export default class TextAreaComponent extends TextFieldComponent { - static schema(...extend) { - return TextFieldComponent.schema({ - type: 'textarea', - label: 'Text Area', - key: 'textArea', - rows: 3, - wysiwyg: false, - editor: '', - fixedSize: true, - inputFormat: 'html', - validate: { - minWords: '', - maxWords: '' - } - }, ...extend); - } - - static get builderInfo() { - return { - title: 'Text Area', - group: 'basic', - icon: 'font', - documentation: '/userguide/form-building/form-components#text-area', - weight: 20, - schema: TextAreaComponent.schema() - }; - } - - init() { - super.init(); - this.editors = []; - this.editorsReady = []; - this.updateSizes = []; - - // Never submit on enter for text areas. - this.options.submitOnEnter = false; - } - - get defaultSchema() { - return TextAreaComponent.schema(); - } - - get inputInfo() { - const info = super.inputInfo; - info.type = this.component.wysiwyg ? 'div' : 'textarea'; - if (this.component.rows) { - info.attr.rows = this.component.rows; - } - return info; - } - - validateMultiple() { - return !this.isJsonValue; - } - - renderElement(value, index) { - const info = this.inputInfo; - info.attr = info.attr || {}; - info.content = value; - if ((this.options.readOnly || this.disabled) && !this.isHtmlRenderMode()) { - const elementStyle = this.info.attr.style || ''; - const children = `
    `; - - return this.renderTemplate('well', { - children, - nestedKey: this.key, - value - }); - } - - return this.renderTemplate('input', { - prefix: this.prefix, - suffix: this.suffix, - input: info, - value, - index - }); - } - - get autoExpand() { - return this.component.autoExpand; - } - - /** - * Updates the editor value. - * - * @param newValue - */ - updateEditorValue(index, newValue) { - newValue = this.getConvertedValue(this.trimBlanks(newValue)); - const dataValue = this.dataValue; - if (this.component.multiple && Array.isArray(dataValue)) { - const newArray = _.clone(dataValue); - newArray[index] = newValue; - newValue = newArray; - } - - if ((!_.isEqual(newValue, dataValue)) && (!_.isEmpty(newValue) || !_.isEmpty(dataValue))) { - this.updateValue(newValue, { - modified: !this.autoModified - }, index); - } - this.autoModified = false; - } - - attachElement(element, index) { - if (this.autoExpand && (this.isPlain || this.options.readOnly || this.options.htmlView)) { - if (element.nodeName === 'TEXTAREA') { - this.addAutoExpanding(element, index); - } - } - - if (this.options.readOnly) { - return element; - } - - if (this.component.wysiwyg && !this.component.editor) { - this.component.editor = 'ckeditor'; - } - - let settings = _.isEmpty(this.component.wysiwyg) ? - this.wysiwygDefault[this.component.editor] || this.wysiwygDefault.default - : this.component.wysiwyg; - - // Keep track of when this editor is ready. - this.editorsReady[index] = new Promise((editorReady) => { - // Attempt to add a wysiwyg editor. In order to add one, it must be included on the global scope. - switch (this.component.editor) { - case 'ace': - if (!settings) { - settings = {}; - } - settings.mode = this.component.as ? `ace/mode/${this.component.as}` : 'ace/mode/javascript'; - this.addAce(element, settings, (newValue) => this.updateEditorValue(index, newValue)).then((ace) => { - this.editors[index] = ace; - let dataValue = this.dataValue; - dataValue = (this.component.multiple && Array.isArray(dataValue)) ? dataValue[index] : dataValue; - ace.setValue(this.setConvertedValue(dataValue, index)); - editorReady(ace); - return ace; - }).catch(err => console.warn(err)); - break; - case 'quill': - // Normalize the configurations for quill. - if (Object.prototype.hasOwnProperty.call(settings, 'toolbarGroups') || Object.prototype.hasOwnProperty.call(settings, 'toolbar')) { - console.warn('The WYSIWYG settings are configured for CKEditor. For this renderer, you will need to use configurations for the Quill Editor. See https://quilljs.com/docs/configuration for more information.'); - settings = this.wysiwygDefault.quill; - } - - // Add the quill editor. - this.addQuill( - element, - settings, () => this.updateEditorValue(index, this.editors[index].root.innerHTML) - ).then((quill) => { - this.editors[index] = quill; - if (this.component.isUploadEnabled) { - const _this = this; - quill.getModule('uploader').options.handler = function(...args) { - //we need initial 'this' because quill calls this method with its own context and we need some inner quill methods exposed in it - //we also need current component instance as we use some fields and methods from it as well - _this.imageHandler.call(_this, this, ...args); - }; - } - quill.root.spellcheck = this.component.spellcheck; - if (this.options.readOnly || this.disabled) { - quill.disable(); - } + static schema(...extend) { + return TextFieldComponent.schema( + { + type: 'textarea', + label: 'Text Area', + key: 'textArea', + rows: 3, + wysiwyg: false, + editor: '', + fixedSize: true, + inputFormat: 'html', + validate: { + minWords: '', + maxWords: '', + }, + }, + ...extend, + ); + } - let dataValue = this.dataValue; - dataValue = (this.component.multiple && Array.isArray(dataValue)) ? dataValue[index] : dataValue; - quill.setContents(quill.clipboard.convert({ html: this.setConvertedValue(dataValue, index) })); - editorReady(quill); - return quill; - }).catch(err => console.warn(err)); - break; - case 'ckeditor': - settings = settings || {}; - settings.rows = this.component.rows; - this.addCKE(element, settings, (newValue) => this.updateEditorValue(index, newValue)) - .then((editor) => { - this.editors[index] = editor; - let dataValue = this.dataValue; - dataValue = (this.component.multiple && Array.isArray(dataValue)) ? dataValue[index] : dataValue; - const value = this.setConvertedValue(dataValue, index); - const isReadOnly = this.options.readOnly || this.disabled; - // Use ckeditor 4 in IE browser - if (getBrowserInfo().ie) { - editor.on('instanceReady', () => { - editor.setReadOnly(isReadOnly); - editor.setData(value); - }); - } - else { - const numRows = parseInt(this.component.rows, 10); - - if (_.isFinite(numRows) && _.has(editor, 'ui.view.editable.editableElement')) { - // Default height is 21px with 10px margin + a 14px top margin. - const editorHeight = (numRows * 31) + 14; - editor.ui.view.editable.editableElement.style.height = `${(editorHeight)}px`; - } - editor.isReadOnly = isReadOnly; - editor.data.set(value); - } + static get builderInfo() { + return { + title: 'Text Area', + group: 'basic', + icon: 'font', + documentation: '/userguide/form-building/form-components#text-area', + weight: 20, + schema: TextAreaComponent.schema(), + }; + } + + init() { + super.init(); + this.editors = []; + this.editorsReady = []; + this.updateSizes = []; + + // Never submit on enter for text areas. + this.options.submitOnEnter = false; + } + + get defaultSchema() { + return TextAreaComponent.schema(); + } + + get inputInfo() { + const info = super.inputInfo; + info.type = this.component.wysiwyg ? 'div' : 'textarea'; + if (this.component.rows) { + info.attr.rows = this.component.rows; + } + return info; + } + + validateMultiple() { + return !this.isJsonValue; + } - editorReady(editor); - return editor; + renderElement(value, index) { + const info = this.inputInfo; + info.attr = info.attr || {}; + info.content = value; + if ( + (this.options.readOnly || this.disabled) && + !this.isHtmlRenderMode() + ) { + const elementStyle = this.info.attr.style || ''; + const children = `
    `; + + return this.renderTemplate('well', { + children, + nestedKey: this.key, + value, }); - break; - default: - super.attachElement(element, index); - break; - } - }); - - return element; - } - - attach(element) { - const attached = super.attach(element); - // Make sure we restore the value after attaching since wysiwygs and readonly texts need an additional set. - this.restoreValue(); - return attached; - } - - imageHandler(moduleInstance, range, files) { - const quillInstance = moduleInstance.quill; - - if (!files || !files.length) { - console.warn('No files selected'); - return; - } - - quillInstance.enable(false); - const { uploadStorage, uploadUrl, uploadOptions, uploadDir, fileKey } = this.component; - let requestData; - this.fileService - .uploadFile( - uploadStorage, - files[0], - uniqueName(files[0].name), - uploadDir || '', //should pass empty string if undefined - null, - uploadUrl, - uploadOptions, - fileKey - ) - .then(result => { - requestData = result; - return this.fileService.downloadFile(result); - }) - .then(result => { - quillInstance.enable(true); - const Delta = Quill.import('delta'); - quillInstance.updateContents(new Delta() - .retain(range.index) - .delete(range.length) - .insert( - { - image: result.url - }, - { - alt: JSON.stringify(requestData), - }) - , Quill.sources.USER); - }).catch(error => { - console.warn('Quill image upload failed'); - console.warn(error); - quillInstance.enable(true); - }); - } - - get isPlain() { - return (!this.component.wysiwyg && !this.component.editor); - } - - get htmlView() { - return this.options.readOnly && (this.component.editor || this.component.wysiwyg); - } - - setValueAt(index, value, flags = {}) { - super.setValueAt(index, value, flags); - - if (this.editorsReady[index]) { - const setEditorsValue = (flags) => (editor) => { - if (!flags.skipWysiwyg) { - this.autoModified = true; - switch (this.component.editor) { - case 'ace': - editor.setValue(this.setConvertedValue(value, index)); - break; - case 'quill': - if (this.component.isUploadEnabled) { - this.setAsyncConvertedValue(value) - .then(result => { - const content = editor.clipboard.convert({ html: result }); - editor.setContents(content); - }); - } - else { - const convertedValue = this.setConvertedValue(value, index); - const content = editor.clipboard.convert({ html: convertedValue }); - editor.setContents(content); - } - break; - case 'ckeditor': - editor.data.set(this.setConvertedValue(value, index)); - break; - } } - }; - - this.editorsReady[index].then(setEditorsValue(_.clone(flags))); - } - } - - setValue(value, flags = {}) { - if (this.isPlain || this.options.readOnly || this.disabled) { - value = (this.component.multiple && Array.isArray(value)) ? - value.map((val, index) => this.setConvertedValue(val, index)) : - this.setConvertedValue(value); - return super.setValue(value, flags); - } - flags.skipWysiwyg = value === '' && flags.resetValue ? false : _.isEqual(value, this.getValue()); - return super.setValue(value, flags); - } - - setContent(element, content, forceSanitize) { - super.setContent(element, content, forceSanitize, { - addAttr: ['allow', 'allowfullscreen', 'frameborder', 'scrolling'], - addTags: ['iframe'], - }); - } - - setReadOnlyValue(value, index) { - index = index || 0; - if (this.options.readOnly || this.disabled) { - if (this.refs.input && this.refs.input[index]) { - if (this.component.inputFormat === 'plain') { - this.refs.input[index].innerText = this.isPlain ? value : this.interpolate(value, {}, { noeval: true }); + + return this.renderTemplate('input', { + prefix: this.prefix, + suffix: this.suffix, + input: info, + value, + index, + }); + } + + get autoExpand() { + return this.component.autoExpand; + } + + /** + * Updates the editor value. + * + * @param newValue + */ + updateEditorValue(index, newValue) { + newValue = this.getConvertedValue(this.trimBlanks(newValue)); + const dataValue = this.dataValue; + if (this.component.multiple && Array.isArray(dataValue)) { + const newArray = _.clone(dataValue); + newArray[index] = newValue; + newValue = newArray; } - else { - this.setContent(this.refs.input[index], this.isPlain ? value : this.interpolate(value, {}, { noeval: true }), this.shouldSanitizeValue); + + if ( + !_.isEqual(newValue, dataValue) && + (!_.isEmpty(newValue) || !_.isEmpty(dataValue)) + ) { + this.updateValue( + newValue, + { + modified: !this.autoModified, + }, + index, + ); } - } + this.autoModified = false; } - } - get isJsonValue() { - return this.component.as && this.component.as === 'json'; - } + attachElement(element, index) { + if ( + this.autoExpand && + (this.isPlain || this.options.readOnly || this.options.htmlView) + ) { + if (element.nodeName === 'TEXTAREA') { + this.addAutoExpanding(element, index); + } + } + + if (this.options.readOnly) { + return element; + } - setConvertedValue(value, index) { - if (this.isJsonValue && !_.isNil(value)) { - try { - value = JSON.stringify(value, null, 2); - } - catch (err) { - console.warn(err); - } + if (this.component.wysiwyg && !this.component.editor) { + this.component.editor = 'ckeditor'; + } + + let settings = _.isEmpty(this.component.wysiwyg) + ? this.wysiwygDefault[this.component.editor] || + this.wysiwygDefault.default + : this.component.wysiwyg; + + // Keep track of when this editor is ready. + this.editorsReady[index] = new Promise((editorReady) => { + // Attempt to add a wysiwyg editor. In order to add one, it must be included on the global scope. + switch (this.component.editor) { + case 'ace': + if (!settings) { + settings = {}; + } + settings.mode = this.component.as + ? `ace/mode/${this.component.as}` + : 'ace/mode/javascript'; + this.addAce(element, settings, (newValue) => + this.updateEditorValue(index, newValue), + ) + .then((ace) => { + this.editors[index] = ace; + let dataValue = this.dataValue; + dataValue = + this.component.multiple && + Array.isArray(dataValue) + ? dataValue[index] + : dataValue; + ace.setValue( + this.setConvertedValue(dataValue, index), + ); + editorReady(ace); + return ace; + }) + .catch((err) => console.warn(err)); + break; + case 'quill': + // Normalize the configurations for quill. + if ( + Object.prototype.hasOwnProperty.call( + settings, + 'toolbarGroups', + ) || + Object.prototype.hasOwnProperty.call( + settings, + 'toolbar', + ) + ) { + console.warn( + 'The WYSIWYG settings are configured for CKEditor. For this renderer, you will need to use configurations for the Quill Editor. See https://quilljs.com/docs/configuration for more information.', + ); + settings = this.wysiwygDefault.quill; + } + + // Add the quill editor. + this.addQuill(element, settings, () => + this.updateEditorValue( + index, + this.editors[index].root.innerHTML, + ), + ) + .then((quill) => { + this.editors[index] = quill; + if (this.component.isUploadEnabled) { + const _this = this; + quill.getModule('uploader').options.handler = + function (...args) { + //we need initial 'this' because quill calls this method with its own context and we need some inner quill methods exposed in it + //we also need current component instance as we use some fields and methods from it as well + _this.imageHandler.call( + _this, + this, + ...args, + ); + }; + } + quill.root.spellcheck = this.component.spellcheck; + if (this.options.readOnly || this.disabled) { + quill.disable(); + } + + let dataValue = this.dataValue; + dataValue = + this.component.multiple && + Array.isArray(dataValue) + ? dataValue[index] + : dataValue; + quill.setContents( + quill.clipboard.convert({ + html: this.setConvertedValue( + dataValue, + index, + ), + }), + ); + editorReady(quill); + return quill; + }) + .catch((err) => console.warn(err)); + break; + case 'ckeditor': + settings = settings || {}; + settings.rows = this.component.rows; + this.addCKE(element, settings, (newValue) => + this.updateEditorValue(index, newValue), + ).then((editor) => { + this.editors[index] = editor; + let dataValue = this.dataValue; + dataValue = + this.component.multiple && Array.isArray(dataValue) + ? dataValue[index] + : dataValue; + const value = this.setConvertedValue(dataValue, index); + const isReadOnly = + this.options.readOnly || this.disabled; + // Use ckeditor 4 in IE browser + if (getBrowserInfo().ie) { + editor.on('instanceReady', () => { + editor.setReadOnly(isReadOnly); + editor.setData(value); + }); + } else { + const numRows = parseInt(this.component.rows, 10); + + if ( + _.isFinite(numRows) && + _.has( + editor, + 'ui.view.editable.editableElement', + ) + ) { + // Default height is 21px with 10px margin + a 14px top margin. + const editorHeight = numRows * 31 + 14; + editor.ui.view.editable.editableElement.style.height = `${editorHeight}px`; + } + editor.isReadOnly = isReadOnly; + editor.data.set(value); + } + + editorReady(editor); + return editor; + }); + break; + default: + super.attachElement(element, index); + break; + } + }); + + return element; } - if (!_.isString(value)) { - value = ''; + attach(element) { + const attached = super.attach(element); + // Make sure we restore the value after attaching since wysiwygs and readonly texts need an additional set. + this.restoreValue(); + return attached; } - this.setReadOnlyValue(value, index); - return value; - } + imageHandler(moduleInstance, range, files) { + const quillInstance = moduleInstance.quill; + + if (!files || !files.length) { + console.warn('No files selected'); + return; + } - setAsyncConvertedValue(value) { - if (this.isJsonValue && value) { - try { - value = JSON.stringify(value, null, 2); - } - catch (err) { - console.warn(err); - } + quillInstance.enable(false); + const { uploadStorage, uploadUrl, uploadOptions, uploadDir, fileKey } = + this.component; + let requestData; + this.fileService + .uploadFile( + uploadStorage, + files[0], + uniqueName(files[0].name), + uploadDir || '', //should pass empty string if undefined + null, + uploadUrl, + uploadOptions, + fileKey, + ) + .then((result) => { + requestData = result; + return this.fileService.downloadFile(result); + }) + .then((result) => { + quillInstance.enable(true); + const Delta = Quill.import('delta'); + quillInstance.updateContents( + new Delta() + .retain(range.index) + .delete(range.length) + .insert( + { + image: result.url, + }, + { + alt: JSON.stringify(requestData), + }, + ), + Quill.sources.USER, + ); + }) + .catch((error) => { + console.warn('Quill image upload failed'); + console.warn(error); + quillInstance.enable(true); + }); } - if (!_.isString(value)) { - value = ''; + get isPlain() { + return !this.component.wysiwyg && !this.component.editor; } - const htmlDoc = new DOMParser().parseFromString(value,'text/html'); - const images = htmlDoc.getElementsByTagName('img'); - if (images.length) { - return this.setImagesUrl(images) - .then( () => { - value = htmlDoc.getElementsByTagName('body')[0].innerHTML; - return value; - }); + get htmlView() { + return ( + this.options.readOnly && + (this.component.editor || this.component.wysiwyg) + ); } - else { - return Promise.resolve(value); + + setValueAt(index, value, flags = {}) { + super.setValueAt(index, value, flags); + + if (this.editorsReady[index]) { + const setEditorsValue = (flags) => (editor) => { + if (!flags.skipWysiwyg) { + this.autoModified = true; + switch (this.component.editor) { + case 'ace': + editor.setValue( + this.setConvertedValue(value, index), + ); + break; + case 'quill': + if (this.component.isUploadEnabled) { + this.setAsyncConvertedValue(value).then( + (result) => { + const content = + editor.clipboard.convert({ + html: result, + }); + editor.setContents(content); + }, + ); + } else { + const convertedValue = this.setConvertedValue( + value, + index, + ); + const content = editor.clipboard.convert({ + html: convertedValue, + }); + editor.setContents(content); + } + break; + case 'ckeditor': + editor.data.set( + this.setConvertedValue(value, index), + ); + break; + } + } + }; + + this.editorsReady[index].then(setEditorsValue(_.clone(flags))); + } } - } - setImagesUrl(images) { - return Promise.all(_.map(images, image => { - let requestData; - try { - requestData = JSON.parse(image.getAttribute('alt')); - } - catch (error) { - console.warn(error); - } + setValue(value, flags = {}) { + if (this.isPlain || this.options.readOnly || this.disabled) { + value = + this.component.multiple && Array.isArray(value) + ? value.map((val, index) => + this.setConvertedValue(val, index), + ) + : this.setConvertedValue(value); + return super.setValue(value, flags); + } + flags.skipWysiwyg = + value === '' && flags.resetValue + ? false + : _.isEqual(value, this.getValue()); + return super.setValue(value, flags); + } - return this.fileService.downloadFile(requestData) - .then((result) => { - image.setAttribute('src', result.url); + setContent(element, content, forceSanitize) { + super.setContent(element, content, forceSanitize, { + addAttr: ['allow', 'allowfullscreen', 'frameborder', 'scrolling'], + addTags: ['iframe'], }); - })); - } + } + + setReadOnlyValue(value, index) { + index = index || 0; + if (this.options.readOnly || this.disabled) { + if (this.refs.input && this.refs.input[index]) { + if (this.component.inputFormat === 'plain') { + this.refs.input[index].innerText = this.isPlain + ? value + : this.interpolate(value, {}, { noeval: true }); + } else { + this.setContent( + this.refs.input[index], + this.isPlain + ? value + : this.interpolate(value, {}, { noeval: true }), + this.shouldSanitizeValue, + ); + } + } + } + } - addAutoExpanding(textarea, index) { - let heightOffset = null; - let previousHeight = null; + get isJsonValue() { + return this.component.as && this.component.as === 'json'; + } + + setConvertedValue(value, index) { + if (this.isJsonValue && !_.isNil(value)) { + try { + value = JSON.stringify(value, null, 2); + } catch (err) { + console.warn(err); + } + } - const changeOverflow = (value) => { - const width = textarea.style.width; + if (!_.isString(value)) { + value = ''; + } - textarea.style.width = '0px'; - textarea.offsetWidth; - textarea.style.width = width; + this.setReadOnlyValue(value, index); + return value; + } - textarea.style.overflowY = value; - }; + setAsyncConvertedValue(value) { + if (this.isJsonValue && value) { + try { + value = JSON.stringify(value, null, 2); + } catch (err) { + console.warn(err); + } + } - const preventParentScroll = (element, changeSize) => { - const nodeScrolls = []; + if (!_.isString(value)) { + value = ''; + } - while (element && element.parentNode && element.parentNode instanceof Element) { - if (element.parentNode.scrollTop) { - nodeScrolls.push({ - node: element.parentNode, - scrollTop: element.parentNode.scrollTop, - }); + const htmlDoc = new DOMParser().parseFromString(value, 'text/html'); + const images = htmlDoc.getElementsByTagName('img'); + if (images.length) { + return this.setImagesUrl(images).then(() => { + value = htmlDoc.getElementsByTagName('body')[0].innerHTML; + return value; + }); + } else { + return Promise.resolve(value); } - element = element.parentNode; - } - - changeSize(); - - nodeScrolls.forEach((nodeScroll) => { - nodeScroll.node.scrollTop = nodeScroll.scrollTop; - }); - }; - - const resize = () => { - if (textarea.scrollHeight === 0) { - return; - } - - preventParentScroll(textarea, () => { - textarea.style.height = ''; - textarea.style.height = `${textarea.scrollHeight + heightOffset}px`; - }); - }; - - const update = _.debounce(() => { - resize(); - const styleHeight = Math.round(parseFloat(textarea.style.height)); - const computed = window.getComputedStyle(textarea, null); - let currentHeight = textarea.offsetHeight; - if (currentHeight < styleHeight && computed.overflowY === 'hidden') { - changeOverflow('scroll'); - } - else if (computed.overflowY !== 'hidden') { - changeOverflow('hidden'); - } - - resize(); - currentHeight = textarea.offsetHeight; - if (previousHeight !== currentHeight) { - previousHeight = currentHeight; + } + + setImagesUrl(images) { + return Promise.all( + _.map(images, (image) => { + let requestData; + try { + requestData = JSON.parse(image.getAttribute('alt')); + } catch (error) { + console.warn(error); + } + + return this.fileService + .downloadFile(requestData) + .then((result) => { + image.setAttribute('src', result.url); + }); + }), + ); + } + + addAutoExpanding(textarea, index) { + let heightOffset = null; + let previousHeight = null; + + const changeOverflow = (value) => { + const width = textarea.style.width; + + textarea.style.width = '0px'; + textarea.offsetWidth; + textarea.style.width = width; + + textarea.style.overflowY = value; + }; + + const preventParentScroll = (element, changeSize) => { + const nodeScrolls = []; + + while ( + element && + element.parentNode && + element.parentNode instanceof Element + ) { + if (element.parentNode.scrollTop) { + nodeScrolls.push({ + node: element.parentNode, + scrollTop: element.parentNode.scrollTop, + }); + } + element = element.parentNode; + } + + changeSize(); + + nodeScrolls.forEach((nodeScroll) => { + nodeScroll.node.scrollTop = nodeScroll.scrollTop; + }); + }; + + const resize = () => { + if (textarea.scrollHeight === 0) { + return; + } + + preventParentScroll(textarea, () => { + textarea.style.height = ''; + textarea.style.height = `${ + textarea.scrollHeight + heightOffset + }px`; + }); + }; + + const update = _.debounce(() => { + resize(); + const styleHeight = Math.round(parseFloat(textarea.style.height)); + const computed = window.getComputedStyle(textarea, null); + let currentHeight = textarea.offsetHeight; + if ( + currentHeight < styleHeight && + computed.overflowY === 'hidden' + ) { + changeOverflow('scroll'); + } else if (computed.overflowY !== 'hidden') { + changeOverflow('hidden'); + } + + resize(); + currentHeight = textarea.offsetHeight; + if (previousHeight !== currentHeight) { + previousHeight = currentHeight; + update(); + } + }, 200); + const computedStyle = window.getComputedStyle(textarea, null); + + textarea.style.resize = 'none'; + heightOffset = + parseFloat(computedStyle.borderTopWidth) + + parseFloat(computedStyle.borderBottomWidth) || 0; + + if (window) { + this.addEventListener(window, 'resize', update); + } + + this.addEventListener(textarea, 'input', update); + this.on('initialized', update); + this.updateSizes[index] = update; update(); - } - }, 200); - const computedStyle = window.getComputedStyle(textarea, null); - - textarea.style.resize = 'none'; - heightOffset = parseFloat(computedStyle.borderTopWidth) + parseFloat(computedStyle.borderBottomWidth) || 0; - - if (window) { - this.addEventListener(window, 'resize', update); - } - - this.addEventListener(textarea, 'input', update); - this.on('initialized', update); - this.updateSizes[index] = update; - update(); - } - - trimBlanks(value) { - if (!value || this.isPlain) { - return value; - } - - const trimBlanks = (value) => { - const nbsp = '

     

    '; - const br = '


    '; - const brNbsp = '


     

    '; - const regExp = new RegExp(`^${nbsp}|${nbsp}$|^${br}|${br}$|^${brNbsp}|${brNbsp}$`, 'g'); - return typeof value === 'string' ? value.replace(regExp, '') : value; - }; - - if (Array.isArray(value)) { - value.forEach((input, index) => { - value[index] = trimBlanks(input); - }); - } - else { - value = trimBlanks(value); - } - return value; - } - - onChange(flags, fromRoot) { - const changed = super.onChange(flags, fromRoot); - this.updateSizes.forEach(updateSize => updateSize()); - return changed; - } - - hasChanged(newValue, oldValue) { - return super.hasChanged(this.trimBlanks(newValue), this.trimBlanks(oldValue)); - } - - isEmpty(value = this.dataValue) { - return super.isEmpty(this.trimBlanks(value)); - } - - get defaultValue() { - let defaultValue = super.defaultValue; - if (this.component.editor === 'quill' && !defaultValue) { - defaultValue = '


    '; - } - return defaultValue; - } - - getConvertedValue(value) { - if (this.isJsonValue && value) { - try { - value = JSON.parse(value); - } - catch (err) { - // console.warn(err); - } - } - return value; - } - - detach() { - // Destroy all editors. - this.editors.forEach(editor => { - if (editor.destroy) { - editor.destroy(); - } - }); - this.editors = []; - this.editorsReady = []; - this.updateSizes.forEach(updateSize => this.removeEventListener(window, 'resize', updateSize)); - this.updateSizes = []; - super.detach(); - } - - getValue() { - if (this.isPlain) { - return this.getConvertedValue(super.getValue()); - } - - return this.dataValue; - } - - focus() { - super.focus(); - switch (this.component.editor) { - case 'ckeditor': { - // Wait for the editor to be ready. - this.editorsReady[0]?.then(() => { - if (this.editors[0].editing?.view?.focus) { - this.editors[0].editing.view.focus(); - } - this.element.scrollIntoView(); - }).catch((err) => { - console.warn('An editor did not initialize properly when trying to focus:', err); - }); - break; - } - case 'ace': { - this.editorsReady[0]?.then(() => { - this.editors[0].focus(); - this.element.scrollIntoView(); - }).catch((err) => { - console.warn('An editor did not initialize properly when trying to focus:', err); - }); - break; - } - case 'quill': { - this.editorsReady[0]?.then(() => { - this.editors[0].focus(); - }).catch((err) => { - console.warn('An editor did not initialize properly when trying to focus:', err); + } + + trimBlanks(value) { + if (!value || this.isPlain) { + return value; + } + + const trimBlanks = (value) => { + const nbsp = '

     

    '; + const br = '


    '; + const brNbsp = '


     

    '; + const regExp = new RegExp( + `^${nbsp}|${nbsp}$|^${br}|${br}$|^${brNbsp}|${brNbsp}$`, + 'g', + ); + return typeof value === 'string' + ? value.replace(regExp, '') + : value; + }; + + if (Array.isArray(value)) { + value.forEach((input, index) => { + value[index] = trimBlanks(input); + }); + } else { + value = trimBlanks(value); + } + return value; + } + + onChange(flags, fromRoot) { + const changed = super.onChange(flags, fromRoot); + this.updateSizes.forEach((updateSize) => updateSize()); + return changed; + } + + hasChanged(newValue, oldValue) { + return super.hasChanged( + this.trimBlanks(newValue), + this.trimBlanks(oldValue), + ); + } + + isEmpty(value = this.dataValue) { + return super.isEmpty(this.trimBlanks(value)); + } + + get defaultValue() { + let defaultValue = super.defaultValue; + if (this.component.editor === 'quill' && !defaultValue) { + defaultValue = '


    '; + } + return defaultValue; + } + + getConvertedValue(value) { + if (this.isJsonValue && value) { + try { + value = JSON.parse(value); + } catch (err) { + // console.warn(err); + } + } + return value; + } + + detach() { + // Destroy all editors. + this.editors.forEach((editor) => { + if (editor.destroy) { + editor.destroy(); + } }); - break; - } + this.editors = []; + this.editorsReady = []; + this.updateSizes.forEach((updateSize) => + this.removeEventListener(window, 'resize', updateSize), + ); + this.updateSizes = []; + super.detach(); + } + + getValue() { + if (this.isPlain) { + return this.getConvertedValue(super.getValue()); + } + + return this.dataValue; + } + + focus() { + super.focus(); + switch (this.component.editor) { + case 'ckeditor': { + // Wait for the editor to be ready. + this.editorsReady[0] + ?.then(() => { + if (this.editors[0].editing?.view?.focus) { + this.editors[0].editing.view.focus(); + } + this.element.scrollIntoView(); + }) + .catch((err) => { + console.warn( + 'An editor did not initialize properly when trying to focus:', + err, + ); + }); + break; + } + case 'ace': { + this.editorsReady[0] + ?.then(() => { + this.editors[0].focus(); + this.element.scrollIntoView(); + }) + .catch((err) => { + console.warn( + 'An editor did not initialize properly when trying to focus:', + err, + ); + }); + break; + } + case 'quill': { + this.editorsReady[0] + ?.then(() => { + this.editors[0].focus(); + }) + .catch((err) => { + console.warn( + 'An editor did not initialize properly when trying to focus:', + err, + ); + }); + break; + } + } } - } } diff --git a/src/components/textarea/TextArea.unit.js b/src/components/textarea/TextArea.unit.js index 31fe16c4af..55a10f736f 100644 --- a/src/components/textarea/TextArea.unit.js +++ b/src/components/textarea/TextArea.unit.js @@ -10,394 +10,517 @@ import { comp1, comp2, comp3, comp4 } from './fixtures'; import TextAreaComponent from './TextArea'; import 'ace-builds'; -describe('TextArea Component', function() { - it('Should build a TextArea component', function() { - return Harness.testCreate(TextAreaComponent, comp1).then((component) => { - Harness.testElements(component, 'textarea', 1); +describe('TextArea Component', function () { + it('Should build a TextArea component', function () { + return Harness.testCreate(TextAreaComponent, comp1).then( + (component) => { + Harness.testElements(component, 'textarea', 1); + }, + ); + }); + + it('setValue should be called only once', function () { + return Harness.testCreate(TextAreaComponent, comp2).then( + (component) => { + const valueToSet = [ + { + firstName: 'Bobby', + lastName: 'Lynch', + }, + { + firstName: 'Harold', + lastName: 'Freeman', + }, + ]; + const emit = sinon.spy(component, 'setValue'); + component.setValue(valueToSet); + expect(component.getValue()).to.deep.equal([ + { + firstName: 'Bobby', + lastName: 'Lynch', + }, + { + firstName: 'Harold', + lastName: 'Freeman', + }, + ]); + expect(emit.callCount).to.equal(1); + }, + ); }); - }); - - it('setValue should be called only once', function() { - return Harness.testCreate(TextAreaComponent, comp2).then((component) => { - const valueToSet = [ - { - firstName: 'Bobby', - lastName: 'Lynch' - }, - { - firstName: 'Harold', - lastName: 'Freeman' - }, - ]; - const emit = sinon.spy(component, 'setValue'); - component.setValue(valueToSet); - expect(component.getValue()).to.deep.equal([ - { - firstName: 'Bobby', - lastName: 'Lynch' - }, - { - firstName: 'Harold', - lastName: 'Freeman' - } - ]); - expect(emit.callCount).to.equal(1); + + it('Should provide min/max length validation', function (done) { + const form = _.cloneDeep(comp3); + form.components[0].validate = { minLength: 5, maxLength: 10 }; + + const validValues = [ + '', + 'te_st', + 'test value', + ' ', + 'What?', + 'test: ', + 't ', + ' t ', + ]; + + const invalidMin = ['t', 'tt', 'ttt', 'tttt', ' t ', ' t', '_4_']; + + const invalidMax = [ + 'test__value', + 'test value ', + ' test value', + 'test: value', + ]; + + const testValidity = (values, valid, message, lastValue) => { + _.each(values, (value) => { + const element = document.createElement('div'); + + Formio.createForm(element, form) + .then((form) => { + form.setPristine(false); + + const component = form.getComponent('textArea'); + const changed = component.setValue(value); + const error = message; + + if (value) { + assert.equal(changed, true, 'Should set value'); + } + + setTimeout(() => { + if (valid) { + assert.equal( + !!component.error, + false, + 'Should not contain error', + ); + } else { + assert.equal( + !!component.error, + true, + 'Should contain error', + ); + assert.equal( + component.error.message, + error, + 'Should contain error message', + ); + assert.equal( + component.element.classList.contains( + 'has-error', + ), + true, + 'Should contain error class', + ); + assert.equal( + component.refs.messageContainer.textContent.trim(), + error, + 'Should show error', + ); + } + + if (_.isEqual(value, lastValue)) { + done(); + } + }, 300); + }) + .catch(done); + }); + }; + + testValidity(validValues, true); + testValidity( + invalidMin, + false, + 'Text Area must have at least 5 characters.', + ); + testValidity( + invalidMax, + false, + 'Text Area must have no more than 10 characters.', + invalidMax[invalidMax.length - 1], + ); }); - }); - - it('Should provide min/max length validation', function(done) { - const form = _.cloneDeep(comp3); - form.components[0].validate = { minLength: 5, maxLength: 10 }; - - const validValues = [ - '', - 'te_st', - 'test value', - ' ', - 'What?', - 'test: ', - 't ', - ' t ' - ]; - - const invalidMin = [ - 't', - 'tt', - 'ttt', - 'tttt', - ' t ', - ' t', - '_4_' - ]; - - const invalidMax = [ - 'test__value', - 'test value ', - ' test value', - 'test: value', - ]; - - const testValidity = (values, valid, message, lastValue) => { - _.each(values, (value) => { + + it('Should provide min/max words validation', function (done) { + const form = _.cloneDeep(comp3); + form.components[0].validate = { minWords: 2, maxWords: 5 }; + + const validValues = [ + '', + 'test value', + 'some, test value', + 'some - test - value', + ' value value value value value ', + ' What ?', + '" test "', + ]; + + const invalidMin = [ + ' t ', + '? ', + 'e', + '_test ', + ' 9', + 't ', + 'What?', + '"4"', + ]; + + const invalidMax = [ + 'te st __ va lue ""', + '" te st va lue "', + '11 - 22 - 33 - 44', + 'te st : va lue :', + ]; + + const testValidity = (values, valid, message, lastValue) => { + _.each(values, (value) => { + const element = document.createElement('div'); + + Formio.createForm(element, form) + .then((form) => { + form.setPristine(false); + + const component = form.getComponent('textArea'); + const changed = component.setValue(value); + const error = message; + + if (value) { + assert.equal(changed, true, 'Should set value'); + } + + setTimeout(() => { + if (valid) { + assert.equal( + !!component.error, + false, + 'Should not contain error', + ); + } else { + assert.equal( + !!component.error, + true, + 'Should contain error', + ); + assert.equal( + component.error.message, + error, + 'Should contain error message', + ); + assert.equal( + component.element.classList.contains( + 'has-error', + ), + true, + 'Should contain error class', + ); + assert.equal( + component.refs.messageContainer.textContent.trim(), + error, + 'Should show error', + ); + } + + if (_.isEqual(value, lastValue)) { + done(); + } + }, 300); + }) + .catch(done); + }); + }; + + testValidity(validValues, true); + testValidity( + invalidMin, + false, + 'Text Area must have at least 2 words.', + ); + testValidity( + invalidMax, + false, + 'Text Area must have no more than 5 words.', + invalidMax[invalidMax.length - 1], + ); + }); + + it('Should provide pattern validation', function (done) { + const form = _.cloneDeep(comp3); + form.components[0].validate = { pattern: '\\D+' }; + + const validValues = [ + '', + ' ', + 'test value', + '& "" (test) _ ,.*', + ' some - test - value ', + ]; + + const invalidValues = ['test(2)', '123', '0 waste', '"9"', ' 9']; + + const testValidity = (values, valid, message, lastValue) => { + _.each(values, (value) => { + const element = document.createElement('div'); + + Formio.createForm(element, form) + .then((form) => { + form.setPristine(false); + + const component = form.getComponent('textArea'); + const changed = component.setValue(value); + const error = message; + + if (value) { + assert.equal(changed, true, 'Should set value'); + } + + setTimeout(() => { + if (valid) { + assert.equal( + !!component.error, + false, + 'Should not contain error', + ); + } else { + assert.equal( + !!component.error, + true, + 'Should contain error', + ); + assert.equal( + component.error.message.trim(), + error, + 'Should contain error message', + ); + assert.equal( + component.element.classList.contains( + 'has-error', + ), + true, + 'Should contain error class', + ); + assert.equal( + component.refs.messageContainer.textContent.trim(), + error, + 'Should show error', + ); + } + + if (_.isEqual(value, lastValue)) { + done(); + } + }, 300); + }) + .catch(done); + }); + }; + + testValidity(validValues, true); + testValidity( + invalidValues, + false, + 'Text Area does not match the pattern \\D+', + invalidValues[invalidValues.length - 1], + ); + }); + + it('Should set custom number of rows', function (done) { + const form = _.cloneDeep(comp3); + form.components[0].rows = 5; const element = document.createElement('div'); - Formio.createForm(element, form).then(form => { - form.setPristine(false); - - const component = form.getComponent('textArea'); - const changed = component.setValue(value); - const error = message; - - if (value) { - assert.equal(changed, true, 'Should set value'); - } - - setTimeout(() => { - if (valid) { - assert.equal(!!component.error, false, 'Should not contain error'); - } - else { - assert.equal(!!component.error, true, 'Should contain error'); - assert.equal(component.error.message, error, 'Should contain error message'); - assert.equal(component.element.classList.contains('has-error'), true, 'Should contain error class'); - assert.equal(component.refs.messageContainer.textContent.trim(), error, 'Should show error'); - } - - if (_.isEqual(value, lastValue)) { - done(); - } - }, 300); - }).catch(done); - }); - }; - - testValidity(validValues, true); - testValidity(invalidMin, false, 'Text Area must have at least 5 characters.'); - testValidity(invalidMax, false, 'Text Area must have no more than 10 characters.', invalidMax[invalidMax.length-1]); - }); - - it('Should provide min/max words validation', function(done) { - const form = _.cloneDeep(comp3); - form.components[0].validate = { minWords: 2, maxWords: 5 }; - - const validValues = [ - '', - 'test value', - 'some, test value', - 'some - test - value', - ' value value value value value ', - ' What ?', - '" test "', - ]; - - const invalidMin = [ - ' t ', - '? ', - 'e', - '_test ', - ' 9', - 't ', - 'What?', - '"4"' - ]; - - const invalidMax = [ - 'te st __ va lue ""', - '" te st va lue "', - '11 - 22 - 33 - 44', - 'te st : va lue :', - ]; - - const testValidity = (values, valid, message, lastValue) => { - _.each(values, (value) => { + Formio.createForm(element, form) + .then((form) => { + const component = form.getComponent('textArea'); + assert.equal( + component.refs.input[0].rows, + component.component.rows, + 'Should set custom number of rows', + ); + + done(); + }) + .catch(done); + }); + + it('Should render HTML', function (done) { + const form = _.cloneDeep(comp3); + form.components[0].inputFormat = 'html'; + const element = document.createElement('div'); + + Formio.createForm(element, form, { + readOnly: true, + }) + .then((form) => { + form.setSubmission({ + data: { + textArea: 'HTML!', + }, + }); + setTimeout(() => { + const textArea = form.getComponent('textArea'); + assert.equal( + textArea.refs.input[0].innerHTML, + 'HTML!', + ); + done(); + }, 300); + }) + .catch(done); + }); + + it('Should render plain text', function (done) { + const form = _.cloneDeep(comp3); + form.components[0].inputFormat = 'plain'; const element = document.createElement('div'); - Formio.createForm(element, form).then(form => { - form.setPristine(false); - - const component = form.getComponent('textArea'); - const changed = component.setValue(value); - const error = message; - - if (value) { - assert.equal(changed, true, 'Should set value'); - } - - setTimeout(() => { - if (valid) { - assert.equal(!!component.error, false, 'Should not contain error'); - } - else { - assert.equal(!!component.error, true, 'Should contain error'); - assert.equal(component.error.message, error, 'Should contain error message'); - assert.equal(component.element.classList.contains('has-error'), true, 'Should contain error class'); - assert.equal(component.refs.messageContainer.textContent.trim(), error, 'Should show error'); - } - - if (_.isEqual(value, lastValue)) { - done(); - } - }, 300); - }).catch(done); - }); - }; - - testValidity(validValues, true); - testValidity(invalidMin, false, 'Text Area must have at least 2 words.'); - testValidity(invalidMax, false, 'Text Area must have no more than 5 words.', invalidMax[invalidMax.length-1]); - }); - - it('Should provide pattern validation', function(done) { - const form = _.cloneDeep(comp3); - form.components[0].validate = { pattern: '\\D+' }; - - const validValues = [ - '', - ' ', - 'test value', - '& "" (test) _ ,.*', - ' some - test - value ', - ]; - - const invalidValues = [ - 'test(2)', - '123', - '0 waste', - '"9"', - ' 9', - ]; - - const testValidity = (values, valid, message, lastValue) => { - _.each(values, (value) => { + Formio.createForm(element, form, { + readOnly: true, + }) + .then((form) => { + form.setSubmission({ + data: { + textArea: 'Plain text!', + }, + }); + setTimeout(() => { + const textArea = form.getComponent('textArea'); + assert.equal( + textArea.refs.input[0].innerText, + 'Plain text!', + ); + done(); + }, 300); + }) + .catch(done); + }); + + it('Should correctly count characters if character counter is enabled', function (done) { + const form = _.cloneDeep(comp3); + form.components[0].showCharCount = true; const element = document.createElement('div'); - Formio.createForm(element, form).then(form => { - form.setPristine(false); - - const component = form.getComponent('textArea'); - const changed = component.setValue(value); - const error = message; - - if (value) { - assert.equal(changed, true, 'Should set value'); - } - - setTimeout(() => { - if (valid) { - assert.equal(!!component.error, false, 'Should not contain error'); - } - else { - assert.equal(!!component.error, true, 'Should contain error'); - assert.equal(component.error.message.trim(), error, 'Should contain error message'); - assert.equal(component.element.classList.contains('has-error'), true, 'Should contain error class'); - assert.equal(component.refs.messageContainer.textContent.trim(), error, 'Should show error'); - } - - if (_.isEqual(value, lastValue)) { - done(); - } - }, 300); - }).catch(done); - }); - }; - - testValidity(validValues, true); - testValidity(invalidValues, - false, - 'Text Area does not match the pattern \\D+', - invalidValues[invalidValues.length-1] - ); - }); - - it('Should set custom number of rows', function(done) { - const form = _.cloneDeep(comp3); - form.components[0].rows = 5; - const element = document.createElement('div'); - - Formio.createForm(element, form).then(form => { - const component = form.getComponent('textArea'); - assert.equal(component.refs.input[0].rows, component.component.rows, 'Should set custom number of rows'); - - done(); - }).catch(done); - }); - - it('Should render HTML', function(done) { - const form = _.cloneDeep(comp3); - form.components[0].inputFormat = 'html'; - const element = document.createElement('div'); - - Formio.createForm(element, form, { - readOnly: true - }).then(form => { - form.setSubmission({ - data: { - textArea: 'HTML!' - } - }); - setTimeout(() => { - const textArea = form.getComponent('textArea'); - assert.equal(textArea.refs.input[0].innerHTML, 'HTML!'); - done(); - }, 300); - }).catch(done); - }); - - it('Should render plain text', function(done) { - const form = _.cloneDeep(comp3); - form.components[0].inputFormat = 'plain'; - const element = document.createElement('div'); - - Formio.createForm(element, form, { - readOnly: true - }).then(form => { - form.setSubmission({ - data: { - textArea: 'Plain text!' - } - }); - setTimeout(() => { - const textArea = form.getComponent('textArea'); - assert.equal(textArea.refs.input[0].innerText, 'Plain text!'); - done(); - }, 300); - }).catch(done); - }); - - it('Should correctly count characters if character counter is enabled', function(done) { - const form = _.cloneDeep(comp3); - form.components[0].showCharCount = true; - const element = document.createElement('div'); - - Formio.createForm(element, form).then(form => { - const component = form.getComponent('textArea'); - const inputValue = (value) => { - const input = component.refs.input[0]; - const inputEvent = new Event('input'); - input.value = value; - input.dispatchEvent(inputEvent); - }; - - const checkValue = (value) => { - assert.equal(component.dataValue, value, 'Should set value'); - assert.equal(parseInt(component.refs.charcount[0].textContent), value.length, 'Should show correct chars number'); - assert.equal(component.refs.charcount[0].textContent, `${value.length} characters`, 'Should show correct message'); - }; - - let value = 'test Value (@#!-"]) _ 23.,5}/*&&'; - inputValue(value); - setTimeout(() => { - checkValue(value); - value = ''; - inputValue(value); - - setTimeout(() => { - checkValue(value); - value = ' '; - inputValue(value); - - setTimeout(() => { - checkValue(value); - - done(); - }, 200); - }, 200); - }, 200); - }).catch(done); - }); - - it('Should correctly count words if word counter is enabled', function(done) { - const form = _.cloneDeep(comp3); - form.components[0].showWordCount = true; - const element = document.createElement('div'); - - Formio.createForm(element, form).then(form => { - const component = form.getComponent('textArea'); - const inputValue = (value) => { - const input = component.refs.input[0]; - const inputEvent = new Event('input'); - input.value = value; - input.dispatchEvent(inputEvent); - }; - - const checkValue = (value, expected) => { - assert.equal(component.dataValue, value, 'Should set value'); - assert.equal(parseInt(component.refs.wordcount[0].textContent), expected, 'Should show correct words number'); - assert.equal(component.refs.wordcount[0].textContent, `${expected} words`, 'Should show correct message'); - }; - - let value = 'test , test_test 11 - "so me"'; - inputValue(value); - - setTimeout(() => { - checkValue(value, 7); - value = ' test '; - inputValue(value); - - setTimeout(() => { - checkValue(value, 1); - value = ' . . '; - inputValue(value); - - setTimeout(() => { - checkValue(value, 2); - - done(); - }, 200); - }, 200); - }, 200); - }).catch(done); - }); - - describe('Rich text editors', function() { - describe('CKEditor', function() { - it('Should allow to insert media fiels and show the in them read-only mode', function(done) { + Formio.createForm(element, form) + .then((form) => { + const component = form.getComponent('textArea'); + const inputValue = (value) => { + const input = component.refs.input[0]; + const inputEvent = new Event('input'); + input.value = value; + input.dispatchEvent(inputEvent); + }; + + const checkValue = (value) => { + assert.equal( + component.dataValue, + value, + 'Should set value', + ); + assert.equal( + parseInt(component.refs.charcount[0].textContent), + value.length, + 'Should show correct chars number', + ); + assert.equal( + component.refs.charcount[0].textContent, + `${value.length} characters`, + 'Should show correct message', + ); + }; + + let value = 'test Value (@#!-"]) _ 23.,5}/*&&'; + inputValue(value); + setTimeout(() => { + checkValue(value); + value = ''; + inputValue(value); + + setTimeout(() => { + checkValue(value); + value = ' '; + inputValue(value); + + setTimeout(() => { + checkValue(value); + + done(); + }, 200); + }, 200); + }, 200); + }) + .catch(done); + }); + + it('Should correctly count words if word counter is enabled', function (done) { + const form = _.cloneDeep(comp3); + form.components[0].showWordCount = true; const element = document.createElement('div'); - Formio.createForm(element, formWithCKEditor, { readOnly: true }).then(form => { - form.submission = { - data: { - textArea: ` + Formio.createForm(element, form) + .then((form) => { + const component = form.getComponent('textArea'); + const inputValue = (value) => { + const input = component.refs.input[0]; + const inputEvent = new Event('input'); + input.value = value; + input.dispatchEvent(inputEvent); + }; + + const checkValue = (value, expected) => { + assert.equal( + component.dataValue, + value, + 'Should set value', + ); + assert.equal( + parseInt(component.refs.wordcount[0].textContent), + expected, + 'Should show correct words number', + ); + assert.equal( + component.refs.wordcount[0].textContent, + `${expected} words`, + 'Should show correct message', + ); + }; + + let value = 'test , test_test 11 - "so me"'; + inputValue(value); + + setTimeout(() => { + checkValue(value, 7); + value = ' test '; + inputValue(value); + + setTimeout(() => { + checkValue(value, 1); + value = ' . . '; + inputValue(value); + + setTimeout(() => { + checkValue(value, 2); + + done(); + }, 200); + }, 200); + }, 200); + }) + .catch(done); + }); + + describe('Rich text editors', function () { + describe('CKEditor', function () { + it('Should allow to insert media fiels and show the in them read-only mode', function (done) { + const element = document.createElement('div'); + + Formio.createForm(element, formWithCKEditor, { readOnly: true }) + .then((form) => { + form.submission = { + data: { + textArea: `
    @@ -414,119 +537,143 @@ describe('TextArea Component', function() {
    `, - }, - state: 'submitted', - }; - - setTimeout(() => { - const mediaA = form.element.querySelector('iframe[src="https://www.youtube.com/embed/GsLRrmnJXF8"]'); - const mediaB = form.element.querySelector('iframe[src="https://www.youtube.com/embed/FmA6U5rXl38"]'); - assert(mediaA, 'Should not remove embedded media'); - assert(mediaB, 'Should not remove embedded media'); - - done(); - }, 300); - }).catch(done); - }); - }); - - it('Should clear value in the editor on Reset', function(done) { - const element = document.createElement('div'); - - Formio.createForm(element, formWithRichTextAreas).then(form => { - form.setValue({ - data: { - textArea: 'Test', - textAreaAce: 'Test', - }, + }, + state: 'submitted', + }; + + setTimeout(() => { + const mediaA = form.element.querySelector( + 'iframe[src="https://www.youtube.com/embed/GsLRrmnJXF8"]', + ); + const mediaB = form.element.querySelector( + 'iframe[src="https://www.youtube.com/embed/FmA6U5rXl38"]', + ); + assert(mediaA, 'Should not remove embedded media'); + assert(mediaB, 'Should not remove embedded media'); + + done(); + }, 300); + }) + .catch(done); + }); }); - setTimeout(() => { - const plainTextArea = form.getComponent(['textArea']); - const aceTextArea = form.getComponent(['textAreaAce']); - - const textAreaElement = plainTextArea.element.querySelector('textarea'); - console.log(aceTextArea.editors); - const aceEditor = aceTextArea.editors[0]; - - // Make sure value is set to the components - assert.equal(plainTextArea.dataValue, 'Test'); - assert.equal(aceTextArea.dataValue, 'Test'); - - // Make sure value is set to the editors/elements - assert.equal(textAreaElement.value, 'Test'); - assert.equal(aceEditor.getValue(), 'Test'); - - form.resetValue(); - - setTimeout(() => { - // Make sure value is cleared on the components - assert.equal(plainTextArea.dataValue, ''); - assert.equal(aceTextArea.dataValue, ''); - - // Make sure value is cleared in the editors/elements - assert.equal(textAreaElement.value, ''); - assert.equal(aceEditor.getValue(), ''); - done(); - }, 300); - }, 500); - }).catch(done); - }); - - it('Should set empty value properly when save as JSON', function(done) { - const element = document.createElement('div'); + it('Should clear value in the editor on Reset', function (done) { + const element = document.createElement('div'); + + Formio.createForm(element, formWithRichTextAreas) + .then((form) => { + form.setValue({ + data: { + textArea: 'Test', + textAreaAce: 'Test', + }, + }); + + setTimeout(() => { + const plainTextArea = form.getComponent(['textArea']); + const aceTextArea = form.getComponent(['textAreaAce']); + + const textAreaElement = + plainTextArea.element.querySelector('textarea'); + console.log(aceTextArea.editors); + const aceEditor = aceTextArea.editors[0]; + + // Make sure value is set to the components + assert.equal(plainTextArea.dataValue, 'Test'); + assert.equal(aceTextArea.dataValue, 'Test'); + + // Make sure value is set to the editors/elements + assert.equal(textAreaElement.value, 'Test'); + assert.equal(aceEditor.getValue(), 'Test'); + + form.resetValue(); + + setTimeout(() => { + // Make sure value is cleared on the components + assert.equal(plainTextArea.dataValue, ''); + assert.equal(aceTextArea.dataValue, ''); + + // Make sure value is cleared in the editors/elements + assert.equal(textAreaElement.value, ''); + assert.equal(aceEditor.getValue(), ''); + done(); + }, 300); + }, 500); + }) + .catch(done); + }); - Formio.createForm(element, comp4).then(form => { - const aceTextArea = form.getComponent(['jsonTextarea']); - assert.equal(aceTextArea.data.jsonTextarea, '', 'The value should be empty'); - done(); - }).catch(done); - }); + it('Should set empty value properly when save as JSON', function (done) { + const element = document.createElement('div'); + + Formio.createForm(element, comp4) + .then((form) => { + const aceTextArea = form.getComponent(['jsonTextarea']); + assert.equal( + aceTextArea.data.jsonTextarea, + '', + 'The value should be empty', + ); + done(); + }) + .catch(done); + }); - it('Should not autofocus until the editor is ready', function(done) { - const element = document.createElement('div'); - const testComponents = [ - { - type: 'textarea', - autofocus: true, - editor: 'ckeditor', - key: 'textArea', - label: 'Text Area', - input: true, - } - ]; - const testForm = { ...formWithCKEditor, components: testComponents }; - - Formio.createForm(element, testForm).then(form => { - const textArea = form.getComponent('textArea'); - // since prior to this fix the focus function will throw, we'll make sure it doesn't - expect(textArea.focus.bind(textArea)).to.not.throw(); - - done(); - }).catch(done); - }); + it('Should not autofocus until the editor is ready', function (done) { + const element = document.createElement('div'); + const testComponents = [ + { + type: 'textarea', + autofocus: true, + editor: 'ckeditor', + key: 'textArea', + label: 'Text Area', + input: true, + }, + ]; + const testForm = { + ...formWithCKEditor, + components: testComponents, + }; + + Formio.createForm(element, testForm) + .then((form) => { + const textArea = form.getComponent('textArea'); + // since prior to this fix the focus function will throw, we'll make sure it doesn't + expect(textArea.focus.bind(textArea)).to.not.throw(); + + done(); + }) + .catch(done); + }); - it('Should not autofocus if the form is readOnly', function(done) { - const element = document.createElement('div'); - const testComponents = [ - { - type: 'textarea', - autofocus: true, - editor: 'ckeditor', - key: 'textArea', - label: 'Text Area', - input: true, - } - ]; - const testForm = { ...formWithCKEditor, components: testComponents }; - - Formio.createForm(element, testForm, { readOnly: true }).then(form => { - const textArea = form.getComponent('textArea'); - // since prior to this fix the focus function will throw if readOnly, we'll make sure it doesn't - expect(textArea.focus.bind(textArea)).to.not.throw(); - - done(); - }).catch(done); + it('Should not autofocus if the form is readOnly', function (done) { + const element = document.createElement('div'); + const testComponents = [ + { + type: 'textarea', + autofocus: true, + editor: 'ckeditor', + key: 'textArea', + label: 'Text Area', + input: true, + }, + ]; + const testForm = { + ...formWithCKEditor, + components: testComponents, + }; + + Formio.createForm(element, testForm, { readOnly: true }) + .then((form) => { + const textArea = form.getComponent('textArea'); + // since prior to this fix the focus function will throw if readOnly, we'll make sure it doesn't + expect(textArea.focus.bind(textArea)).to.not.throw(); + + done(); + }) + .catch(done); + }); }); - }); }); diff --git a/src/components/textarea/editForm/TextArea.edit.display.js b/src/components/textarea/editForm/TextArea.edit.display.js index 99e3ee96f6..193bff0b21 100644 --- a/src/components/textarea/editForm/TextArea.edit.display.js +++ b/src/components/textarea/editForm/TextArea.edit.display.js @@ -2,258 +2,231 @@ import _ from 'lodash'; import { Formio } from '../../../Formio'; export default [ - { - key: 'inputMask', - ignore: true - }, - { - key: 'allowMultipleMasks', - ignore: true - }, - { - key: 'mask', - ignore: true - }, - { - type: 'number', - input: true, - key: 'rows', - label: 'Rows', - weight: 210, - tooltip: 'This allows control over how many rows are visible in the text area.', - placeholder: 'Enter the amount of rows' - }, - { - weight: 1350, - type: 'checkbox', - input: true, - key: 'spellcheck', - defaultValue: true, - label: 'Allow Spellcheck' - }, - { - type: 'select', - input: true, - key: 'editor', - label: 'Editor', - tooltip: 'Select the type of WYSIWYG editor to use for this text area.', - dataSrc: 'values', - data: { - values: [ - { label: 'None', value: '' }, - { label: 'ACE', value: 'ace' }, - { label: 'CKEditor', value: 'ckeditor' }, - { label: 'Quill', value: 'quill' }, - ] + { + key: 'inputMask', + ignore: true, }, - weight: 415 - }, - { - type: 'checkbox', - input: true, - key: 'autoExpand', - label: 'Auto Expand', - tooltip: 'This will make the TextArea auto expand it\'s height as the user is typing into the area.', - weight: 415, - conditional: { - json: { - '==': [ - { var: 'data.editor' }, - '' - ] - } - } - }, - { - type: 'checkbox', - input: true, - key: 'isUploadEnabled', - label: 'Enable Image Upload', - weight: 415.1, - conditional: { - json: { - or: [ - { - '===': [ - { var: 'data.editor' }, - 'quill' - ], - }, - { - '===': [ - { var: 'data.editor' }, - 'ckeditor' + { + key: 'allowMultipleMasks', + ignore: true, + }, + { + key: 'mask', + ignore: true, + }, + { + type: 'number', + input: true, + key: 'rows', + label: 'Rows', + weight: 210, + tooltip: + 'This allows control over how many rows are visible in the text area.', + placeholder: 'Enter the amount of rows', + }, + { + weight: 1350, + type: 'checkbox', + input: true, + key: 'spellcheck', + defaultValue: true, + label: 'Allow Spellcheck', + }, + { + type: 'select', + input: true, + key: 'editor', + label: 'Editor', + tooltip: 'Select the type of WYSIWYG editor to use for this text area.', + dataSrc: 'values', + data: { + values: [ + { label: 'None', value: '' }, + { label: 'ACE', value: 'ace' }, + { label: 'CKEditor', value: 'ckeditor' }, + { label: 'Quill', value: 'quill' }, ], - } - ] - } - } - }, - { - type: 'select', - input: true, - key: 'uploadStorage', - label: 'Image Upload Storage', - placeholder: 'Select your file storage provider', - weight: 415.2, - tooltip: 'Which storage to save the files in.', - valueProperty: 'value', - dataSrc: 'custom', - data: { - custom() { - return _.map(Formio.Providers.getProviders('storage'), (storage, key) => ({ - label: storage.title, - value: key - })); - } + }, + weight: 415, + }, + { + type: 'checkbox', + input: true, + key: 'autoExpand', + label: 'Auto Expand', + tooltip: + "This will make the TextArea auto expand it's height as the user is typing into the area.", + weight: 415, + conditional: { + json: { + '==': [{ var: 'data.editor' }, ''], + }, + }, }, - conditional: { - json: { - '===': [ - { var: 'data.isUploadEnabled' }, - true - ] - } - } - }, - { - type: 'textfield', - input: true, - key: 'uploadUrl', - label: 'Image Upload Url', - weight: 415.3, - placeholder: 'Enter the url to post the files to.', - tooltip: 'See https://github.com/danialfarid/ng-file-upload#server-side for how to set up the server.', - conditional: { - json: { '===': [{ var: 'data.uploadStorage' }, 'url'] } - } - }, - { - type: 'textarea', - key: 'uploadOptions', - label: 'Image Upload Custom request options', - tooltip: 'Pass your custom xhr options(optional)', - rows: 5, - editor: 'ace', - input: true, - weight: 415.4, - placeholder: `{ + { + type: 'checkbox', + input: true, + key: 'isUploadEnabled', + label: 'Enable Image Upload', + weight: 415.1, + conditional: { + json: { + or: [ + { + '===': [{ var: 'data.editor' }, 'quill'], + }, + { + '===': [{ var: 'data.editor' }, 'ckeditor'], + }, + ], + }, + }, + }, + { + type: 'select', + input: true, + key: 'uploadStorage', + label: 'Image Upload Storage', + placeholder: 'Select your file storage provider', + weight: 415.2, + tooltip: 'Which storage to save the files in.', + valueProperty: 'value', + dataSrc: 'custom', + data: { + custom() { + return _.map( + Formio.Providers.getProviders('storage'), + (storage, key) => ({ + label: storage.title, + value: key, + }), + ); + }, + }, + conditional: { + json: { + '===': [{ var: 'data.isUploadEnabled' }, true], + }, + }, + }, + { + type: 'textfield', + input: true, + key: 'uploadUrl', + label: 'Image Upload Url', + weight: 415.3, + placeholder: 'Enter the url to post the files to.', + tooltip: + "See https://github.com/danialfarid/ng-file-upload#server-side for how to set up the server.", + conditional: { + json: { '===': [{ var: 'data.uploadStorage' }, 'url'] }, + }, + }, + { + type: 'textarea', + key: 'uploadOptions', + label: 'Image Upload Custom request options', + tooltip: 'Pass your custom xhr options(optional)', + rows: 5, + editor: 'ace', + input: true, + weight: 415.4, + placeholder: `{ "withCredentials": true }`, - conditional: { - json: { - '===': [{ - var: 'data.uploadStorage' - }, 'url'] - } - } - }, - { - type: 'textfield', - input: true, - key: 'uploadDir', - label: 'Image Upload Directory', - placeholder: '(optional) Enter a directory for the files', - tooltip: 'This will place all the files uploaded in this field in the directory', - weight: 415.5, - conditional: { - json: { - '===': [ - { var: 'data.isUploadEnabled' }, - true - ] - } - } - }, - { - type: 'textfield', - key: 'fileKey', - input: true, - label: 'File form-data Key', - tooltip: 'Key name that you would like to modify for the file while calling API request.', - rows: 5, - weight: 415.6, - placeholder: 'Enter the key name of a file for form data.', - conditional: { - json: { - and: [ - { '===': [ - { var: 'data.editor' }, - 'quill' - ] }, - { '===': [ - { var: 'data.isUploadEnabled' }, - true - ] }, - { '===': [ - { var: 'data.uploadStorage' }, - 'url' - ] }, - ] - } - } - }, - { - type: 'select', - input: true, - key: 'as', - label: 'Save As', - dataSrc: 'values', - tooltip: 'This setting determines how the value should be entered and stored in the database.', - clearOnHide: true, - data: { - values: [ - { label: 'String', value: 'string' }, - { label: 'JSON', value: 'json' }, - { label: 'HTML', value: 'html' } - ] + conditional: { + json: { + '===': [ + { + var: 'data.uploadStorage', + }, + 'url', + ], + }, + }, }, - conditional: { - json: { - or: [ - { '===': [ - { var: 'data.editor' }, - 'quill' - ] }, - { '===': [ - { var: 'data.editor' }, - 'ace' - ] } - ] - } + { + type: 'textfield', + input: true, + key: 'uploadDir', + label: 'Image Upload Directory', + placeholder: '(optional) Enter a directory for the files', + tooltip: + 'This will place all the files uploaded in this field in the directory', + weight: 415.5, + conditional: { + json: { + '===': [{ var: 'data.isUploadEnabled' }, true], + }, + }, }, - weight: 416 - }, - { - type: 'textarea', - input: true, - editor: 'ace', - rows: 10, - as: 'json', - label: 'Editor Settings', - tooltip: 'Enter the WYSIWYG editor JSON configuration.', - key: 'wysiwyg', - customDefaultValue(value, component, row, data, instance) { - return instance ? instance.wysiwygDefault : ''; + { + type: 'textfield', + key: 'fileKey', + input: true, + label: 'File form-data Key', + tooltip: + 'Key name that you would like to modify for the file while calling API request.', + rows: 5, + weight: 415.6, + placeholder: 'Enter the key name of a file for form data.', + conditional: { + json: { + and: [ + { '===': [{ var: 'data.editor' }, 'quill'] }, + { '===': [{ var: 'data.isUploadEnabled' }, true] }, + { '===': [{ var: 'data.uploadStorage' }, 'url'] }, + ], + }, + }, + }, + { + type: 'select', + input: true, + key: 'as', + label: 'Save As', + dataSrc: 'values', + tooltip: + 'This setting determines how the value should be entered and stored in the database.', + clearOnHide: true, + data: { + values: [ + { label: 'String', value: 'string' }, + { label: 'JSON', value: 'json' }, + { label: 'HTML', value: 'html' }, + ], + }, + conditional: { + json: { + or: [ + { '===': [{ var: 'data.editor' }, 'quill'] }, + { '===': [{ var: 'data.editor' }, 'ace'] }, + ], + }, + }, + weight: 416, }, - conditional: { - json: { - or: [ - { '===': [ - { var: 'data.editor' }, - 'ace' - ] }, - { '===': [ - { var: 'data.editor' }, - 'ckeditor' - ] }, - { '===': [ - { var: 'data.editor' }, - 'quill' - ] }, - ] - } + { + type: 'textarea', + input: true, + editor: 'ace', + rows: 10, + as: 'json', + label: 'Editor Settings', + tooltip: 'Enter the WYSIWYG editor JSON configuration.', + key: 'wysiwyg', + customDefaultValue(value, component, row, data, instance) { + return instance ? instance.wysiwygDefault : ''; + }, + conditional: { + json: { + or: [ + { '===': [{ var: 'data.editor' }, 'ace'] }, + { '===': [{ var: 'data.editor' }, 'ckeditor'] }, + { '===': [{ var: 'data.editor' }, 'quill'] }, + ], + }, + }, + weight: 417, }, - weight: 417 - } ]; diff --git a/src/components/textarea/editForm/TextArea.edit.validation.js b/src/components/textarea/editForm/TextArea.edit.validation.js index 9261c911eb..f52d58aee9 100644 --- a/src/components/textarea/editForm/TextArea.edit.validation.js +++ b/src/components/textarea/editForm/TextArea.edit.validation.js @@ -1,20 +1,20 @@ export default [ - { - weight: 125, - key: 'validate.minWords', - label: 'Minimum Word Length', - placeholder: 'Minimum Word Length', - type: 'number', - tooltip: 'The minimum amount of words that can be added to this field.', - input: true - }, - { - weight: 126, - key: 'validate.maxWords', - label: 'Maximum Word Length', - placeholder: 'Maximum Word Length', - type: 'number', - tooltip: 'The maximum amount of words that can be added to this field.', - input: true - } + { + weight: 125, + key: 'validate.minWords', + label: 'Minimum Word Length', + placeholder: 'Minimum Word Length', + type: 'number', + tooltip: 'The minimum amount of words that can be added to this field.', + input: true, + }, + { + weight: 126, + key: 'validate.maxWords', + label: 'Maximum Word Length', + placeholder: 'Maximum Word Length', + type: 'number', + tooltip: 'The maximum amount of words that can be added to this field.', + input: true, + }, ]; diff --git a/src/components/textarea/fixtures/comp1.js b/src/components/textarea/fixtures/comp1.js index 9c903885e2..3dfbeaba01 100644 --- a/src/components/textarea/fixtures/comp1.js +++ b/src/components/textarea/fixtures/comp1.js @@ -1,31 +1,29 @@ export default { - 'conditional': { - 'eq': '', - 'when': null, - 'show': '' - }, - 'tags': [ - - ], - 'type': 'textarea', - 'validate': { - 'custom': '', - 'pattern': '', - 'maxLength': '', - 'minLength': '', - 'required': false - }, - 'wysiwyg': false, - 'persistent': true, - 'protected': false, - 'defaultValue': '', - 'multiple': false, - 'rows': 3, - 'suffix': '', - 'prefix': '', - 'placeholder': 'Enter your message here', - 'key': 'message', - 'label': 'Message', - 'tableView': true, - 'input': true + conditional: { + eq: '', + when: null, + show: '', + }, + tags: [], + type: 'textarea', + validate: { + custom: '', + pattern: '', + maxLength: '', + minLength: '', + required: false, + }, + wysiwyg: false, + persistent: true, + protected: false, + defaultValue: '', + multiple: false, + rows: 3, + suffix: '', + prefix: '', + placeholder: 'Enter your message here', + key: 'message', + label: 'Message', + tableView: true, + input: true, }; diff --git a/src/components/textarea/fixtures/comp2.js b/src/components/textarea/fixtures/comp2.js index 7a66bef905..5c2c6557c9 100644 --- a/src/components/textarea/fixtures/comp2.js +++ b/src/components/textarea/fixtures/comp2.js @@ -1,27 +1,27 @@ export default { - 'tags': [], - 'type': 'textarea', - 'validate': { - 'custom': '', - 'pattern': '', - 'maxLength': '', - 'minLength': '', - 'required': false - }, - 'wysiwyg': false, - 'persistent': true, - 'protected': false, - 'defaultValue': '', - 'multiple': false, - 'rows': 3, - 'suffix': '', - 'prefix': '', - 'placeholder': '', - 'key': 'textArea', - 'label': 'Text Area', - 'editor': 'ace', - 'as': 'json', - 'tableView': true, - 'spellcheck': true, - 'input': true + tags: [], + type: 'textarea', + validate: { + custom: '', + pattern: '', + maxLength: '', + minLength: '', + required: false, + }, + wysiwyg: false, + persistent: true, + protected: false, + defaultValue: '', + multiple: false, + rows: 3, + suffix: '', + prefix: '', + placeholder: '', + key: 'textArea', + label: 'Text Area', + editor: 'ace', + as: 'json', + tableView: true, + spellcheck: true, + input: true, }; diff --git a/src/components/textarea/fixtures/comp3.js b/src/components/textarea/fixtures/comp3.js index 51da8fa954..925012121c 100644 --- a/src/components/textarea/fixtures/comp3.js +++ b/src/components/textarea/fixtures/comp3.js @@ -1,27 +1,27 @@ export default { - type: 'form', - components: [ - { - label: 'Text Area', - autoExpand: false, - tableView: true, - key: 'textArea', - type: 'textarea', - input: true - }, - { - label: 'Submit', - showValidations: false, - tableView: false, - key: 'submit', - type: 'button', - input: true - } - ], - revisions: '', - _vid: 0, - title: 'text area tests', - display: 'form', - name: 'textAriaTests', - path: 'textAriaTests', + type: 'form', + components: [ + { + label: 'Text Area', + autoExpand: false, + tableView: true, + key: 'textArea', + type: 'textarea', + input: true, + }, + { + label: 'Submit', + showValidations: false, + tableView: false, + key: 'submit', + type: 'button', + input: true, + }, + ], + revisions: '', + _vid: 0, + title: 'text area tests', + display: 'form', + name: 'textAriaTests', + path: 'textAriaTests', }; diff --git a/src/components/textarea/fixtures/comp4.js b/src/components/textarea/fixtures/comp4.js index 35df665c91..70650c27e8 100644 --- a/src/components/textarea/fixtures/comp4.js +++ b/src/components/textarea/fixtures/comp4.js @@ -1,25 +1,25 @@ export default { - type: 'form', - components: [ - { - type: 'textarea', - key: 'jsonTextarea', - rows: 5, - editor: 'ace', - hideLabel: true, - as: 'json', - input: true - }, - { - label: 'Submit', - showValidations: false, - tableView: false, - key: 'submit', - type: 'button', - input: true - } - ], - title: 'text area tests', - display: 'form', - name: 'textAriaTests', + type: 'form', + components: [ + { + type: 'textarea', + key: 'jsonTextarea', + rows: 5, + editor: 'ace', + hideLabel: true, + as: 'json', + input: true, + }, + { + label: 'Submit', + showValidations: false, + tableView: false, + key: 'submit', + type: 'button', + input: true, + }, + ], + title: 'text area tests', + display: 'form', + name: 'textAriaTests', }; diff --git a/src/components/textarea/fixtures/values.js b/src/components/textarea/fixtures/values.js index bd7aa8d249..ccad3b3858 100644 --- a/src/components/textarea/fixtures/values.js +++ b/src/components/textarea/fixtures/values.js @@ -1,5 +1 @@ -export default [ - '

    asdf

    ', - '
    foobar
    ', -]; - +export default ['

    asdf

    ', '
    foobar
    ']; diff --git a/src/components/textfield/TextField.builder.spec.js b/src/components/textfield/TextField.builder.spec.js index 1436a840cb..b3b8e92cdd 100644 --- a/src/components/textfield/TextField.builder.spec.js +++ b/src/components/textfield/TextField.builder.spec.js @@ -2,147 +2,353 @@ import Harness from '../../../test/harness'; import EventEmitter from '../../EventEmitter'; import assert from 'power-assert'; -describe('TextField Builder', function() { - let builder = null; - - before(function(done) { - // Incrise Events limit for this tests set - Harness.builderBefore(done, { - editForm: { - events: new EventEmitter() - } - }); - }); - - after(function() { - Harness.builderAfter(); - }); - - it('Should create a new textfield component', function() { - builder = Harness.buildComponent('textfield'); - return builder.editForm.formReady.then(() => { - // Make sure default preview is correct. - const preview = builder.componentPreview.innerHTML; - assert(preview.indexOf('formio-component formio-component-textfield formio-component-textField') !== -1, 'Must have correct classes'); - assert(preview.indexOf('') !== -1, 'Must have a label'); - assert(preview.indexOf(' { - assert(preview.match(/label.*input/), 'Label must be on top.'); - assert(preview.indexOf('') !== -1, 'Must have a label'); - done(); - }); - }); - - it('Should allow you to hide/show the label', function(done) { - Harness.setComponentProperty('hideLabel', false, true, (preview) => { - assert(preview.indexOf('